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 ] Copyright Rajesh Bawankule 1997 - 2005 |