0

I have attemted to write a Minimal, Complete, and Verifiable example below.

I want to write 10 values to the first 10 addresses of the BRAM (single port Block RAM) and then read the values. After inspecting the results I find that

  • there are no changes in the first 10 addresses while perfoming the write operation.
  • While reading, the output changes after 3 clock cycles and stays constant when the 'address' signal stops changing.

Can you give an explanation to this behaviour and how to get the desired result (write 10 values in the 10 addresses). I'm more interested in solving the second problem (reading the values from the first 10 addresses).

Below is my verilog testbench and snapshot of the waveguide.

module BRAM_tb;
    // Inputs
    reg clk;
    reg [3:0] wea;                  // write enable signal
    reg [31:0] addra;               // address
    reg signed [31:0] dina;     // data in

    // Outputs
    wire [31:0] douta;          // data out

    // Instantiate the Unit Under Test (UUT)
    BLOCK_MEM uut (
        .clka(clk), 
        .wea(wea), 
        .addra(addra), 
        .dina(dina), 
        .douta(douta)
    );

    always begin
        #15 clk =~clk;
    end

    task writeStuff;    //write to address
        begin
            addra <= addra + 1;
            dina <= dina+1;
        end
    endtask

    task readStuff; // read the at address
        begin
            addra <= addra + 1;
        end
    endtask

    reg [1:0] writing;
    integer counter;
    initial begin
        // Initialize Inputs
        clk = 0;
        addra = 0;
        dina = 16;
        counter = 0;
        writing = 2'b10; //idle state
        // Wait 100 ns for global reset to finish
        #100;
        wea <= 1;
        writing <=1;
    end

    always @(posedge clk)begin
        case(writing)
            1: if(counter<10) begin
                    writeStuff;
                    counter <=counter+1;
                end else begin
                    writing <=0;    // change state to reading
                    counter <=0;
                    addra <= 0;
                    wea <=0;    // stop writing
                end
            0:  if(counter<10) begin
                    readStuff;
                    counter <=counter+1;
                end else begin // change addra to zero and do nothing
                    addra <= 0;
                    writing <=2'b10; //goto idle state
                end
            2: if(1) begin
                    //do nothing
                end
        endcase
    end

BRAM - The gray line is where the write operation begins. The blue line is where the read operation begins.

BRAM_2

BLOCK_MEM is an IP-CORE that is generated by Xilinx.

1
  • simplify your test bench, for just read write 10 location you don't need have to write that much. Commented Apr 7, 2016 at 5:37

1 Answer 1

2

I'm going to take some guesses as to how you've configured your BRAM (I'm using Vivado 2015.4 and http://www.xilinx.com/support/documentation/ip_documentation/blk_mem_gen/v8_3/pg058-blk-mem-gen.pdf as references). Looks like you've opted for the always-enable (since the ENA signal isn't present), 32-bit data, and the 32-bit address interface. Note that if you use the 32-bit address interface, WEA changes from a 1-bit signal to a 4-bit signal. This is to allow for byte-addressable writes.

Given this, we know that for a wea of 0b0001, only the least-significant byte will be written. Also, from the timing diagram on pg 46 of the above guide, we know we can expect the written data to be available after the write on douta. We can verify this on your first image - for example, at 200ns, douta = 0xfff75c13, with the 0x13 byte having come from dina on the previous clock edge (the other bytes are what was previously in the memory). So, this confirms the writes are working as expected.

As for the reads in image two, if you count again, you'll see that douta changes every 4 clock cycles. Again, remember that the memory is addressed per byte, but you're returning 4 bytes, so the lowest two bits of the address are ignored (address 0x07 == 0x06 == 0x05 == 0x04).

In short, the BRAM is working as intended, which might not be the way you expected. To move to the next 32-bit/4-byte word in memory, you need to increment the address by 4, not by 1. To write the whole word, (not just the lowest byte) set wea='b1111.

Sign up to request clarification or add additional context in comments.

5 Comments

Does that mean when writing i should increment the addra by 4. For example [0, 4, 8, 12, ...] ? And for reading I should do the same [0, 4, 8, 12, ...] ?
Yep, and turn all the byte-enable bits you want for writing.
It's an architecture thing. Most memories are designed to be "byte-addressable" (meaning each byte has its' own address in memory), but use a word-sized interface (like 32-bits/4-bytes), and read/write access can't cross word boundaries. Many bus protocols (such as AXI) handle this by doing conversion - a 1-byte write of 0xab to address 0x01 would be converted into addr = 0x0, wea=0x2, dina=0x0000ab00. Since you're doing direct-access, you don't have the bus there to do the conversion for you.
Just clear all doubts, if I choose the 'write depth' to be 98304,does it mean that I have 98304 blocks of 32bit, or '98304/4'=24576 blocks of 32bit memory. Here is a snapshot
90% certain that it will create a 98304x32-bit memory.

Your Answer

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

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.