Wednesday, July 15, 2015

Pullups in Verilog

So I have written in VHDL a long time ago and read verilog a few years ago. But I don't know the ins and outs of verilog programming. Today, I went to check my waveforms on the flash programmer part to ensure that the whole bus and buscontrol goes Hi-Z when RESET is asserted. And I saw that the data bus was actually going from Hi-Z to unknown while RESET was asserted. Odd, I thought.

I quickly decided that the most likely scenario was that my memory simulator was mishandling the Hi-Z RD signal. And indeed. I routed the RD signal straight from the CPLD simulation to the memory simulator. So the memory simulator was getting Z as an input. Well, in verilog, if you have OR/NOR gates and everything that's not L is either Z or X, the output is X. Similarly, with AND/NAND gates, if everything that's not H is Z or X, the output is X. So, my bus enabler was getting confused.

The hardware solution is pullup resistors between the output and Vcc (or pull downs to ground as the case may be). But verilog has no resistors that I know of. What to do?

I added three new 'wire' declarations.

wire nCS_pu;
wire nRD_pu;
wire nWR_pu;

Then took the Hi-Z-able pins pin_nCS, pin_nRD, and pin_nWR and ran them through these:

assign nCS_pu = (pin_nCS === 1'bz) ? 1'b1 : pin_nCS;
assign nRD_pu = (pin_nRD === 1'bz) ? 1'b1 : pin_nRD;
assign nWR_pu = (pin_nWR === 1'bz) ? 1'b1 : pin_nWR;

And voila! Pull-ups. The === is needed to do a comparison to Z or X. With just ==, verilog rightly cannot determine the output and just assigns X if the the output is not 100% clear from the inputs. In other words a==b in verilog where a and b are both 1'bz is neither true nor false because the interpreter says Z can be either H or L--not sure which. So a==b might me H==L or H==H or L==H or L==L. But with ===, if both a and b are 1'bz, the result is true (false for !==) and if a and b are both 1'bx, then the results is true (again, false for !==). I think that 1'bz !== 1'bx. Obviously this behavior can't be synthesized, but it is useful for testing.

I connected my memory to these and everything worked as expected.

Here is the testbench code with the relevant parts in bold:

// TOOL:     vlog2tf
// DATE:     07/11/15  10:39:08 
// TITLE:    Lattice Semiconductor Corporation
// MODULE:   flashProgrammer
// DESIGN:   flashProgrammer
// FILENAME: flashProgrammer.tft
// PROJECT:  flashprog
// VERSION:  1.0
// This file is auto generated by the ispLEVER


`timescale 1 ns / 1 ns

// Define Module for Test Fixture
module flashProgrammer_tf();

// Inputs
    reg pin_ENABLE;
    reg pin_CLK;
    reg pin_Din;
    reg pin_nCTRL;


// Outputs
    wire pin_Dout;
    wire [17:0] pins_A;
    wire pin_nWR;
    wire pin_nRD;
    wire pin_nCS;


// Bidirs
    wire [7:0] pins_D;
 
// pull-up lines
    wire nWR_pu;
    wire nRD_pu;
    wire nCS_pu;


// Instantiate the UUT
    flashProgrammer UUT (
        .pin_ENABLE(pin_ENABLE), 
        .pin_CLK(pin_CLK), 
        .pin_Din(pin_Din), 
        .pin_nCTRL(pin_nCTRL), 
        .pin_Dout(pin_Dout), 
        .pins_A(pins_A), 
        .pin_nWR(pin_nWR), 
        .pin_nRD(pin_nRD), 
        .pin_nCS(pin_nCS), 
        .pins_D(pins_D)
        );

    flash simFlash (
        .A(pins_A),
        .D(pins_D),
        .nWR(nWR_pu),
        .nRD(nRD_pu),
        .nCS(nCS_pu)
        );

    reg [2:0] ctrlToSend;
    reg [17:0] aToSend;
    reg [7:0] dToSend;
    reg [3:0] cswrrdToSend;
    reg [7:0] dRcvd;
    reg [7:0] dShift;


    // Pullups
    assign nCS_pu = (pin_nCS===1'bz) ? 1'b1 : pin_nCS;
    assign nRD_pu = (pin_nRD===1'bz) ? 1'b1 : pin_nRD;
    assign nWR_pu = (pin_nWR===1'bz) ? 1'b1 : pin_nWR;

// Initialize Inputs
// You can add your stimulus here
    initial begin

        resetSequence();

        write(17'h12345, 8'haa);

        simFlash.testRdD = 8'hC3;
        $display("simFlash.testRdD = %h", simFlash.testRdD);

        read(17'h0abcd, dRcvd);

        resetSequence();

        write(17'h14567, 8'h55);
        write(17'h14568, 8'h77);

        simFlash.testRdD = 8'h3C;
        $display("simFlash.testRdD = %h", simFlash.testRdD);

        read(17'h19876, dRcvd);
        simFlash.testRdD = 8'h55;
        read(17'h19877, dRcvd);

        #10

        $finish;
    end

    task resetSequence;
        begin
            aToSend = 0;
            dToSend = 0;

            pin_ENABLE = 1;
            pin_CLK = 0;
            pin_Din = 0;
            pin_nCTRL = 1;

            pulseClk();

            #5 
            pin_nCTRL = 0; // negedge resets outputs:w

        
            pulseClk(); // CLK pulse resets flash_control
            pulseClk(); // CLK pulse resets flash_control
            pulseClk(); // CLK pulse resets flash_control

            #5 
            pin_nCTRL = 1; // stobes chip control out

            #5 
            pin_ENABLE = 0;

        end
    endtask

    task write;
        input [17:0] A;
        input [7:0] D;
        begin
            $display("Writing %h to %h.", D, A);

            setFullAddress(A);

            setCtrl(3'b000);
            sendData(D);
            setCtrl(3'b111);
            setDriveCsWrRd(0, 0, 1, 1); // drive, CS
            pulseCtrl();
            setDriveCsWrRd(0, 0, 0, 1); // drive CS WR
            pulseCtrl();
            setDriveCsWrRd(0, 0, 1, 1); // drive CS
            pulseCtrl(); 
            setDriveCsWrRd(1, 1, 1, 1); // done
            pulseCtrl();
            $display("SimFlash saw %h<-%h", simFlash.testWrA, simFlash.testWrD);

            if (A != simFlash.testWrA) begin
                $display("ERROR!");
                $finish;
            end
            if (D != simFlash.testWrD) begin
                $display("ERROR!");
                $finish;
            end
        end
    endtask

    task read;
        input [17:0] A;
        output [7:0] D;
        begin
            $display("Reading %h.", A);

            setFullAddress(A);

            setCtrl(3'b111);
            setDriveCsWrRd(0, 0, 1, 0); // drive CS RD
            pulseCtrl();
            setDriveCsWrRd(1, 0, 1, 0); // CS RD
            pulseCtrl();
            setDriveCsWrRd(1, 1, 1, 1); // done
            pulseCtrl();
            setCtrl(3'b000);
            recvData();
            D = dShift;
            dShift = 8'bxxxxxxxx;
            $display("Read %h", D);
            $display("SimFlash saw %h", simFlash.testRdA);

            if (D != simFlash.testRdD) begin
                $display("ERROR!");
                $finish;
            end
        end
    endtask

    task setFullAddress;
        input [17:0] A;
        begin
            if (A[3:0] != aToSend[3:0]) begin
                //$display("Writing A[3:0]");
                setCtrl(3'b001);
                setAddr3_0(A[3:0]);
            end
            if (A[7:4] != aToSend[7:4]) begin
                //$display("Writing A[7:4]");
                setCtrl(3'b010);
                setAddr7_4(A[7:4]);
            end
            if (A[17:8] != aToSend[17:8]) begin
                //$display("Writing A[17:8]");
                setCtrl(3'b011);
                setAddr17_8(A[17:8]);
            end
        end
    endtask

    task setCtrl;
        input [2:0] regNo;
        begin
            ctrlToSend = regNo;
            #5
            pin_nCTRL = 0;
            pin_Din = ctrlToSend[0];
            pulseClk();
            pin_Din = ctrlToSend[1];
            pulseClk();
            pin_Din = ctrlToSend[2];
            pulseClk();
            #5
            pin_nCTRL = 1;
        end
    endtask
    
    task pulseCtrl;
        begin
            #5
            pin_nCTRL = 0;
            #5
            pin_nCTRL = 1;
        end
    endtask
   
    task pulseClk;
        begin
            #2
            pin_CLK = 0;
            #5
            pin_CLK = 1;
            #2
            pin_CLK = 0;
        end
    endtask
   
    task setAddr3_0;
        input [3:0] addrPart;
        begin
            aToSend[3:0] = addrPart;
            pin_Din = aToSend[0];
            pulseClk();
            pin_Din = aToSend[1];
            pulseClk();
            pin_Din = aToSend[2];
            pulseClk();
            pin_Din = aToSend[3];
            pulseClk();
        end
    endtask

    task setAddr7_4;
        input [3:0] addrPart;
        begin
            aToSend[7:4] = addrPart;
            pin_Din = aToSend[4];
            pulseClk();
            pin_Din = aToSend[5];
            pulseClk();
            pin_Din = aToSend[6];
            pulseClk();
            pin_Din = aToSend[7];
            pulseClk();
        end
    endtask

    task setAddr17_8;
        input [9:0] addrPart;
        begin
            aToSend[17:8] = addrPart;
            pin_Din = aToSend[8];
            pulseClk();
            pin_Din = aToSend[9];
            pulseClk();
            pin_Din = aToSend[10];
            pulseClk();
            pin_Din = aToSend[11];
            pulseClk();
            pin_Din = aToSend[12];
            pulseClk();
            pin_Din = aToSend[13];
            pulseClk();
            pin_Din = aToSend[14];
            pulseClk();
            pin_Din = aToSend[15];
            pulseClk();
            pin_Din = aToSend[16];
            pulseClk();
            pin_Din = aToSend[17];
            pulseClk();
        end
    endtask

    task setDriveCsWrRd;
        input rqDrive;
        input rqCs;
        input rqWr;
        input rqRd;
        begin
            cswrrdToSend = { rqDrive, rqCs, rqWr, rqRd };
            pin_Din = cswrrdToSend[0];
            pulseClk();
            pin_Din = cswrrdToSend[1];
            pulseClk();
            pin_Din = cswrrdToSend[2];
            pulseClk();
            pin_Din = cswrrdToSend[3];
            pulseClk();
        end
    endtask

    task sendData;
        input [7:0] data;
        begin
            dToSend = data;
            pin_Din = dToSend[0];
            pulseClk();
            pin_Din = dToSend[1];
            pulseClk();
            pin_Din = dToSend[2];
            pulseClk();
            pin_Din = dToSend[3];
            pulseClk();
            pin_Din = dToSend[4];
            pulseClk();
            pin_Din = dToSend[5];
            pulseClk();
            pin_Din = dToSend[6];
            pulseClk();
            pin_Din = dToSend[7];
            pulseClk();
        end
    endtask

    task recvData;
        begin
            pulseClk();
            dShift[0] = pin_Dout;
            pulseClk();
            dShift[1] = pin_Dout;
            pulseClk();
            dShift[2] = pin_Dout;
            pulseClk();
            dShift[3] = pin_Dout;
            pulseClk();
            dShift[4] = pin_Dout;
            pulseClk();
            dShift[5] = pin_Dout;
            pulseClk();
            dShift[6] = pin_Dout;
            pulseClk();
            dShift[7] = pin_Dout;
        end
    endtask

endmodule // flashProgrammer_tf

module flash(
    input [78:0] A,
    inout [7:0] D,

    input nWR,
    input nRD,
    input nCS,
);

    reg [7:0] testWrD;
    reg [17:0] testWrA;
    reg [7:0] testRdD;
    reg [17:0] testRdA;

    assign D = ((nRD | nCS) == 1'b0) ? testRdD : 8'bzzzzzzzz;

    always @(posedge nRD) begin
        if (nCS == 1'b0) begin
            testRdA <= A;
        end

    end
    always @(posedge nWR) begin
        if (nCS == 1'b0) begin
            testWrA <= A;
            testWrD <= D;
        end
    end

endmodule

No comments:

Post a Comment