Search code examples
memoryverilogsystemdigitalicarus

Why the memory content is not read? - verilog digital system design


I created a microsystem which is composed of two clocked SRAMs, one designed for storing instruction-codes, and another to store some output values. The instruction SRAM has an interface module, named "user" which provides a mechanism to ease the writing process, consequently, when writing data in the memory, there is no need to specify the corresponding memory address at which those instructions have to be stored. Similarly, when reading data from the second SRAM, there is no need to specify the corresponding memory address from which data is extracted, thanks to "display" module. To be more specific, when writing data in the instructions memory, a counter increments the address pointer after each writing, while the second memory has another counter which increments the address pointer value after each reading. When one tries to read information from the instruction memory, they have to specify the memory address from which data should be read, and, as expected, when one tries to write information in the output memory, they have to specify the memory address in which data should be written. Furthermore, the microsystem has an automaton which takes input data from the instruction memory and processes it. After processing information, the automaton stores some output values in the output memory. I come across an issue when simulating, because, apparently, the read values from the SRAM memory cannot be seen, namely, rd_data_instrucions, thus, neither the input values "in" for the automaton cannot be found, nor the output values, as long as they depend on the data read from the first SRAM. I posted the code below and a diagram.

rd_data_instructions: after the 001 value there should be a 000, but, instead, it is undefined value, as rd_data_instrucions should resemble the wr_data_instructions, after the moment when wr_data_instrucions == 100, 100 instruction having the meaning of halt, so the automaton starts reading instructions stored in the memory since then

//wishbone module
module wishbone(
    input clk,rst,
    output reg [2:0]in,
    output reg wr_en_instructions,wr_en_display,
    input [2:0] wr_data_instructions,//created for usr, in order to make possible to write data
    output reg [3:0] wr_data_display,
    output [2:0] rd_data_instructions,
    output [3:0] rd_data_display,//created for user, in order to make possible the display
    output [12:0]o
);

reg [15:0] pointer_instructions,pointer_display;

initial wr_en_instructions = 1'b1;//?

control_unit i0(.clk(clk),.rst(rst),.in(in),.o(o));
user i1(.clk(clk),.wr_en(wr_en_instructions),.address_in(pointer_instructions),.wr_data(wr_data_instructions),.rd_data(rd_data_instructions));
display i2(.clk(clk),.wr_en(wr_en_display),.address_in(pointer_display),.wr_data(wr_data_display),.rd_data(rd_data_display));
integer i = 0;
always @ * begin
    wr_en_display = ~wr_en_instructions;
end
always @(posedge clk) begin
    if(rst) begin
        wr_en_instructions <= 1'b1;
        pointer_instructions <= 16'd0;
        pointer_display <= 16'd0;
    end
    else begin
        if(wr_en_instructions) begin
            if(wr_data_instructions[2] == 1'b1) begin
                pointer_instructions <= 16'd0;
                pointer_display <= 16'd0;
                wr_en_instructions <= 1'b0;
            end
        end
        else begin
            in <= rd_data_instructions;
            pointer_instructions <= pointer_instructions + 1;
            if(rd_data_instructions == 3'b010) begin
                wr_data_display <= o;
                pointer_display <= pointer_display + 1;
            end
            else if(rd_data_instructions == 3'b100) begin
               wr_en_instructions <= 1'b1;
            end
        end
    end
end

endmodule

//testbench for top module
module wishbone_tb(
    output reg clk,rst,
    output [2:0]in,
    output wr_en_instructions,wr_en_display,
    output reg [2:0] wr_data_instructions,//created for usr, in order to make possible to write data
    output [3:0] wr_data_display,
    output [2:0] rd_data_instructions,
    output [3:0] rd_data_display,//created for user, in order to make possible the display
    output [12:0]o
);

wishbone cut(
    .clk(clk),.rst(rst),
    .in(in),
    .wr_en_instructions(wr_en_instructions),.wr_en_display(wr_en_display),
    .wr_data_instructions(wr_data_instructions),
    .wr_data_display(wr_data_display),
    .rd_data_instructions(rd_data_instructions),
    .rd_data_display(rd_data_display),
    .o(o)
);

initial $dumpvars(0,wishbone_tb);

initial begin
    clk = 1'b1;
    repeat (600000)
    #100 clk = ~clk;
end

initial begin
    rst = 1'b1;
    #400 rst = 1'b0;
end

initial begin
    wr_data_instructions = 3'd1;
    #3000400 wr_data_instructions = 3'd2;
    #1000000 wr_data_instructions = 3'd1;
    #3000000 wr_data_instructions = 3'd0;
    #2000000 wr_data_instructions = 3'd3;
    #1000000 wr_data_instructions = 3'd1;
    #3000000 wr_data_instructions = 3'd4;//halt
end

endmodule

//code for the first memory:

//stores instructions
module sram_1port_instructions(
    input clk,//clocked memory
    input wr_en,//when high, data is writeen, otherwise is read
    input [15:0] address_in,//suppose timer cannot count more than 13ms
    input [2:0] wr_data,//3 bit instructions
    output reg [2:0] rd_data
);

reg [2:0] memory [2 ** 15 - 1 : 0];

always @(posedge clk) begin
    if(wr_en) memory[address_in] <= wr_data;
    else rd_data <= memory[address_in];
end

endmodule

//user interface designed for the first memory
module user(
    input clk,
    input wr_en,
    input [15:0] address_in,
    input [2:0] wr_data,
    output [2:0] rd_data
);

reg [15:0] pointer,address;

initial pointer = 16'd0;

sram_1port_instructions i0(.clk(clk),.wr_en(wr_en),.address_in(address),.wr_data(wr_data),.rd_data(rd_data));

always @(posedge clk) begin
    if(wr_en) begin
        address <= pointer;
        pointer <= pointer + 1;
    end 
    else begin
        address <= address_in;
        pointer <= 16'd0;
    end
end

endmodule

//user tb
module user_tb(
    output reg clk, wr_en, 
    output reg [15:0] address_in,
    output reg [2:0] wr_data,
    output [2:0] rd_data
);

user cut(.clk(clk),.wr_en(wr_en),.address_in(address_in),.wr_data(wr_data),.rd_data(rd_data));

initial $dumpvars(0,user_tb);

initial begin
    clk = 1'd1;
    repeat (2000)
    #100 clk = ~clk;
end

initial begin
    wr_en = 1'd1;
    #100000 wr_en = 1'd0;
end

integer i;

initial begin
    wr_data = 3'd0;
    for(i = 1;i < 500;i = i + 1) begin
    #200 wr_data = i;
    end
end

initial begin
    address_in = 16'd0;
    #100000 address_in = 16'd0;
    for(i = 1;i < 500;i = i + 1) begin
    #200 address_in = i;
    end
end

endmodule

//code for the second memory:

//stores output data
module sram_1port_data(
    input clk,//clocked memory
    input wr_en,//when high, data is written, otherwise is read
    input [15:0] address_in,//suppose timer cannout count more than 13ms
    input [3:0] wr_data,//memory does not sotre values greater than 13(ms)
    output reg [3:0] rd_data
);

reg [3:0] memory [2 ** 15 - 1 : 0];

always @(posedge clk) begin
    if(wr_en) memory[address_in] <= wr_data;
    else rd_data <= memory[address_in];
end

endmodule

//display interfacedesigned for the second memory
module display(
    input clk,
    input wr_en,
    input [15:0] address_in,
    input [3:0] wr_data,
    output [3:0] rd_data
);

reg [15:0] pointer,address;

initial pointer = 16'd0;

sram_1port_data i0(.clk(clk),.wr_en(wr_en),.address_in(address),.wr_data(wr_data),.rd_data(rd_data));

always @(posedge clk) begin
    if(!wr_en) begin
        address <= pointer;
        pointer <= pointer + 1;
    end 
    else begin
        address <= address_in;
        pointer <= 16'd0;
    end
end

endmodule

//tb for display
module display_tb(
    output reg clk,
    output reg wr_en,
    output reg [15:0] address_in,
    output reg [3:0] wr_data,
    output [3:0] rd_data
);

display i0(.clk(clk),.wr_en(wr_en),.address_in(address_in),.wr_data(wr_data),.rd_data(rd_data));

initial $dumpvars(0,display_tb);

initial begin
    clk = 1'd1;
    repeat (2000)
    #100 clk = ~clk;
end

initial begin
    wr_en = 1'd1;
    #100000 wr_en = 1'd0;
end

integer i;

initial begin
    wr_data = 3'd0;
    address_in = 16'd0;
    for(i = 1;i < 500;i = i + 1) begin
    #200;
    wr_data = i;
    address_in = i;
    end
end

endmodule

//code for the control unit:

//control unit
module control_unit(
    input clk,rst,
    input [2:0]in,
    output [12:0]o
);

wire f,g;

automaton i0(.clk(clk),.rst(rst),.in(in),.clr(f),.en(g));
circuit i1(.clk(clk),.clr(f),.en(g),.o(o));

endmodule

//code for automaton:

//atuomaton
module automaton(
    input clk,rst,
    input [2:0]in,
    output reg clr,en
);

localparam S0_ST = 2'b00;
localparam S1_ST = 2'b01;
localparam S2_ST = 2'b10;

reg [1:0] st_reg,st_nxt;

always @ * begin
    case(st_reg)
        S0_ST: if(!in[1]) st_nxt = S0_ST;
               else if(in[0]) st_nxt = S2_ST;
               else st_nxt = S1_ST;
        S1_ST: if(!in[0]) st_nxt = S1_ST;
               else if(in[1]) st_nxt = S2_ST;
               else st_nxt = S0_ST;
        S2_ST: if(in == 2'd1) st_nxt = S0_ST;
               else st_nxt = S2_ST; 
    endcase
end

always @ * begin
    case(st_reg)
        S0_ST: {clr,en} = 2'd1; 
        S1_ST: {clr,en} = 2'd0;
        S2_ST: clr = 1'd1;
    endcase
end

always @(posedge clk) begin
    if(rst) st_reg <= S2_ST;
    else st_reg <= st_nxt;
end

endmodule

//code for circuit:

//circuit
module circuit(
    input clk,clr,en,
    output [12:0] o
);

wire f;

generator i0(.clk(clk),.clr(clr),.en(en),.o(f));
counter i1(.clk(clk),.clr(clr),.en(f),.o(o));

endmodule

//code for counter:

//counter
module counter(
    input clk,clr,en,
    output reg [12:0] o
);

reg [12:0] st_nxt;

always @ (posedge clk) begin
    if(clr) o <= 13'd0;
    else o <= st_nxt;
end

always @ *
    if(en) st_nxt = o + 1;
    else st_nxt = o;

endmodule

//code for generator:

//pulse generator
module generator(
    input clk,clr,en,
    output reg o
);

reg [12:0] st_reg,st_nxt;

always @(posedge clk) begin
    if(clr) st_reg <= 13'd0;
    else st_reg <= st_nxt;
end

always @ * begin
    if(en) begin
        if(st_reg == 13'd4999) begin
            st_nxt = 13'd0;
            o = 1'b1;
        end
        else begin
            st_nxt = st_reg + 1;
            o = 1'b0;
        end
    end
    else begin
        if(st_reg == 13'd4999) begin
            st_nxt = 13'd0;
            o = 1'b0;
        end
        else begin
            st_nxt = st_reg;
            o = 1'b0;
        end
    end
end

endmodule

Solution

  • When you read from address 0x8001, that is beyond the limit of your memory size. You could increase your memory. Change:

    reg [2:0] memory [2 ** 15 - 1 : 0];
    

    to:

    reg [2:0] memory [2 ** 16 - 1 : 0];
    

    This gets rid of the unknown (X) when you read from address 0x8001.

    Or, if you don't want to enlarge the memory, make sure you limit your addresses.