Verilog FAQ Version 09/05: September 2005 [ FAQ Main | Part-1 | Part-2 | Part-3 | What' New | Links ] |
Examples from "The Verilog Hardware Description Language"by D.E. Thomas and P.R. Moorby//Example 1.2. NAND Latch To Be Simulated.
module ffNand;
wire q, qBar;
reg preset, clear;
nand #1
g1 (q, qBar, preset),
g2 (qBar, q, clear);
initial
begin
// two slashes introduce a single line comment
$monitor ($time,,
"Preset = %b clear = %b q = %b qBar = %b",
preset, clear, q, qBar);
//waveform for simulating the nand flip flop
#10 preset = 0; clear = 1;
#10 preset = 1;
#10 clear = 0;
#10 clear = 1;
#10 $finish;
end
endmodule
//Example 1.4. A 16-Bit Counter.
module m16 (value, clock, fifteen, altFifteen);
output [3:0] value;
output fifteen,
altFifteen;
input clock;
dEdgeFF a (value[0], clock, ~value[0]),
b (value[1], clock, value[1] ^ value[0]),
c (value[2], clock, value[2] ^ &value[1:0]),
d (value[3], clock, value[3] ^ &value[2:0]);
assign fifteen = value[0] & value[1] & value[2] & value[3];
assign altFifteen = &value;
endmodule
//Example 1.5. A D-Type Edge-Triggered Flip Flop.
module dEdgeFF (q, clock, data);
output q;
reg q;
input clock, data;
initial
q = 0;
always
@(negedge clock) #10 q = data;
endmodule
//Example 1.6. A Clock For the Counter.
module m555 (clock);
output clock;
reg clock;
initial
#5 clock = 1;
always
#50 clock = ~ clock;
endmodule
//Example 1.7. The Top-Level Module of the Counter.
module board;
wire [3:0] count;
wire clock,
f,
af;
m16 counter (count, clock, f, af);
m555 clockGen (clock);
always @(posedge clock)
$display ($time,,,"count=%d, f=%d, af=%d", count, f, af);
endmodule
//Example 1.8. The Counter Module Described With Behavioral Statements.
module m16Behav (value, clock, fifteen, altFifteen);
output [3:0] value;
reg [3:0] value;
output fifteen,
altFifteen;
reg fifteen,
altFifteen;
input clock;
initial
value = 0;
always
begin
@(negedge clock) #10 value = value + 1;
if (value == 15)
begin
altFifteen = 1;
fifteen = 1;
end
else
begin
altFifteen = 0;
fifteen = 0;
end
end
endmodule
//Example 1.9. Top Level of the Fibonacci Number Generator.
module top();
wire flag, numProduced, numConsumed;
wire [15:0] number, numberOut;
nandLatch ready (flag, , numConsumed, numProduced);
numberGen ng (number, numProduced, flag);
fibNumberGen fng (number, flag, numConsumed, numberOut);
endmodule
//Example 1.10. A NAND Latch.
module nandLatch (q, qBar, set, reset);
output q, qBar;
input set, reset;
nand #2
(q, qBar, set),
(qBar, q, reset);
endmodule
//Example 1.11. The Seed-Number Generator.
module numberGen (number, numProduced, flag);
output [15:0] number;
output numProduced;
input flag;
reg numProduced;
reg [15:0] number;
initial
begin
number = 3;
numProduced = 1;
end
always
begin
wait (flag == 1)
#100 number = number + 1;
numProduced = 0;
#10 numProduced = 1;
end
endmodule
//Example 1.12. The Fibonacci Number Generator Module.
module fibNumberGen (startingValue, flag, numConsumed, fibNum);
input [15:0] startingValue;
input flag;
output numConsumed;
output [15:0] fibNum;
reg numConsumed;
reg [15:0] myValue;
reg [15:0] fibNum;
initial
begin
numConsumed = 0;
#10 numConsumed = 1;
$monitor ($time,,
"fibNum=%d, startingValue=%d",
fibNum, startingValue);
end
always
begin
wait (flag == 0)
myValue = startingValue;
numConsumed = 0;
#10 numConsumed = 1; //signal ready for input
for (fibNum = 0; myValue != 0; myValue = myValue - 1)
fibNum = fibNum + myValue;
$display ("%d, fibNum=%d", $time, fibNum);
end
endmodule
//Example 2.1. A Divide Module.
module divide (ddInput, dvInput, quotient, go, done);
parameter
DvLen = 15,
DdLen = 31,
QLen = 15,
HiDdMin = 16;
input [DdLen:0] ddInput;
input [DvLen:0] dvInput;
output [QLen:0] quotient;
input go;
output done;
reg [DdLen:0] dividend;
reg done;
reg [QLen:0] quotient;
reg negDivisor,
negDividend;
reg [DvLen:0] divisor;
always
begin
done = 0;
wait (go);
divisor = dvInput;
dividend = ddInput;
quotient = 0;
if (divisor)
begin
negDivisor = divisor[DvLen];
if (negDivisor)
divisor = - divisor;
negDividend = dividend[DdLen];
if (negDividend)
dividend = - dividend;
repeat (DvLen + 1)
begin
quotient = quotient << 1;
dividend = dividend << 1;
dividend[DdLen:HiDdMin] =
dividend[DdLen:HiDdMin] - divisor;
if (! dividend [DdLen])
quotient = quotient + 1;
else
dividend[DdLen:HiDdMin] =
dividend[DdLen:HiDdMin] + divisor;
end
if (negDivisor != negDividend)
quotient = - quotient;
end
done = 1;
wait (~go);
end
endmodule
//Example 2.5. The Mark-1 Processor With If-Else-If.
module mark1;
reg [31:0] m [0:8191]; // 8192 x 32 bit memory
reg [12:0] pc; // 13 bit program counter
reg [31:0] acc; // 32 bit accumulator
reg [15:0] ir; // 16 bit instruction register
always
begin
ir = m [pc]; //fetch an instruction
if (ir[15:13] == 3'b000) //begin decoding
pc = m [ir [12:0]]; //and executing
else if (ir[15:13] == 3'b001)
pc = pc + m [ir [12:0]];
else if (ir[15:13] == 3'b010)
acc = -m [ir [12:0]];
else if (ir[15:13] == 3'b011)
m [ir [12:0]] = acc;
else if ((ir[15:13] == 3'b101) || (ir[15:13] == 3'b100))
acc = acc - m [ir [12:0]];
else if (ir[15:13] == 3'b110)
if (acc < 0) pc = pc + 1;
#1 pc = pc + 1; //increment program counter and time
end
endmodule
//Example 2.6. The Mark-1 With a Case Statement.
module mark1Case;
reg [31:0] m [0:8191]; // 8192 x 32 bit memory
reg [12:0] pc; // 13 bit program counter
reg [31:0] acc; // 32 bit accumulator
reg [15:0] ir; // 16 bit instruction register
always
begin
ir = m [pc];
case (ir [15:13])
3'b000 : pc = m [ir [12:0]];
3'b001 : pc = pc + m [ir [12:0]];
3'b010 : acc = -m [ir [12:0]];
3'b011 : m [ir [12:0]] = acc;
3'b100,
3'b101 : acc = acc - m [ir [12:0]];
3'b110 : if (acc < 0) pc = pc + 1;
endcase
pc = pc + 1;
end
endmodule
//Example 2.8. The Mark-1 With a Multiply Instruction.
module mark1Mult;
reg [31:0] m [0:8191]; // 8192 x 32 bit memory
reg [12:0] pc; // 13 bit program counter
reg [31:0] acc; // 32 bit accumulator
reg [15:0] ir; // 16 bit instruction register
always
begin
ir = m [pc];
case (ir [15:13])
3'b000 : pc = m [ir [12:0]];
3'b001 : pc = pc + m [ir [12:0]];
3'b010 : acc = -m [ir [12:0]];
3'b011 : m [ir [12:0]] = acc;
3'b100,
3'b101 : acc = acc - m [ir [12:0]];
3'b110 : if (acc < 0) pc = pc + 1;
3'b111 : acc = acc * m [ir [12:0]]; //multiply
endcase
#1 pc = pc + 1;
end
endmodule
//Example 2.9. A Task Specification.
module mark1Task;
reg [31:0] m [0:8191]; // 8192 x 32 bit memory
reg [12:0] pc; // 13 bit program counter
reg [31:0] acc; // 32 bit accumulator
reg [15:0] ir; // 16 bit instruction register
always
begin
ir = m [pc];
case (ir [15:13])
3'b000 : pc = m [ir [12:0]];
3'b001 : pc = pc + m [ir [12:0]];
3'b010 : acc = -m [ir [12:0]];
3'b011 : m [ir [12:0]] = acc;
3'b100,
3'b101 : acc = acc - m [ir [12:0]];
3'b110 : if (acc < 0) pc = pc + 1;
3'b111 : multiply(acc, m [ir [12:0]]);
endcase
pc = pc + 1;
end
task multiply;
inout [31:0] a;
input [31:0] b;
reg [15:0] mcnd, mpy; //multiplicand and multiplier
reg [31:0] prod; //product
begin
mpy = b[15:0];
mcnd = a[15:0];
prod = 0;
repeat (16)
begin
if (mpy[0])
prod = prod + {mcnd, 16'h0000};
prod = prod >> 1;
mpy = mpy >> 1;
end
a = prod;
end
endtask
endmodule
//Example 2.10. A Function Specification.
module mark1Fun;
reg [31:0] m [0:8191]; // 8192 x 32 bit memory
reg [12:0] pc; // 13 bit program counter
reg [31:0] acc; // 32 bit accumulator
reg [15:0] ir; // 16 bit instruction register
always
begin
ir = m [pc];
case (ir [15:13])
3'b000 : pc = m [ir [12:0]];
3'b001 : pc = pc + m [ir [12:0]];
3'b010 : acc = -m [ir [12:0]];
3'b011 : m [ir [12:0]] = acc;
3'b100,
3'b101 : acc = acc - m [ir [12:0]];
3'b110 : if (acc < 0) pc = pc + 1;
3'b111 : acc = multiply(acc, m [ir [12:0]]);
endcase
pc = pc + 1;
end
function [31:0] multiply;
input [31:0] a;
input [31:0] b;
reg [15:0] mcnd,mpy;
begin
mpy = b[15:0];
mcnd = a[15:0];
multiply = 0;
repeat (16)
begin
if (mpy[0])
multiply = multiply + {mcnd, 16'h0000};
multiply = multiply >> 1;
mpy = mpy >> 1;
end
end
endfunction
endmodule
//Example 2.11. The Multiply as a Separate Module.
module mark1Mod;
reg [31:0] m [0:8191]; // 8192 x 32 bit memory
reg [12:0] pc; // 13 bit program counter
reg [31:0] acc; // 32 bit accumulator
reg [15:0] ir; // 16 bit instruction register
reg [31:0] mcnd;
reg go;
wire [31:0] prod;
wire done;
multiply mul (prod, acc, mcnd, go, done);
always
begin
go = 0;
ir = m [pc];
case (ir [15:13])
3'b000 : pc = m [ir [12:0]];
3'b001 : pc = pc + m [ir [12:0]];
3'b010 : acc = -m [ir [12:0]];
3'b011 : m [ir [12:0]] = acc;
3'b100,
3'b101 : acc = acc - m [ir [12:0]];
3'b110 : if (acc < 0) pc = pc + 1;
3'b111 : begin
mcnd = m [ir [12:0]];
go = 1;
wait (done);
acc = prod;
end
endcase
pc = pc + 1;
end
endmodule
module multiply (prod, mpy, mcnd, go, done);
output [31:0] prod;
input [31:0] mpy, mcnd;
input go;
output done;
reg [31:0] prod;
reg [15:0] myMpy;
reg done;
always
begin
done = 0;
wait (go);
myMpy = mpy[15:0];
prod = 0;
repeat (16)
begin
if (myMpy[0])
prod = prod + {mcnd, 16'h0000};
prod = prod >> 1;
myMpy = myMpy >> 1;
end
done = 1;
wait (~go);
end
endmodule
//Example 3.4. Fibonacci Number Generator Using Named Events.
module topNE();
wire [15:0] number, numberOut;
numberGenNE ng(number);
fibNumberGenNE fng(number, numberOut);
endmodule
module numberGenNE(number);
output [15:0] number;
reg [15:0] number;
event ready;
initial
number = 3;
always
begin
#100 number = number + 1;
-> ready; //generate event signal
end
endmodule
module fibNumberGenNE(startingValue, fibNum);
input [15:0] startingValue;
output [15:0] fibNum;
reg [15:0] myValue;
reg [15:0] fibNum;
always
begin
@ng.ready //accept event signal
myValue = startingValue;
for (fibNum = 0; myValue != 0; myValue = myValue - 1)
fibNum = fibNum + myValue;
$display ("%d, fibNum=%d", $time, fibNum);
end
endmodule
//Example 3.6 The Consumer With Fully Interlocked Handshake.
module consumer(dataIn, prodReady, consReady);
input [7:0] dataIn;
input prodReady;
output consReady;
reg consReady;
reg [7:0] dataInCopy;
always
begin
consReady = 1; // indicate consumer ready
forever
begin
wait (prodReady)
dataInCopy = dataIn;
consReady = 0; // indicate value consumed
//...munch on data
wait (!prodReady) // complete handshake
consReady = 1;
end
end
endmodule
//Example 3.7. The Producer With Fully Interlocked Handshake.
module producer(dataOut, prodReady, consReady);
output [7:0] dataOut;
output prodReady;
input consReady;
reg prodReady;
reg [7:0] dataOut,
temp;
always
begin
prodReady = 0; // indicate nothing to transfer
forever
begin
// ... produce data and put into "temp"
wait (consReady) // wait for consumer ready
dataOut = $random;
prodReady = 1; //indicate ready to transfer
wait (!consReady) //finish handshake
prodReady = 0;
end
end
endmodule
//Example 3.8. The Producer-Consumer Module.
module ProducerConsumer;
wire [7:0] data;
wire pReady, cReady;
producer p(data, pReady, cReady);
consumer c(data, pReady, cReady);
endmodule
//Example 3.10. Using Disable Statement to Model a Reset.
module numberGenDisable (number, reset);
output [15:0] number;
input reset;
event ready;
reg [15:0] number;
always
begin :generator
number = 3;
forever
begin
#100 number = number + 1;
-> ready;
end
end
always
@(negedge reset) disable generator;
endmodule
//Example 3.11. Flip Flop With Quasi-Continuous Assignment.
module dFlop (preset, clear, q, clock, d);
input preset, clear, clock, d;
output q;
reg q;
always
@(clear or preset)
begin
if (!clear)
#10 assign q = 0;
else if (!preset)
#10 assign q = 1;
else
#10 deassign q;
end
always
@(negedge clock)
#10 q = d;
endmodule
//Example 3.13. The Fibonacci Number Generator With Reset.
module numberGenFork (number, reset);
output [15:0] number;
input reset;
event ready;
reg [15:0] number;
always
begin
number = 3;
fork : generator
begin
#100 number = number + 1;
-> ready;
end
@(negedge reset)
disable generator;
join
end
endmodule
//Example 4.1. A One-Bit Full Adder
module fullAdder(cOut, sum, aIn, bIn, cIn);
output cOut, sum;
input aIn, bIn, cIn;
wire x2;
nand (x2, aIn, bIn),
(cOut, x2, x8);
xnor (x9, x5, x6);
nor (x5, x1, x3),
(x1, aIn, bIn);
or (x8, x1, x7);
not (sum, x9),
(x3, x2),
(x6, x4),
(x4, cIn),
(x7, x6);
endmodule
//Example 4.2. Wire AND Example.
module andOfComplements (a, b, c, d);
input a, b;
output c, d;
wand c;
wire d;
not (c, a);
not (c, b);
not (d, a);
not (d, b);
endmodule
//Example 4.3. Illustration of Continuous Assignment.
module oneBitFullAdder(cOut, sum, aIn, bIn, cIn);
output cOut, sum;
input aIn, bIn, cIn;
assign sum = aIn ^ bIn ^ cIn,
cOut = (aIn & bIn) | (bIn & cIn) | (aIn & cIn);
endmodule
//Example 4.4. Function Call From Continuous Assignment.
module multiplexor(a, b, c, d, select, e);
input a, b, c, d;
input [1:0] select;
output e;
assign e = mux (a, b, c,d, select);
function mux;
input a, b, c, d;
input [1:0] select;
case (select)
2'b00: mux = a;
2'b01: mux = b;
2'b10: mux = c;
2'b11: mux = d;
default: mux = 'bx;
endcase
endfunction
endmodule
//Example 4.5. Combined Net and Continuous Assignment.
module modXor (AXorB, a, b);
parameter size = 8, delay = 15;
output [size-1:0] AXorB;
input [size-1:0] a, b;
wire [size-1:0] #delay AXorB = a ^ b;
endmodule
//Example 4.6. Net and Continuous Assignment Delays.
module wandOfAssigns (a, b, c);
input a, b;
output c;
wand #10 c;
assign #5 c = ~a;
assign #3 c = ~b;
endmodule
//Example 4.7. Continuous Assignment to an Inout.
module bufferDriver (busLine, bufferedVal, bufInput, busEnable);
inout busLine;
input bufInput, busEnable;
output bufferedVal;
assign bufferedVal = busLine,
busLine = (busEnable) ? bufInput : 1'bz;
endmodule
//Example 4.10. A Tristate Latch.
module triStateLatch (qOut, nQOut, clock, data, enable);
output qOut, nQOut;
input clock, data, enable;
tri qOut, nQOut;
not #5 (ndata, data);
nand #(3,5) d(wa, data, clock),
nd(wb, ndata, clock);
nand #(12, 15) qQ(q, nq, wa),
nQ(nq, q, wb);
bufif1 #(3, 7, 13) qDrive (qOut, q, enable),
nQDrive(nQOut, nq, enable);
endmodule
//Example 4.11. Illustration of Min, Typical, and Max Delays.
module IOBuffer (bus, in, out, dir);
inout bus;
input in, dir;
output out;
parameter
R_Min = 3, R_Typ = 4, R_Max = 5,
F_Min = 3, F_Typ = 5, F_Max = 7,
Z_Min = 12, Z_Typ = 15, Z_Max = 17;
bufif1 #(R_Min: R_Typ: R_Max,
F_Min: F_Typ: F_Max,
Z_Min: Z_Typ: Z_Max)
(bus, out, dir);
buf #(R_Min: R_Typ: R_Max,
F_Min: F_Typ: F_Max)
(in, bus);
endmodule
//Example 5.1. A User-Defined Combinational Primitive.
primitive carry(carryOut, carryIn, aIn, bIn);
output carryOut;
input carryIn,
aIn,
bIn;
table
0 00 : 0;
0 01 : 0;
0 10 : 0;
0 11 : 1;
1 00 : 0;
1 01 : 1;
1 10 : 1;
1 11 : 1;
endtable
endprimitive
//Example 5.2. A Carry Primitive.
primitive carryX(carryOut, carryIn, aIn, bIn);
output carryOut;
input aIn,
bIn,
carryIn;
table
0 00 : 0;
0 01 : 0;
0 10 : 0;
0 11 : 1;
1 00 : 0;
1 01 : 1;
1 10 : 1;
1 11 : 1;
0 0x : 0;
0 x0 : 0;
x 00 : 0;
1 1x : 1;
1 x1 : 1;
x 11 : 1;
endtable
endprimitive
//Example 5.3. A Carry Primitive With Shorthand Notation.
primitive carryAbbrev(carryOut, carryIn, aIn, bIn);
output carryOut;
input aIn,
bIn,
carryIn;
table
0 0? : 0;
0 ?0 : 0;
? 00 : 0;
? 11 : 1;
1 ?1 : 1;
1 1? : 1;
endtable
endprimitive
//Example 5.4. A User-Defined Sequential Primitive.
primitive latch (q, clock, data);
output q;
reg q;
input clock, data;
table
// clock data state output
0 1 : ? : 1;
0 0 : ? : 0;
1 ? : ? : -;
endtable
endprimitive
//Example 5.5. Edge-Sensitive Behavior.
primitive dEdgeFF_Prim (q, clock, data);
output q;
reg q;
input clock, data;
table
// clock data state output
(01) 0 : ? : 0;
(01) 1 : ? : 1;
(0x) 1 : 1 : 1;
(0x) 0 : 0 : 0;
(?0) ? : ? : -;
? (??) : ? : -;
endtable
endprimitive
//Example 5.6. Edge-Sensitive Behavior With Shorthand Notation.
primitive dEdgeFFShort (q, clock, data);
output q;
reg q;
input clock, data;
table
// clock data state output
r 0 : ? : 0;
r 1 : ? : 1;
(0x) 0 : 1 : 1;
(0x) 1 : 1 : 1;
(?0) ? : ? : -;
? * : ? : -;
endtable
endprimitive
//Example 5.7. A JK Flip Flop Example.
primitive jkEdgeFF (q, clock, j, k, preset, clear);
output q; reg q;
input clock, j, k, preset, clear;
table
//clock jk pc state output
// preset logic
? ?? 01 : ? : 1;
? ?? *1 : 1 : 1;
// clear logic
? ?? 10 : ? : 0;
? ?? 1* : 0 : 0;
// normal clocking cases
r 00 11 : ? : -;
r 01 11 : ? : 0;
r 10 11 : ? : 1;
r 11 11 : 0 : 1;
r 11 11 : 1 : 0;
f ?? ?? : ? : -;
// j and k transition cases
b *? ?? : ? : -;
b ?* ?? : ? : -;
//cases reducing pessimism
p 00 11 : ? : -;
p 0? 1? : 0 : -;
p ?0 ?1 : 1 : -;
(x0) ?? ?? : ? : -;
(1x) 00 11 : ? : -;
(1x) 0? 1? : 0 : -;
(1x) ?0 ?1 : 1 : -;
x *0 ?1 : 1 : -;
x 0* 1? : 0 : -;
endtable
endprimitive
//Example 6.1. MOS Shift Register.
//Dynamic MOS serial shift register circuit description
module shreg (out, in, phase1, phase2);
/* IO port declarations, where 'out' is the inverse
of 'in' controlled by the dual-phased clock */
output out; //shift register output
input in, //shift register input
phase1, //clocks
phase2;
tri wb1, wb2, out; //tri nets pulled up to VDD
pullup //depletion mode pullup devices
(wb1), (wb2), (out);
trireg (medium) wa1, wa2, wa3; //charge storage nodes
supply0 gnd; //ground supply
nmos #3 //pass devices and their interconnections
a1(wa1,in,phase1), b1(wb1,gnd,wa1),
a2(wa2,wb1,phase2), b2(wb2,gnd,wa2),
a3(wa3,wb2,phase1), gout(out,gnd,wa3);
endmodule
//Example 6.2. Simulating the MOS Shift Register.
module waveShReg;
wire shiftout; //net to receive circuit output value
reg shiftin; //register to drive value into circuit
reg phase1,phase2; //clock driving values
parameter d = 100; //define the waveform time step
shreg cct (shiftout, shiftin, phase1, phase2);
initial
begin :main
shiftin = 0; //initialize waveform input stimulus
phase1 = 0;
phase2 = 0;
setmon; // setup the monitoring information
repeat(2) //shift data in
clockcct;
end
task setmon; //display header and setup monitoring
begin
$display(" time clks in out wa1-3 wb1-2");
$monitor ($time,,,,phase1, phase2,,,,,,shiftin,,,, shiftout,,,,,
cct.wa1, cct.wa2, cct.wa3,,,,,cct.wb1, cct.wb2);
end
endtask
task clockcct; //produce dual-phased clock pulse
begin
#d phase1 = 1; //time step defined by parameter d
#d phase1 = 0;
#d phase2 = 1;
#d phase2 = 0;
end
endtask
endmodule
//Example 6.3. A Static RAM Cell.
//description of a MOS static RAM cell
module sram(dataOut, address, dataIn, write);
output dataOut;
input address, dataIn, write;
tri w1, w3, w4, w43;
bufif1
g1(w1, dataIn, write);
tranif1
g2(w4, w1, address);
not (pull0, pull1)
g3(w3, w4), g4(w4, w3);
buf
g5(dataOut, w1);
endmodule
//waveform for testing the static RAM cell
module wave_sram;
wire dataOut;
reg address, dataIn, write;
//make the sram a submodule and define the interconnections
sram cell(dataOut, address, dataIn, write);
//define the waveform to drive the circuit
parameter d = 100;
initial
begin
#d dis;
#d address = 1;
#d dis;
#d dataIn = 1;
#d dis;
#d write = 1;
#d dis;
#d write = 0;
#d dis;
#d write = 'bx;
#d dis;
#d address = 'bx;
#d dis;
#d address = 1;
#d dis;
#d write = 0;
#d dis;
end
task dis; //display the circuit state
$display($time,,
"addr=%v d_in=%v write=%v d_out=%v",
address, dataIn, write, dataOut,
" (134)=%b%b%b w134=%v %v %v",
cell.w1, cell.w3, cell.w4,
cell.w1, cell.w3, cell.w4);
endtask
endmodule
//THE MINISIM EXAMPLE
module miniSim;
// element types being modeled
`define Nand 0
`define DEdgeFF 1
`define Wire 2
// literal values with strength:
// format is 8 0-strength bits in decreasing strength order
// followed by 8 1-strength bits in decreasing strength order
`define Strong0 16'b01000000_00000000
`define Strong1 16'b00000000_01000000
`define StrongX 16'b01111111_01111111
`define Pull0 16'b00100000_00000000
`define Pull1 16'b00000000_00100000
`define Highz0 16'b00000001_00000000
`define Highz1 16'b00000000_00000001
// three-valued logic set
`define Val0 3'd0
`define Val1 3'd1
`define ValX 3'd2
parameter
DebugFlags = 'b11000, //set to 1 for message
// |||||
// loading <--+||||
// event value changes <----+|||
// wire calculation <------+||
// evaluation <--------+|
// scheduling <-----------+
IndexSize = 16, //maximum size for index pointers
MaxElements = 50, //maximum number of elements
TypeSize = 12; //maximum number of types
reg [IndexSize-1:0]
eventElement, //output value change element
evalElement, //evaluation element on fanout
fo0Index[1:MaxElements], //first fanout index of eventElement
fo1Index[1:MaxElements], //second fanout index of eventElement
currentList, //current time scheduled event list
nextList, //unit delay scheduled event list
schedList[1:MaxElements]; //scheduled event list index
reg [TypeSize-1:0]
eleType[1:MaxElements]; //element type
reg
fo0TermNum[1:MaxElements], //first fanout input terminal number
fo1TermNum[1:MaxElements], //second fanout input terminal number
schedPresent[1:MaxElements]; //element is in scheduled event list flags
reg [15:0]
eleStrength[1:MaxElements], //element strength indication
outVal[1:MaxElements], //element output value
in0Val[1:MaxElements], //element first input value
in1Val[1:MaxElements], //element second input value
in0, in1, out, oldIn0; //temporary value storage
integer pattern, simTime; //time keepers
initial
begin
// initialize variables
pattern = 0;
currentList = 0;
nextList = 0;
$display("Loading toggle circuit");
loadElement(1, `DEdgeFF, 0, `Strong1,0,0, 4,0,0,0);
loadElement(2, `DEdgeFF, 0, `Strong1,0,0, 3,0,0,0);
loadElement(3, `Nand, (`Strong0|`Strong1),
`Strong0,`Strong1,`Strong1, 4,0,1,0);
loadElement(4, `DEdgeFF, (`Strong0|`Strong1),
`Strong1,`Strong1,`Strong0, 3,0,1,0);
// apply stimulus and simulate
$display("Applying 2 clocks to input element 1");
applyClock(2, 1);
$display("Changing element 2 to value 0 and applying 1 clock");
setupStim(2, `Strong0);
applyClock(1, 1);
$display("\nLoading open-collector and pullup circuit");
loadElement(1, `DEdgeFF, 0, `Strong1,0,0, 3,0,0,0);
loadElement(2, `DEdgeFF, 0, `Strong0,0,0, 4,0,0,0);
loadElement(3, `Nand, (`Strong0|`Highz1),
`Strong0,`Strong1,`Strong1, 5,0,0,0);
loadElement(4, `Nand, (`Strong0|`Highz1),
`Highz1,`Strong0,`Strong1, 5,0,1,0);
loadElement(5, `Wire, 0,
`Strong0,`Strong0,`Highz1, 7,0,1,0);
loadElement(6, `DEdgeFF, 0, `Pull1,0,0, 7,0,0,0);
loadElement(7, `Wire, 0,
`Strong0,`Pull1,`Strong0, 0,0,0,0);
// apply stimulus and simulate
$display("Changing element 1 to value 0");
pattern = pattern + 1;
setupStim(1, `Strong0);
executeEvents;
$display("Changing element 2 to value 1");
pattern = pattern + 1;
setupStim(2, `Strong1);
executeEvents;
$display("Changing element 2 to value X");
pattern = pattern + 1;
setupStim(2, `StrongX);
executeEvents;
end
// Initialize data structure for a given element.
task loadElement;
input [IndexSize-1:0] loadAtIndex; //element index being loaded
input [TypeSize-1:0] type; //type of element
input [15:0] strengthCoercion; //strength specification of element
input [15:0] oVal, i0Val, i1Val; //output and input values
input [IndexSize-1:0] fo0, fo1; //fanout element indexes
input fo0Term, fo1Term; //fanout element input terminal indicators
begin
if (DebugFlags[4])
$display(
"Loading element %0d, type %0s, with initial value %s(%b_%b)",
loadAtIndex, typeString(type),
valString(oVal), oVal[15:8], oVal[7:0]);
eleType[loadAtIndex] = type;
eleStrength[loadAtIndex] = strengthCoercion;
outVal[loadAtIndex] = oVal;
in0Val[loadAtIndex] = i0Val;
in1Val[loadAtIndex] = i1Val;
fo0Index[loadAtIndex] = fo0;
fo1Index[loadAtIndex] = fo1;
fo0TermNum[loadAtIndex] = fo0Term;
fo1TermNum[loadAtIndex] = fo1Term;
schedPresent[loadAtIndex] = 0;
end
endtask
// Given a type number, return a type string
function [32*8:1] typeString;
input [TypeSize-1:0] type;
case (type)
`Nand: typeString = "Nand";
`DEdgeFF: typeString = "DEdgeFF";
`Wire: typeString = "Wire";
default: typeString = "*** Unknown element type";
endcase
endfunction
// Setup a value change on an element.
task setupStim;
input [IndexSize-1:0] vcElement; //element index
input [15:0] newVal; //new element value
begin
if (! schedPresent[vcElement])
begin
schedList[vcElement] = currentList;
currentList = vcElement;
schedPresent[vcElement] = 1;
end
outVal[vcElement] = newVal;
end
endtask
// Setup and simulate a given number of clock pulses to a given element.
task applyClock;
input [7:0] nClocks;
input [IndexSize-1:0] vcElement;
repeat(nClocks)
begin
pattern = pattern + 1;
setupStim(vcElement, `Strong0);
executeEvents;
pattern = pattern + 1;
setupStim(vcElement, `Strong1);
executeEvents;
end
endtask
// Execute all events in the current event list.
// Then move the events in the next event list to the current event
// list and loop back to execute these events. Continue this loop
// until no more events to execute.
// For each event executed, evaluate the two fanout elements if present.
task executeEvents;
reg [15:0] newVal;
begin
simTime = 0;
while (currentList)
begin
eventElement = currentList;
currentList = schedList[eventElement];
schedPresent[eventElement] = 0;
newVal = outVal[eventElement];
if (DebugFlags[3])
$display(
"At %0d,%0d Element %0d, type %0s, changes to %s(%b_%b)",
pattern, simTime,
eventElement, typeString(eleType[eventElement]),
valString(newVal), newVal[15:8], newVal[7:0]);
if (fo0Index[eventElement]) evalFo(0);
if (fo1Index[eventElement]) evalFo(1);
if (! currentList) // if empty move to next time unit
begin
currentList = nextList;
nextList = 0;
simTime = simTime + 1;
end
end
end
endtask
// Evaluate a fanout element by testing its type and calling the
// appropriate evaluation routine.
task evalFo;
input fanout; //first or second fanout indicator
begin
evalElement = fanout ? fo1Index[eventElement] :
fo0Index[eventElement];
if (DebugFlags[1])
$display("Evaluating Element %0d type is %0s",
evalElement, typeString(eleType[evalElement]));
case (eleType[evalElement])
`Nand: evalNand(fanout);
`DEdgeFF: evalDEdgeFF(fanout);
`Wire: evalWire(fanout);
endcase
end
endtask
// Store output value of event element into
// input value of evaluation element.
task storeInVal;
input fanout; //first or second fanout indicator
begin
// store new input value
if (fanout ? fo1TermNum[eventElement] : fo0TermNum[eventElement])
in1Val[evalElement] = outVal[eventElement];
else
in0Val[evalElement] = outVal[eventElement];
end
endtask
// Convert a given full strength value to three-valued logic (0, 1 or X)
function [1:0] log3;
input [15:0] inVal;
casez (inVal)
16'b00000000_00000000: log3 = `ValX;
16'b???????0_00000000: log3 = `Val0;
16'b00000000_???????0: log3 = `Val1;
default: log3 = `ValX;
endcase
endfunction
// Convert a given full strength value to four-valued logic (0, 1, X or Z),
// returning a 1 character string
function [8:1] valString;
input [15:0] inVal;
case (log3(inVal))
`Val0: valString = "0";
`Val1: valString = "1";
`ValX: valString = (inVal & 16'b11111110_11111110) ? "X" : "Z";
endcase
endfunction
// Coerce a three-valued logic output value to a full output strength value
// for the scheduling of the evaluation element
function [15:0] strengthVal;
input [1:0] logVal;
case (logVal)
`Val0: strengthVal = eleStrength[evalElement] & 16'b11111111_00000000;
`Val1: strengthVal = eleStrength[evalElement] & 16'b00000000_11111111;
`ValX: strengthVal = fillBits(eleStrength[evalElement]);
endcase
endfunction
// Given an incomplete strength value, fill the missing strength bits.
// The filling is only necessary when the value is unknown.
function [15:0] fillBits;
input [15:0] val;
begin
fillBits = val;
if (log3(val) == `ValX)
begin
casez (val)
16'b1???????_????????: fillBits = fillBits | 16'b11111111_00000001;
16'b01??????_????????: fillBits = fillBits | 16'b01111111_00000001;
16'b001?????_????????: fillBits = fillBits | 16'b00111111_00000001;
16'b0001????_????????: fillBits = fillBits | 16'b00011111_00000001;
16'b00001???_????????: fillBits = fillBits | 16'b00001111_00000001;
16'b000001??_????????: fillBits = fillBits | 16'b00000111_00000001;
16'b0000001?_????????: fillBits = fillBits | 16'b00000011_00000001;
endcase
casez (val)
16'b????????_1???????: fillBits = fillBits | 16'b00000001_11111111;
16'b????????_01??????: fillBits = fillBits | 16'b00000001_01111111;
16'b????????_001?????: fillBits = fillBits | 16'b00000001_00111111;
16'b????????_0001????: fillBits = fillBits | 16'b00000001_00011111;
16'b????????_00001???: fillBits = fillBits | 16'b00000001_00001111;
16'b????????_000001??: fillBits = fillBits | 16'b00000001_00000111;
16'b????????_0000001?: fillBits = fillBits | 16'b00000001_00000011;
endcase
end
end
endfunction
// Evaluate a 'Nand' gate primitive.
task evalNand;
input fanout; //first or second fanout indicator
begin
storeInVal(fanout);
// calculate new output value
in0 = log3(in0Val[evalElement]);
in1 = log3(in1Val[evalElement]);
out = ((in0 == `Val0) || (in1 == `Val0)) ?
strengthVal(`Val1) :
((in0 == `ValX) || (in1 == `ValX)) ?
strengthVal(`ValX):
strengthVal(`Val0);
// schedule if output value is different
if (out != outVal[evalElement])
schedule(out);
end
endtask
// Evaluate a D positive edge-triggered flip flop
task evalDEdgeFF;
input fanout; //first or second fanout indicator
// check value change is on clock input
if (fanout ? (fo1TermNum[eventElement] == 0) :
(fo0TermNum[eventElement] == 0))
begin
// get old clock value
oldIn0 = log3(in0Val[evalElement]);
storeInVal(fanout);
in0 = log3(in0Val[evalElement]);
// test for positive edge on clock input
if ((oldIn0 == `Val0) && (in0 == `Val1))
begin
out = strengthVal(log3(in1Val[evalElement]));
if (out != outVal[evalElement])
schedule(out);
end
end
else
storeInVal(fanout); // store data input value
endtask
// Evaluate a wire with full strength values
task evalWire;
input fanout;
reg [7:0] mask;
begin
storeInVal(fanout);
in0 = in0Val[evalElement];
in1 = in1Val[evalElement];
mask = getMask(in0[15:8]) & getMask(in0[7:0]) &
getMask(in1[15:8]) & getMask(in1[7:0]);
out = fillBits((in0 | in1) & {mask, mask});
if (out != outVal[evalElement])
schedule(out);
if (DebugFlags[2])
$display("in0 = %b_%b\nin1 = %b_%b\nmask= %b %b\nout = %b_%b",
in0[15:8],in0[7:0], in1[15:8],in1[7:0],
mask,mask, out[15:8],out[7:0]);
end
endtask
// Given either a 0-strength or 1-strength half of a strength value
// return a masking pattern for use in a wire evaluation.
function [7:0] getMask;
input [7:0] halfVal; //half a full strength value
casez (halfVal)
8'b???????1: getMask = 8'b11111111;
8'b??????10: getMask = 8'b11111110;
8'b?????100: getMask = 8'b11111100;
8'b????1000: getMask = 8'b11111000;
8'b???10000: getMask = 8'b11110000;
8'b??100000: getMask = 8'b11100000;
8'b?1000000: getMask = 8'b11000000;
8'b10000000: getMask = 8'b10000000;
8'b00000000: getMask = 8'b11111111;
endcase
endfunction
// Schedule the evaluation element to change to a new value.
// If the element is already scheduled then just insert the new value.
task schedule;
input [15:0] newVal; // new value to change to
begin
if (DebugFlags[0])
$display(
"Element %0d, type %0s, scheduled to change to %s(%b_%b)",
evalElement, typeString(eleType[evalElement]),
valString(newVal), newVal[15:8], newVal[7:0]);
if (! schedPresent[evalElement])
begin
schedList[evalElement] = nextList;
schedPresent[evalElement] = 1;
nextList = evalElement;
end
outVal[evalElement] = newVal;
end
endtask
endmodule
/******************* PLEASE NOTE: ************************************
The values printed out in the book section 7.1.3 have the wrong
format. The 0 and 1 strength bits need to be swapped around. For
example, the first value printed in the book should read
1(00000000_01000000) and not 1(0100000_00000000). The value format you
get from running the above minisim description through Verilog should
be correct.
Here is a listing of the results:
Loading toggle circuit
Loading element 1, type DEdgeFF, with initial value 1(00000000_01000000)
Loading element 2, type DEdgeFF, with initial value 1(00000000_01000000)
Loading element 3, type Nand, with initial value 0(01000000_00000000)
Loading element 4, type DEdgeFF, with initial value 1(00000000_01000000)
Applying 2 clocks to input element 1
At 1,0 Element 1, type DEdgeFF, changes to 0(01000000_00000000)
At 2,0 Element 1, type DEdgeFF, changes to 1(00000000_01000000)
At 2,1 Element 4, type DEdgeFF, changes to 0(01000000_00000000)
At 2,2 Element 3, type Nand, changes to 1(00000000_01000000)
At 3,0 Element 1, type DEdgeFF, changes to 0(01000000_00000000)
At 4,0 Element 1, type DEdgeFF, changes to 1(00000000_01000000)
At 4,1 Element 4, type DEdgeFF, changes to 1(00000000_01000000)
At 4,2 Element 3, type Nand, changes to 0(01000000_00000000)
Changing element 2 to value 0 and applying 1 clock
At 5,0 Element 1, type DEdgeFF, changes to 0(01000000_00000000)
At 5,0 Element 2, type DEdgeFF, changes to 0(01000000_00000000)
At 5,1 Element 3, type Nand, changes to 1(00000000_01000000)
At 6,0 Element 1, type DEdgeFF, changes to 1(00000000_01000000)
Loading open-collector and pullup circuit
Loading element 1, type DEdgeFF, with initial value 1(00000000_01000000)
Loading element 2, type DEdgeFF, with initial value 0(01000000_00000000)
Loading element 3, type Nand, with initial value 0(01000000_00000000)
Loading element 4, type Nand, with initial value Z(00000000_00000001)
Loading element 5, type Wire, with initial value 0(01000000_00000000)
Loading element 6, type DEdgeFF, with initial value 1(00000000_00100000)
Loading element 7, type Wire, with initial value 0(01000000_00000000)
Changing element 1 to value 0
At 7,0 Element 1, type DEdgeFF, changes to 0(01000000_00000000)
At 7,1 Element 3, type Nand, changes to Z(00000000_00000001)
At 7,2 Element 5, type Wire, changes to Z(00000000_00000001)
At 7,3 Element 7, type Wire, changes to 1(00000000_00100000)
Changing element 2 to value 1
At 8,0 Element 2, type DEdgeFF, changes to 1(00000000_01000000)
At 8,1 Element 4, type Nand, changes to 0(01000000_00000000)
At 8,2 Element 5, type Wire, changes to 0(01000000_00000000)
At 8,3 Element 7, type Wire, changes to 0(01000000_00000000)
Changing element 2 to value X
At 9,0 Element 2, type DEdgeFF, changes to X(01111111_01111111)
At 9,1 Element 4, type Nand, changes to X(01111111_00000001)
At 9,2 Element 5, type Wire, changes to X(01111111_00000001)
At 9,3 Element 7, type Wire, changes to X(01111111_00111111)
*****************************************************************/
//THE 8251A EXAMPLE
//Cadence Design Systems, Inc. does not guarantee
//the accuracy or completeness of this model.
//Anyone using this does so at their own risk.
//Intel and MCS are trademarks of the Intel Corporation.
module I8251A(dbus,rcd,gnd,txc_,write_,chipsel_,comdat_,read_,rxrdy,
txrdy,syndet,cts_,txe,txd,clk,reset,dsr_,rts_,dtr_,rxc_,vcc);
parameter [7:0] instance_id = 8'h00;
parameter [8:1] dflags = 8'b00000100; // Defaults for diagnostics
// |||||
// diagnostic dflags: |||||
// bit 5 (16) operation event trace <-+||||
// bit 4 (8) communication errors <---+||| (parity, frame, overrun)
// bit 3 (4) timing check <-----+||
// bit 2 (2) = print receiving <-------+|
// bit 1 (1) = print transmitting <---------+
/* timing constants, for A. C. timing check, only non-zero times
are specified, in nano-sec */
/* read cycle */
`define TRR 250
`define TRD 200
`define TDF 100 // max. time used
/* write cycle */
`define TWW 250
`define TDW 150
`define TWD 20
`define TRV 6 // in terms of clock cycles
/* other timing */
`define TTXRDY 8 // 8 clock cycles
input
rcd, // receive data
rxc_, // receive clock
txc_, // transmit clock
chipsel_, // chip selected when low
comdat_, // command/data_ select
read_, write_,
dsr_, // data set ready
cts_, // clear to send
reset,// reset when high
clk, // at least 30 times of the transmit/receive data bit rates
gnd,
vcc;
output
rxrdy, // receive data ready when high
txd, // transmit data line
txrdy, // transmit buffer ready to accept another byte to transfer
txe, // transmit buffer empty
rts_, // request to send
dtr_; // data terminal ready
inout[7:0]
dbus;
inout
syndet; //outside synchonous detect or output to indicate syn det
supply0
gnd;
supply1
vcc;
reg
txd,
rxrdy,
txe,
dtr_,
rts_;
reg[7:0]
receivebuf,
rdata,
status;
reg
recvdrv,
statusdrv;
assign
// if recvdrv 1 dbus is driven by rdata
dbus = recvdrv ? rdata : 8'bz,
dbus = statusdrv ? status : 8'bz;
reg[7:0]
command,
tdata_out, // data being transmitted serially
tdata_hold, // data to be transmitted next if tdata_out is full
sync1, sync2, // synchronous data bytes
modreg;
and (txrdy,status[0],command[0], ~ cts_);
reg transmitter_reset, // set to 1 upon a reset, cleared upon write data
tdata_out_full, // 1 if data in tdata_out has not been transmitted.
tdata_hold_full, // 1 if data in tdata_hold has not been transferred to
// tdata_out for serial transmission.
tdata_hold_cts; // 1 if tdata_hold_full and it was cts when data was
// transferred to tdata_hold.
// 0 if tdata_hold is empty or is full but was filled
// while it was not cts.
reg tdata_out_wait; // 0 if a stop bit was just sent and we do not need
// to wait for a negedge on txc before transmitting
reg[7:0] syncmask;
nmos syndet_gate1(syndet,status[6], ~ modreg[6]);
reg sync_to_receive; // 1(2) if looking for 1st(2nd) sync on rxd
reg syncs_received; // 1 if sync chars received, 0 if looking for sync(s)
reg rec_sync_index; // indicating the syn. character to be matched
integer breakcount_period; // number of clock periods to count as break
reg sync_to_transmit; // 1(2) if 1st(2nd) sync char should be sent next
reg[7:0] data_mask; // masks off the data bits (if char size is not 8)
// temporary registers
reg[1:0] csel; // indicates what next write means if comdat_=1:
// (0=mode instruction,1=sync1,2=sync2,3=command)
reg[5:0]
baudmx,
tbaudcnt,
rbaudcnt; // baud rate
reg[7:0]
tstoptotal; // no. of tranmit clock pulses for stop bit (0 if sync mode)
reg[3:0]
databits; // no. of data bits in a character (5,6,7 or 8)
reg
rdatain; // a data byte is read in if 1
reg was_cts_when_received; // 0: if cts_ was high when char was received
// 1: if cts_ was low when char was received
// (and so char was sent before shutdown)
event
resete,
start_receiver_e;
reg receive_in_progress;
event txende;
/*** COMMUNICATION ERRORS ***/
task frame_error;
begin
if(dflags[4])
$display("I8251A (%h) at %d: *** frame error",
instance_id, $time);
status[5]=1;
end
endtask
task parity_error;
begin
if(dflags[4])
$display("I8251A (%h) at %d: *** parity error on data: %b",
instance_id, $time, receivebuf);
status[3]=1;
end
endtask
task overrun_error;
begin
if(dflags[4])
$display("I8251A (%h) at %d: *** overrun error",
instance_id, $time);
status[4]=1;
end
endtask
/*** TIMING VIOLATIONS ***/
integer
time_dbus_setup,
time_write_begin,
time_write_end,
time_read_begin,
time_read_end,
between_write_clks; // to check between write recovery
reg reset_signal_in; // to check the reset signal pulse width
initial
begin
time_dbus_setup = -9999;
time_write_begin = -9999;
time_write_end = -9999;
time_read_begin = -9999;
time_read_end = -9999;
between_write_clks = `TRV; // start: TRV clk periods since last write
end
/** Timing analysis for read cycles **/
always @(negedge read_)
if (chipsel_==0)
begin
time_read_begin=$time;
read_address_watch;
end
/* Timing violation: read pulse must be TRR ns */
always @(posedge read_)
if (chipsel_==0)
begin
disable read_address_watch;
time_read_end=$time;
if(dflags[3] && (($time-time_read_begin) < `TRR))
$display("I8251A (%h) at %d: *** read pulse width violation",
instance_id, $time);
end
/* Timing violation: address (comdat_ and chipsel_) must be stable */
/* stable throughout read */
task read_address_watch;
@(comdat_ or chipsel_) // if the "address" changes
if (read_==0) // and read_ did not change at the same time
if (dflags[3])
$display("I8251A (%h) at %d: *** address hold error on read",
instance_id, $time);
endtask
/** Timing analysis for write cycles **/
always @(negedge write_)
if (chipsel_==0)
begin
time_write_begin=$time;
write_address_watch;
end
/* Timing violation: read pulse must be TRR ns */
/* Timing violation: TDW ns bus setup time before posedge write_ */
/* Timing violation: TWD ns bus hold time after posedge write_ */
always @(posedge write_)
if (chipsel_==0)
begin
disable write_address_watch;
time_write_end=$time;
if(dflags[3] && (($time-time_write_begin) < `TWW))
$display("I8251A (%h) at %d: *** write pulse width violation",
instance_id, $time);
if(dflags[3] && (($time-time_dbus_setup) < `TDW))
$display("I8251A (%h) at %d: *** data setup violation on write",
instance_id, $time);
end
always @dbus
begin
time_dbus_setup=$time;
if(dflags[3] && (($time-time_write_end < `TWD)))
$display("I8251A (%h) at %d: *** data hold violation on write",
instance_id, $time);
end
/* Timing violation: address (comdat_ and chipsel_) must be stable */
/* stable throughout write */
task write_address_watch;
@(comdat_ or chipsel_) // if the "address" changes
if (write_==0) // and write_ did not change at the same time
if (dflags[3])
$display("I8251A (%h) at %d: *** address hold error on write",
instance_id, $time);
endtask
/* Timing violation: minimum of TRV clk cycles between writes */
always @(negedge write_)
if (chipsel_== 0)
begin
time_write_begin=$time;
if(dflags[3] && between_write_clks < `TRV)
$display("I8251A (%h) at %d: *** between write recovery violation",
instance_id, $time);
between_write_clks = 0;
end
always @(negedge write_)
repeat (`TRV) @(posedge clk)
between_write_clks = between_write_clks + 1;
/** Timing analysis for reset sequence **/
/* Timing violation: reset pulse must be 6 clk cycles */
always @(posedge reset)
begin :reset_block
reset_signal_in=1;
repeat(6) @(posedge clk);
reset_signal_in=0;
// external reset
-> resete;
end
always @(negedge reset)
begin
if(dflags[3] && (reset_signal_in==1))
$display("I8251A (%h) at %d: *** reset pulse too short",
instance_id, $time);
disable reset_block;
end
/*** BEHAVIORAL DESCRIPTION ***/
/* Reset sequence */
initial
begin // power-on reset
reset_signal_in=0;
->resete;
end
always @resete
begin
if(dflags[5])
$display("I8251A (%h) at %d: performing reset sequence",
instance_id, $time);
csel=0;
transmitter_reset=1;
tdata_out_full=0;
tdata_out_wait=0;
tdata_hold_full=0;
tdata_hold_cts=0;
rdatain=0;
status=4; // only txe is set
txe=1;
statusdrv=0;
recvdrv=0;
txd=1; // line at mark state upon reset until data is transmitted
// assign not allowed for status, etc.
rxrdy=0;
command=0;
dtr_=1;
rts_=1;
status[6]=0; // syndet is reset to output low
sync_to_transmit=1; // transmit sync char #1 when sync are transmt'd
sync_to_receive=1;
between_write_clks = `TRV;
receive_in_progress=0;
disable read_address_watch;
disable write_address_watch;
disable trans1;
disable trans2;
disable trans3;
disable trans4;
disable rcv_blk;
disable sync_hunt_blk;
disable double_sync_hunt_blk;
disable parity_sync_hunt_blk;
disable syn_receive_internal;
disable asyn_receive;
disable break_detect_blk;
disable break_delay_blk;
end
always @(negedge read_)
if (chipsel_==0)
begin
#(`TRD) // time for data to show on the data bus
if (comdat_==0) // 8251A DATA ==> DATA BUS
begin
recvdrv=1;
rdatain=0; // no receive byte is ready
rxrdy=0;
status[1]=0;
end
else // 8251A STATUS ==> DATA BUS
begin
statusdrv=1;
if (modreg [1:0] == 2'b00) // if sync mode
status[6]=0; // reset syndet upon status read
// Note: is only reset upon reset
// or rxd=1 in async mode
end
end
always @(posedge read_)
begin
#(`TDF) // data from read stays on the bus after posedge read_
recvdrv=0;
statusdrv=0;
end
always @(negedge write_)
begin
if((chipsel_==0) && (comdat_==0))
begin
txe=0;
status[2]=0; // transmitter not empty after receiving data
status[0]=0; // transmitter not ready after receiving data
end
end
always @(posedge write_) // read the command/data from the CPU
if (chipsel_==0)
begin
if (comdat_==0) // DATA BUS ==> 8251A DATA
begin
case (command[0] & ~ cts_)
0: // if it is not clear to send
begin
tdata_hold=dbus;
tdata_hold_full=1;// then mark the data as received and
tdata_hold_cts=0; // that it should be sent when cts
end
1: // if it is clear to send ...
if(transmitter_reset) // ... and this is 1st data since reset
begin
transmitter_reset=0;
tdata_out=dbus;
tdata_out_wait=1; // then wait for a negedge on txc
tdata_out_full=1; // and transmit the data
tdata_hold_full=0;
tdata_hold_cts=0;
repeat(`TTXRDY) @(posedge clk);
status[0]=1; // and set the txrdy status bit
end
else // ... and a sync/data char is being sent
begin
tdata_hold=dbus; // then mark the data as being received
tdata_hold_full=1;// and that it should be transmitted if
tdata_hold_cts=1; // it becomes not cts,
// but do not set the txrdy status bit
end
endcase
end
else // DATA BUS ==> CONTROL
begin
case (csel)
0: // case 0: MODE INSTRUCTION
begin
modreg=dbus;
if(modreg[1:0]==0) // synchronous mode
begin
csel=1;
baudmx=1;
tstoptotal=0; // no stop bit for synch. op.
end
else
begin // asynchronous mode
csel=3;
baudmx=1; // 1X baud rate
if(modreg[1:0]==2'b10)baudmx=16;
if(modreg[1:0]==2'b11)baudmx=64;
// set up the stop bits in clocks
tstoptotal=baudmx;
if(modreg[7:6]==2'b10)tstoptotal=
tstoptotal+baudmx/2;
if(modreg[7:6]==2'b11)tstoptotal=
tstoptotal+tstoptotal;
end
databits=modreg[3:2]+5; // bits per char
data_mask=255 >> (3-modreg[3:2]);
end
1: // case 1: 1st SYNC CHAR - SYNC MODE
begin
sync1=dbus;
/* the syn. character will be adjusted to the most
significant bit to simplify syn. hunt,
syncmask is also set to test the top data bits */
case (modreg[3:2])
0:
begin
sync1=sync1<< 3;
syncmask=8'b11111000;
end
1:
begin
sync1=sync1<< 2;
syncmask=8'b11111100;
end
2:
begin
sync1=sync1<< 1;
syncmask=8'b11111110;
end
3:
syncmask=8'b11111111;
endcase
if(modreg[7]==0)
csel=2; // if in double sync char mode, get 2 syncs
else
csel=3; // if in single sync char mode, get 1 sync
end
2: // case 2: 2nd SYNC CHAR - SYNC MODE
begin
sync2=dbus;
case (modreg[3:2])
0: sync2=sync2<< 3;
1: sync2=sync2<< 2;
2: sync2=sync2<< 1;
endcase
csel=3;
end
3: // case 3: COMMAND INSTRUCTION - SYNC/ASYNC MODE
begin
status[0]=0; // Trick: force delay txrdy pin if command[0]=1
command=dbus;
dtr_= ! command[1];
if(command[3]) // if send break command
assign txd=0; // set txd=0 (ignores/overrides
else // later non-assign assignments)
deassign txd;
if(command[4]) status[5:3]=0; // Clear Frame/Parity/Overrun
rts_= ! command[5];
if(command[6]) ->resete; // internal reset
if(modreg[1:0]==0 && command[7])
begin // if sync mode and enter hunt
disable // disable the sync receiver
syn_receive_internal;
disable
syn_receive_external;
receivebuf=8'hff; // reset recieve buffer 1's
-> start_receiver_e; // restart sync mode receiver
end
if(receive_in_progress==0)
-> start_receiver_e;
repeat(`TTXRDY) @(posedge clk);
status[0]=1;
end
endcase
end
end
reg [7:0] serial_data;
reg parity_bit;
always wait (tdata_out_full==1)
begin :trans1
if(dflags[1])
$display("I8251A (%h) at %d: transmitting data: %b",
instance_id, $time, tdata_out);
if (tdata_out_wait) // if the data arrived any old time
@(negedge txc_); // wait for a negedge on txc_
// but if a stop bit was just sent
// do not wait
serial_data=tdata_out;
if (tstoptotal != 0) // if async mode ...
begin
txd=0; // then send a start bit 1st
repeat(baudmx) @(negedge txc_);
end
repeat(databits) // send all start, data bits
begin
txd=serial_data[0];
repeat(baudmx) @(negedge txc_);
serial_data=serial_data>> 1;
end
if (modreg [4]) // if parity is enabled ...
begin
parity_bit= ^ (tdata_out & data_mask);
if(modreg[5]==0)parity_bit= ~parity_bit; // odd parity
txd=parity_bit;
repeat(baudmx) @(negedge txc_); // then send the parity bit
end
if(tstoptotal != 0) // if sync mode
begin
txd=1; // then send out the stop bit(s)
repeat(tstoptotal) @(negedge txc_);
end
tdata_out_full=0;// block this routine until data/sync char to be sent
// is immediately transferred to tdata_out.
->txende; // decide what data should be sent (data/sync/stop bit)
end
event transmit_held_data_e,
transmitter_idle_e;
always @txende // end of transmitted data/sync character
begin :trans2
case (command[0] & ~ cts_)
0: // if it is not now cts
// but data was received while it was cts
if (tdata_hold_full && tdata_hold_cts)
-> transmit_held_data_e; // then send the data char
else
-> transmitter_idle_e; // else send sync char(s) or 1 stop bit
1: // if it is now cts
if (tdata_hold_full) // if a character has been received
// but not yet transmitted ...
-> transmit_held_data_e; // then send the data char
else // else (no character has been received)
-> transmitter_idle_e; // send sync char(s) or 1 stop bit
endcase
end
always @transmitter_idle_e // if there are no data chars to send ...
begin :trans3
status[2]=1; // mark transmitter as being empty
txe=1;
if (tstoptotal != 0 || // if async mode or after a reset
command[0]==0 || cts_==1)// or TxEnable=false or cts_=false
begin
if(dflags[1])
$display("I8251A (%h) at %d: transmitting data: 1 (stop bit)",
instance_id, $time);
txd=1; // then send out 1 stop bit
tdata_out=1; // and make any writes
// go to tdata_hold
repeat(baudmx) @(negedge txc_);
-> txende;
end
else // if sync mode
case (sync_to_transmit)
1: // ... send 1st sync char now
begin
tdata_out=sync1 >> (8-databits);
tdata_out_wait=0; // without waiting on negedge txc
tdata_out_full=1;
if(modreg[7]==0) // if double sync mode
sync_to_transmit=2;// send 2nd sync after 1st
end
2: // ... send 2nd sync char now
begin
tdata_out=sync2 >> (8-databits);
tdata_out_wait=0; // without waiting on negedge txc
tdata_out_full=1;
sync_to_transmit=1; // send 1st sync char next
end
endcase
end
always @ transmit_held_data_e // if a character has been received
begin :trans4
tdata_out=tdata_hold; // but not transmitted ...
tdata_out_wait=0; // then do not wait on negedge txc
tdata_out_full=1; // and send the char immediately
tdata_hold_full=0;
repeat(`TTXRDY) @(posedge clk);
status[0]=1; // and set the txrdy status bit
end
/******************** RECEIVER PORTION OF THE 8251A ********************/
// data is received at leading edge of the clock
event break_detect_e, //
break_delay_e; //
event hunt_sync1_e, // hunt for the 1st sync char
hunt_sync2_e, // hunt for the 2nd sync char (double sync mode)
sync_hunted_e, // sync char(s) was found (on a bit aligned basis)
external_syndet_watche;// external sync mode: whenever syndet pin
// goes high, set the syndet status bit
always @start_receiver_e
begin :rcv_blk
receive_in_progress=1;
case(modreg[1:0])
2'b00:
if(modreg[6]==0) // if internal syndet mode ...
begin
if(dflags[5])
$display("I8251A (%h) at %d: starting internal sync receiver",
instance_id, $time);
if(dflags[5] && command[7])
$display("I8251A (%h) at %d: hunting for syncs",
instance_id, $time);
if(modreg[7]==1) // if enter hunt mode
begin
if(dflags[5])
$display("I8251A (%h) at %d: receiver waiting on syndet",
instance_id, $time);
-> hunt_sync1_e; // start search for sync char(s)
// & wait for syncs to be found
@(posedge syndet);
if(dflags[5])
$display("I8251A (%h) at %d: receiver DONE waiting on syndet",
instance_id, $time);
end
syn_receive_internal; // start sync mode receiver
end
else // if external syndet mode ...
begin
if(dflags[5])
$display("I8251A (%h) at %d: starting external sync receiver",
instance_id, $time);
if(dflags[5] && command[7])
$display("I8251A (%h) at %d: hunting for syncs",
instance_id, $time);
-> external_syndet_watche;// whenever syndet pin goes to 1
// set syndet status bit
if (command[7]==1) // if enter hunt mode
begin :external_syn_hunt_blk
fork
syn_receive_external;// assemble chars while waiting
@(posedge syndet) // after rising edge of syndet
@(negedge syndet) // wait for falling edge
// before starting char assembly
disable external_syn_hunt_blk;
join
end
syn_receive_external; // start external sync mode receiver
end
default: // if async mode ...
begin
if(dflags[5])
$display("I8251A (%h) at %d: starting asynchronous receiver",
instance_id, $time);
-> break_detect_e; // start check for rcd=0 too long
asyn_receive; // and start async mode receiver
end
endcase
end
/**** EXTERNAL SYNCHRONOUS MODE RECEIVE ****/
task syn_receive_external;
forever
begin
repeat(databits) // Whether in hunt mode or not, assemble a character
begin
@(posedge rxc_)
receivebuf={rcd, receivebuf[7:1]};
end
get_and_check_parity; // reveive and check parity bit, if any
mark_char_received; // set rxrdy line, if enabled
end
endtask
always @external_syndet_watche
@(posedge rxc_)
status[6]=1;
/**** INTERNAL SYNCHRONOUS MODE RECEIVE ****/
/* Hunt for the sync char(s) */
/* (if in synchronous internal sync detect mode) */
/* Syndet is set high when the sync(s) are found */
always @ hunt_sync1_e // search for 1st sync char in the data stream
begin :sync_hunt_blk
while(!(((receivebuf ^ sync1) & syncmask)===8'b00000000))
begin
@(posedge rxc_)
receivebuf={rcd, receivebuf[7:1]};
end
if(modreg[7]==0) // if double sync mode
-> hunt_sync2_e; // check for 2nd sync char directly after 1st
else
-> sync_hunted_e; // if single sync mode, sync hunt is complete
end
always @ hunt_sync2_e // find the second synchronous character
begin :double_sync_hunt_blk
repeat(databits)
begin
@(posedge rxc_)
receivebuf={rcd, receivebuf[7:1]};
end
if(((receivebuf ^ sync2) & syncmask)===8'b00000000)
->sync_hunted_e; // if sync2 followed syn1, sync hunt is complete
else
->hunt_sync1_e; // else hunt for sync1 again
// Note: the data stream [sync1 sync1 sync2] will have sync detected.
// Suppose sync1=11001100:
// then [1100 1100 1100 sync2] will NOT be detected.
// In general: never let a suffix of sync1 also be a prefix of sync1.
end
always @ sync_hunted_e
begin :parity_sync_hunt_blk
get_and_check_parity;
status[6]=1; // set syndet status bit (sync chars detected)
end
task syn_receive_internal;
forever
begin
repeat(databits) // no longer in hunt mode so read entire chars and
begin // then look for syncs (instead of on bit boundaries)
@(posedge rxc_)
receivebuf={rcd, receivebuf[7:1]};
end
case (sync_to_receive)
2: // if looking for 2nd sync char ...
begin
if(((receivebuf ^ sync2)
& syncmask)===0)
begin // ... and 2nd sync char is found
sync_to_receive=1; // then look for 1st sync (or data)
status[6]=1; // and mark sync detected
end
else if(((receivebuf ^ sync1)
& syncmask)===0)
begin // ... and 1st sync char is found
sync_to_receive=2; // then look for 2nd sync char
end
end
1: // but if looking for 1st or data ...
begin
if(((receivebuf ^ sync1) // ... and 1st sync is found
& syncmask)===0)
begin
if(modreg[7]==0) // if double sync mode
sync_to_receive=2; // look for 2nd sync to follow
else // else look for 1st or data
status[6]=1; // and mark sync detected
end
else ; // ... and data was found, do nothing
end
endcase
get_and_check_parity; // reveive and check parity bit, if any
mark_char_received;
end
endtask
task get_and_check_parity;
begin
receivebuf=receivebuf >> (8-databits);
if (modreg[4]==1)
begin
@(posedge rxc_)
if((^receivebuf ^ modreg[5] ^ rcd) != 1)
parity_error;
end
end
endtask
task mark_char_received;
begin
if (command[2]==1) // if receiving is enabled
begin
rxrdy=1;
status[1]=1; // set receive ready status bit
if (rdatain==1) // if previous data was not read
overrun_error; // overrun error
rdata=receivebuf; // latch the data
rdatain=1; // mark data as not having been read
end
if(dflags[2])
$display("I8251A (%h) at %d: received data: %b",
instance_id, $time, receivebuf);
end
endtask
/************** ASYNCHRONOUS MODE RECEIVER **************/
/* Check for break detection (rcd low through 2 */
/* receive sequences) in the asynchronous mode. */
always @ break_detect_e
begin :break_detect_blk
#1 /* to be sure break_delay_blk is waiting on break_deley_e
after it triggered break_detect_e */
if(rcd==0)
begin
-> break_delay_e;//start + databits + parity + stop bit
breakcount_period = 1 + databits + modreg[4] + (tstoptotal != 0);
// the number of rxc periods needed for 2 receive sequences
breakcount_period = 2 * breakcount_period * baudmx;
// If rcd stays low through 2 consecutive
// (start,data,parity,stop) sequences ...
repeat(breakcount_period)
@(posedge rxc_);
status[6]=1; // ... then set break detect (status[6]) high
end
end
always @break_delay_e
begin : break_delay_blk
@(posedge rcd) // but if rcd goes high during that time ...
begin :break_delay_blk
disable break_detect_blk;
status[6]=0; // ... then set the break detect low
@(negedge rcd) // and when rcd goes low again ...
-> break_detect_e; // ... start the break detection again
end
end
/**** ASYNCHRONOUS MODE RECEIVE TASK ****/
task asyn_receive;
forever
@(negedge rcd) // the receive line went to zero, maybe a start bit
begin
rbaudcnt = baudmx / 2;
if(baudmx==1)
rbaudcnt=1;
repeat(rbaudcnt) @(posedge rxc_); // after half a bit ...
if (rcd == 0) // if it is still a start bit
begin
rbaudcnt=baudmx;
repeat(databits) // receive the data bits
begin
repeat(rbaudcnt) @(posedge rxc_);
#1 receivebuf= {rcd, receivebuf[7:1]};
end
repeat(rbaudcnt) @(posedge rxc_);
// shift the data to the low part
receivebuf=receivebuf >> (8-databits);
if (modreg[4]==1) // if parity is enabled
begin
if((^receivebuf ^ modreg[5] ^ rcd) != 1)
parity_error; // check for a parity error
repeat(rbaudcnt) @(posedge rxc_);
end
#1 if (rcd==0) // if middle of stop bit is 0
frame_error; // frame error (should be 1)
mark_char_received;
end
end
endtask
endmodule
|
[ FAQ Main | Part-1 | Part-2 | Part-3 | What' New | Links ] |
沒有留言:
張貼留言