2012年10月11日 星期四















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

沒有留言:

張貼留言

Node-Red Dashboard UI Template + AngularJS 參考 AngularJS教學 --3

  Node-Red Dashboard UI Template + AngularJS 參考 AngularJS教學 --3 AngularJS 實例 <!DOCTYPE html> <html> <head> <meta charse...