0
\$\begingroup\$

Following shows a single-port RAM in write-first mode.

module raminfr (clk, we, en, addr, di, do);
        input        clk;
        input        we;
        input        en;
        input  [4:0] addr;
        input  [3:0] di;
        output [3:0] do;
        reg    [3:0] RAM [31:0];
        reg    [4:0] read_addr;
        always @(posedge clk)
        begin
           if (en) begin
              if (we) 
             RAM[addr] <= di;
                  read_addr <= addr;
           end
        end
           assign do = RAM[read_addr];
    endmodule

Here, how can we assure that output do is changed after RAM[addr] <= di; is executed?

If the logic does write-first mode, data has to be written first and then read has to happen. But, as we know, RAM[addr] <= di; and read_addr <= addr; are executed concurrently. However, it seems that we cannot guarantee that output do is changed after writing.

Why this code works?

Following are my exepctation:

assumption: addr[10] = 5, addr[20] = 15;

previous addr: 10, current addr:10, en: 1, we: 0 => do = 5

previous addr: 10, current addr:20, en: 1, we: 1, di: 12 => do will be changed because of RAM[read_addr] will be changed as 15. But, how can we assure that do will be 12 instead of 15 (write-first mode)?

Also, is the reg read_addr necessary? If it is, why?

\$\endgroup\$
2
\$\begingroup\$

Here, how can we assure that output do is changed after RAM[addr] <= di; is executed?

Because assign do = RAM[read_addr]; will be executed when any of its signals change. Thus it will be called when either read_addr changes or if RAM changes. This makes that the do output follows both the address but also reflect any writes which happen.

The reg read_addr makes sure that the data of the last read stays on the output if addr changes. That is needed because addr must be ignored if there is no enable and if there is no clock.

The whole code builds a memory with a certain behavior. It may just not be the behavior you where expecting.


I hope the code formatting is not the original one. It is very sloppy. Also nowadays you should use modern port definitions:

module raminfr (
   input clk, // Memory synchronous clock
   input we,  // Write enable
   input en,  // Enable for both read and write
   ....

Adding the comment makes clear what each signal does. Thus it tells the user that the en is required for read and write. Otherwise she/he has to go working through the code to find out.

Final remark: just because you find some code which works don't assume it is the best there is.

\$\endgroup\$
  • \$\begingroup\$ Thks! If assign do = RAM[read_addr]; is fired whenever read_addr and RAM[read_addr]; is changed, the output do could show two results. First of all, if read_addr is changed, then do = RAM[read_addr]; can trigger data written in RAM[read_addr] before writing happen. And, right after writing is done, then written data will be triggered to output do. Isn't it? \$\endgroup\$ – sungjun cho May 1 '18 at 9:47
  • \$\begingroup\$ Signal changes will always 'ripple' trough a design. But that is all done in delta times. (In real life in gate delay times) In simulation with a synchronous design all the signals will be stable far before the next clock edge. \$\endgroup\$ – Oldfart May 1 '18 at 10:01

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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