Assuming you need a read cycle on each port on each clock cycle, each BRAM will give you two read ports. Beyond that, you have to replicate the contents of the memory.
Is the bandwidth required at each port less than the raw bandwidth of the BRAM? In that case, you might consider multiplexing the ports. Use a counter that runs at the full speed of the BRAM to drive a multiplexer that scans the address bus for each port, feed these addresses to the BRAM, and then deliver the data (typically 2 clocks later) to the corresponding data bus for each port.
The downside of this approach is that the access latency for each port is now N clocks longer than the non-multiplexed case. There are various ways to deal with this latency, including adding additional pipeline stages to the other data paths.
Note that with a 2-port BRAM inside the module, you can scan two of the external ports at a time.
Actually, the structure of your verilog looks just fine. It's the details that are wrong, in a lot of places. Here are some:
reg [3:1] y2, y1, Y2, Y1
Yes, the missing semicolon throws the compiler off. More to the point, [3:1]
tells it that each of these regs is three bits wide, but they're only one bit wide in the planning. Traditionally we use 0 for the least significant bit (such that the binary interpretation has weight 2^n at bit n). The parameter line would thus be [1:0]
, as it is you extend it to three bits wide.
always @(w,y2,y1)
always @(negedge Resetn, posedge Clock)
The sensitivity list is separated by or
, not comma.
case (y2)
A: if (w) Y2 = 0;
Y1 = 1;
else Y2 = 0;
Y1 = 0;
y2
in this case statement only matches one bit in your planning (three in the code, but that made less sense). You can concatenate bits using {y2,y1}
; in fact, extending the case to case ({y2,y1,w})
will let you use case matches like {A,1'b0}:
and remove the if
statements entirely.
Secondly, you are trying to manage groups of statements (both assignments to Y2 and Y1) with if
; doing so requires enclosing them with begin
and end
. Alternatively, you could make a wider assignment such as {Y2,Y1} <= B;
, which ends up more readable as it can use your named states.
Thirdly, assignment using =
can cause some confusion (it acts more like sequential languages, while <=
doesn't modify the meaning of a reg within your always). In this case, it is fine as the block is fully combinatorial and does not depend on its own outputs.
Finally (for the case
section), you can simply add more matches. You don't even need a default
match, but it's probably convenient to use default
to go to state A in this case.
always @(negedge Resetn, posedge Clock)
if (Resetn == 0) //something :/
else //something else :/
Something and something else would be register updates, such as {y2,y1} <= {Y2,Y1};
. It is the clock edge sensitivity that turns the regs into flipflops.
Finally, since you should now understand what defines a reg width, why don't you make two bit wide regs named state
and next_state
to replace {y2,y1}
and {Y2,Y1}
respectively?
Best Answer
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 thedo
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 ifaddr
changes. That is needed becauseaddr
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:
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.