Forum Discussion
Quartus joins two RAMs?
Hello, thank you for your reply.
I used MegaWizard.
Code as simple as this:
reg [8:0] r_fir_RAM_address = {9{1'b0}}; wire [15:0] w_fir_RAM_data_out_lo; wire [15:0] w_fir_RAM_data_out_hi; reg [15:0] r_fir_RAM_data_in_lo = {16{1'b0}}; reg [15:0] r_fir_RAM_data_in_hi = {16{1'b0}}; reg r_fir_RAM_we = 1'b0; fir_ram_lo fir_ram_lo ( .clock(FFCLK), // address - same for both channels, even/odd .address_a( { r_fir_RAM_address[8:0], 1'b0 } ),// even byte .address_b( { r_fir_RAM_address[8:0], 1'b1 } ),// odd byte // write enable .wren_a(r_fir_RAM_we), .wren_b(r_fir_RAM_we), // indata .data_a(r_fir_RAM_data_in_lo[7:0]), .data_b(r_fir_RAM_data_in_lo[15:8]), // outdata .q_a(w_fir_RAM_data_out_lo[7:0]), .q_b(w_fir_RAM_data_out_lo[15:8]) ); fir_ram_hi fir_ram_hi ( .clock(FFCLK), // address - same for both channels, even/odd .address_a( { r_fir_RAM_address[8:0], 1'b0 } ),// even byte .address_b( { r_fir_RAM_address[8:0], 1'b1 } ),// odd byte // write enable .wren_a(r_fir_RAM_we), .wren_b(r_fir_RAM_we), // indata .data_a(r_fir_RAM_data_in_hi[7:0]), .data_b(r_fir_RAM_data_in_hi[15:8]), // outdata .q_a(w_fir_RAM_data_out_hi[7:0]), .q_b(w_fir_RAM_data_out_hi[15:8]) );
In reading your code snippet, for the instantiation of your two RAM blocks the **ONLY** difference is in the data input/output signals.
The address/data/control signals are IDENTICAL for each block.
So Quartus can easily (and validly) pack BOTH of your RAM blocks into a single M9K block that has a wide enough data port (of 32bits) and a deep enough depth (256 locations, as you indicate the upper address bit is always zero). 256*32 = 8192 < M9K size so it fits in one memory block.
Darn good optimization by Quartus. Why are you complaining?
- EugenyB5 years ago
Occasional Contributor
Update: after some conversations we decided that it is not clear how to deal with the issue, and I decided to change architecture of the circuit using one single port RAM. It has performance penalty, but satisfies minimal requirements, thus there's no more urgency in and severity of dealing with the issue.
Special thanks to everyone who was involved into the escalation.
>Why are you complaining?
This is a very good question!
1. In my previous designs I have seen clear difference between performance of *8 and *16 M9K RAMs. I can not explain why *16 configuration is not up to speed, most probably it has some more logic involved speeding down the operation causing invalid data output at specific clock frequencies - I use ~112 MHz;
2. There must be an option to tell Quartus not to be that darn good, but perform as it was designed by the human designer. So far we did not find anything which would instruct Quartus to make 2 RAMs and not one RAM which (see my item #1) may not be up to speed causing artefacts in its output thus system failure;
3. The M9K guide for Cyclone 3, at its page 3-11, has a table listing possible configurations for True Dual Port RAM - the one I used in the design. Table 3-4 does not list 256*32 configuration. Thus Quartus must have had two RAMs. In general, the documentation does not show "native" configuration of the M9K blocks which provides maximum performance;
4. And finally, the actions and optimizations Quartus performing are not clear, and thus suspicious, without any traces in its warnings or informational messages. There must have been messages that Quartus merges RAMs, but there're none. Then only related are about Quartus rightfully reporting that MSBs of the addresses are not used.
- ak6dn5 years ago
Regular Contributor
Your code though is using SIMPLE dual port mode, so on page 3-9 table 3-3 applies.
256x32 is a valid configuration, thus Quartus did what it did.
- EugenyB5 years ago
Occasional Contributor
"Quote ak6dn:
> Your code though is using SIMPLE dual port mode, so on page 3-9 table 3-3 applies. 256x32 is a valid configuration, thus Quartus did what it did."This is real discovery for me. I use dual port RAMs in a way both ports can read from and write to same location at the same time. It does not happen often, but it may happen. Now I learn from you that what I do is "Simple dual-port mode supports simultaneous read and write operations to different locations".
Let's see at the megawizard:
There's 2-port RAM, there're no "simple" or "true" in the list. I suspect something about RAM being "true" must be selected within 2-port RAM creation menu? Here're some more screenshots:
This screenshot assumes that what I create using wizard actually is able to read location being written to by another port. So what I make here can not be a simple RAM because simple RAM, per its definition in the document above is not able to read/write same location through different ports.
Next one confirms this:
This means that I can read location I am writing to, but does not explicitly say about what port to be used.
Let's compare the pictures in the document:
There're clear differences between simple and true dual ports; Looking at my previous replies to this thread I see there're signals defined corresponding to the true dual-port RAM, there're no "rdaddress" or "rden" signals... Do I miss anything?
Therefore, let's return to basics:
- do I understand properly that "simple" dual-port RAM does not support writing from port A and reading from port B at the same time? There's clear discrepancy in documentation and in what I see in Megawizard;
- Are you sure my RAM is simple dual port? How can I insert "True" dual port RAM into the project?