1
\$\begingroup\$

In my specific situation, I am trying to perform a status register read. It consists of a single 8-bit opcode sent on the SI line, and then response data sent back on the SO line until CS is set back to high.

On my system, there is an array of LEDs which I am using to display the data in the status register.

In my testbench, I am manually setting the SDO line so that there is some random data there - this is to mimic the behaviour of the status register.

The testbench inspecting the flash module seems to be correct:

enter image description here

The testbench at top level UUT seems to be correct in all ways except that no data is being output to the LEDs:

enter image description here

Why are the leds not being set in the top level testbench?

The code generating these testbenches is here:

main.v is the top level system file:

module main #(
    parameter                   COUNT_WIDTH = 3,
    parameter [COUNT_WIDTH-1:0] MAX_COUNT = 6000000 - 1
)(
    input                   clk,
    output  reg     [7:0]   led,
    output  reg     [3:0]   col,
    output  wire            cs,
    output  wire            sck,
    output  wire            sdi,
    input                   sdo
);

    // SPI wiring for flash_read module
    wire flash_done;
    reg start_flash;
    wire [7:0] flash_data_out;
    
    // Flash read module instantiation
    flash_status flash_stat (
        .clk(clk),               // 12MHz System clock
        .start(start_flash),     // Start signal to begin reading
        .data_out(flash_data_out), // Data read from flash
        .done(flash_done),       // Done signal to indicate completion
        .CS(cs),                 // Chip Select (active low)
        .SCK(sck),               // SPI Clock (Mode 0)
        .SI(sdi),                // SPI MOSI (Master Output Slave Input)
        .SO(sdo)                 // SPI MISO (Master Input Slave Output)
    );

    // LEDs wiring
    wire [7:0] led_wire;
    assign led_wire = led;
    wire [3:0] col_wire;
    assign col_wire = col;

    // LED state
    reg [7:0] row_0 = 8'b0;
    reg [7:0] row_1 = 8'b0;
    reg [7:0] row_2 = 8'b0;
    reg [7:0] row_3 = 8'b0;

    // Instantiate the LED scan module
    LedScan scan (
        .clk(clk), 
        .leds0(flash_data_out),     
        .leds1(row_1),
        .leds2(row_2),
        .leds3(row_3),
        .leds(led_wire), 
        .lcol(col_wire)
    );

    // 10 clock cycle counter to delay 
    reg [4:0] clock_counter;

    // Initialization
    initial begin
        clock_counter <= 0;
        start_flash <= 0;
    end

    always @(posedge clk) begin
        if (clock_counter < 10) begin
            // Increment clock counter until it reaches 10
            clock_counter <= clock_counter + 1;
        end else if (clock_counter == 10) begin
            // Trigger the flash read once 10 clock cycles have passed
            start_flash <= 1;
            clock_counter <= clock_counter + 1; // Move past the 10 clock mark
        end else if (flash_done && start_flash) begin
            start_flash <= 0;  // Reset the flash read trigger
        end
    end

endmodule

ledscan.v drives the leds:

module LedScan (
    input clk, 
    input [7:0] leds0,      
    input [7:0] leds1,
    input [7:0] leds2,
    input [7:0] leds3,
    output reg [7:0] leds, 
    output reg [3:0] lcol
    );

    /* Counter register */
    reg [11:0] timer = 12'b0;
    
    always @ (posedge clk) begin
        case (timer[11:10])
            2'b00: begin
                leds[7:0] <= ~leds0[7:0];
                lcol[3:0] <= 4'b1110;
            end     
            2'b01: begin
                leds[7:0] <= ~leds1[7:0];
                lcol[3:0] <= 4'b1101;
            end     
            2'b10: begin
                leds[7:0] <= ~leds2[7:0];
                lcol[3:0] <= 4'b1011;
            end     
            2'b11: begin
                leds[7:0] <= ~leds3[7:0];
                lcol[3:0] <= 4'b0111;
            end     
        endcase
    end

    // increment the scan timer
    always @ (posedge clk) begin
        timer <= timer + 1;
    end

endmodule

status.v drives the flash device:

module flash_status (
    input wire clk,            // System clock
    input wire start,          // Start signal to begin the read
    output reg [7:0] data_out, // Data read from flash
    output reg done,           // Done signal to indicate completion
    output reg CS,             // Chip Select (Active low)
    output reg SCK,            // SPI Clock (Mode 0)
    output reg SI,             // SPI MOSI (Master Output Slave Input)
    input wire SO              // SPI MISO (Master Input Slave Output)
);

    // State definitions
    reg [3:0]  state;
    reg [7:0]  bit_count_send;
    reg [7:0]  bit_count_read;
    reg [7:0]  shift_reg;

    integer delay_counter;

    localparam IDLE        = 4'b0000;
    localparam SEND_OPCODE = 4'b0001;
    localparam READ_DATA   = 4'b0011;
    localparam DONE        = 4'b0100;

    reg [7:0] OPCODE = 8'h05; // Opcode for status read

    // Initialization on startup
    initial begin
        state <= IDLE;
        CS <= 1;        // CS inactive (high)
        SI <= 1;
        done <= 0;
        delay_counter <= 0;
        bit_count_send <= 8;
        bit_count_read <= 8;
        data_out = 8'b0; // Initialize data_out to zero to avoid undefined values
    end

    always @ (*) begin
        SCK <= clk;
    end

    always @(negedge clk) begin
        case (state)
            IDLE: begin
                done <= 0;
                CS <= 1;    // CS inactive
                if (start) begin
                    shift_reg <= {OPCODE}; // Load opcode into shift register
                    bit_count_send <= 7;
                    state <= SEND_OPCODE;
                end
            end

            SEND_OPCODE: begin
                CS <= 0; // Assert CS (active low)
                SI <= shift_reg[bit_count_send]; // Send MSB first
                bit_count_send <= bit_count_send - 1;
                if (bit_count_send == 0) begin
                    state <= READ_DATA;
                end
            end

            READ_DATA: begin
                data_out[bit_count_read] <= SO; // Sample MISO data
                bit_count_read <= bit_count_read - 1;
                if (bit_count_read == 0) begin
                    CS <= 1;
                    state <= DONE;
                end
            end

            DONE: begin
                done <= 1;
                if (!start) begin
                    state <= IDLE; // Go back to IDLE if start is deasserted
                end
            end

            default: state <= IDLE;
        endcase
    end
endmodule

output_tb.v is the testbench generator:

`timescale 1 ns / 1 ps

module output_tb();

    wire sdi, sdo, cs, sck;
    wire [7:0] led;
    wire [3:0] col;

    // Clock and flash response data simulation
    reg clk = 0;
    reg sdo_reg = 0;  // Simulated response data from the flash device

    // Duration of the test
    localparam DURATION = 10000;

    always begin
        #41.67
        clk = ~clk;
    end

    // Instantiate the main module
    main #(
        .COUNT_WIDTH(4),
        .MAX_COUNT(6)
    ) uut_0 (
        .clk(clk),
        .led(led),
        .col(col),
        .cs(cs),
        .sck(sck),
        .sdi(sdi),
        .sdo(sdo_reg)  // Connect simulated sdo (flash output)
    );

    // Simulation of the flash memory's output (sdo)
    initial begin
        sdo_reg = 8'hFF; 
        #1000;           // Wait for some time before changing the data
        sdo_reg = 8'hFF; // Change to another dummy data (0b01010101)
        #100;           // Wait again
        sdo_reg = 8'hFF; // Final dummy data (0b11111111)
        #100;
        sdo_reg = 8'hFF; // Change to another dummy data (0b01010101)
        #100;
        sdo_reg = 8'hFF;
        #100;
        sdo_reg = 8'hFF;
        #100;
        sdo_reg = 8'hFF;
        #100;
        sdo_reg = 8'hFF;
        #30;
        sdo_reg = 8'h00; // Final dummy data (0b00000000)
        #30;
        sdo_reg = 8'hFF;
        #30;
        sdo_reg = 8'h00;
        #30;
        sdo_reg = 8'hFF;
        #30;
        sdo_reg = 8'h00;
        #30;
        sdo_reg = 8'hFF;
        #30;
        sdo_reg = 8'h00;
        #30;
        sdo_reg = 8'hFF;
        #30;
        sdo_reg = 8'h00;
        #30;
        sdo_reg = 8'hFF;
        #30;
        sdo_reg = 8'h00;
        #30;
        sdo_reg = 8'hFF;
        #30;
        sdo_reg = 8'h00;
        #30;
        sdo_reg = 8'hFF;
        #30;
        sdo_reg = 8'h00;
        #30;
        sdo_reg = 8'hFF;
        #30;
        sdo_reg = 8'h00;
        #30;
        sdo_reg = 8'hFF;
        #30;
        sdo_reg = 8'h00;
        #30;
        sdo_reg = 8'hFF;
        #30;
        sdo_reg = 8'h00;
        #30;
        sdo_reg = 8'hFF;
        #30;
        sdo_reg = 8'h00;
        #30;
        sdo_reg = 8'hFF;
        #30;
    end

    initial begin
        $dumpfile("output_tb.vcd");
        $dumpvars(0, output_tb);
        #(DURATION);   // Run the simulation for DURATION nanoseconds
        $finish;       // End the simulation
    end

endmodule
\$\endgroup\$

1 Answer 1

1
\$\begingroup\$

In the main module, you declared the led output signal, but you did not drive the signal. In other words, led is not connected to a driver. This is why led is not being set to a known value (it has the unknown value x).

I think you simply need to connect it to the LedScan instance:

LedScan scan (
    .clk(clk), 
    .leds0(flash_data_out),     
    .leds1(row_1),
    .leds2(row_2),
    .leds3(row_3),
    .leds(led), 
    .lcol(col_wire)
);

When I make the connection to the leds port, the led signal becomes known (the x's are gone). Also, I don't think you need the led_wire wire.

I think the same is true for col and col_wire.

\$\endgroup\$
4
  • 1
    \$\begingroup\$ @K_T: You're welcome. If your code works now in simulation and you are interested in getting feedback on it, you could post it on CodeReview. Make sure to read the policy first. There may be some room for improvment. \$\endgroup\$
    – toolic
    Commented Oct 7 at 22:36
  • \$\begingroup\$ I will look into this! Thanks for the heads-up \$\endgroup\$
    – K_T
    Commented Oct 7 at 23:11
  • 1
    \$\begingroup\$ @K_T: I just saw the update to your other question. It looks like you are not the original author of the Verilog design code, which means you would not post that for review. But, if you did write the testbench code, then that would be reviewable. \$\endgroup\$
    – toolic
    Commented Oct 7 at 23:47
  • \$\begingroup\$ Hey yes true, I am not the author of the ledscan.v file, that was 'Devantech Ltd <[email protected]>' - the rest of the code is actually mine :) \$\endgroup\$
    – K_T
    Commented Oct 8 at 12:15

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.