Three codings of the same 64-word RAM. Only the first has the synchronous-read, no-array-reset structure expected by a conventional synchronous block RAM. Actual mapping depends on the target resources and synthesis flow.
// Good: a plain synchronous single-port RAM
module ram_good #(parameter int AW = 6, DW = 8) (input logic clk, we, input logic [AW-1:0] addr, input logic [DW-1:0] din, output logic [DW-1:0] dout);
logic [DW-1:0] mem [2**AW];
always_ff @(posedge clk) begin
if (we) mem[addr] <= din;
dout <= mem[addr];
end
endmodule
// Bad: asynchronous (combinational) read
module ram_async_read #(parameter int AW = 6, DW = 8) (input logic clk, we, input logic [AW-1:0] addr, input logic [DW-1:0] din, output logic [DW-1:0] dout);
logic [DW-1:0] mem [2**AW];
always_ff @(posedge clk) if (we) mem[addr] <= din;
assign dout = mem[addr];
endmodule
// Bad: reset applied to every word of the array
module ram_reset #(parameter int AW = 6, DW = 8) (input logic clk, rst_n, we, input logic [AW-1:0] addr, input logic [DW-1:0] din, output logic [DW-1:0] dout);
logic [DW-1:0] mem [2**AW];
always_ff @(posedge clk) begin
if (!rst_n) for (int i = 0; i < 2**AW; i++) mem[i] <= '0;
else begin
if (we) mem[addr] <= din;
dout <= mem[addr];
end
end
endmoduleAfter reading the source with read_verilog -sv and selecting the module, use proc; memory -nomap; stat; dump to inspect generic memory cells and their port parameters. The -nomap option skips memory_map; a plain memory run includes that pass and lowers any remaining generic memories into word-wide registers and address decoding. A generic memory cell is intermediate inference evidence, not proof of a mapped block RAM. The table describes structural expectations, not measured cell counts:
Coding | Read port | Write behavior | Generic-memory inspection | Mapping eligibility |
|---|---|---|---|---|
| Clocked ( | One synchronous write | Memory with clocked read and write ports | Eligible for synchronous block RAM; verify target mapping |
| Not clocked ( | One synchronous write | Memory with asynchronous read port | Not eligible for a synchronous-read block RAM: LUT RAM or flip-flops |
| Output register present but not merged into the read port ( | 64 reset writes plus one normal write before port optimization | Array-wide reset writes; port and mux counts depend on optimization | Conventional block RAM cannot clear every stored word in one cycle |
The ram_reset case is instructive. Its output is functionally registered like ram_good, but dout holds during reset, so Yosys keeps that register as a separate $dff instead of merging it into the read port. The bigger problem is the write side: before port optimization, the loop produces 64 extra reset writes. Optimization may merge ports, but it cannot remove the requirement to clear every stored word in one cycle, which a conventional block RAM cannot implement. For ram_reset, the generic fallback is registers plus a mux tree.
To establish actual block-RAM mapping, run the target-specific synthesis flow and inspect the mapped RAM primitives, dimensions, and read/write behavior. Do not treat a generic memory count or RD_CLK_ENABLE alone as proof that a device RAM resource was selected.
memory -nomap prove the design uses block RAM?No. It proves memory structure remains at that intermediate stage. Target-specific mapping must select a RAM primitive, and the mapped report must confirm the intended resource and behavior. Plain memory instead maps remaining generic memories to registers and decoding logic.
Memory contents are almost never reset; for clean contents, write zeros with a short state machine after reset.
Use a plain array logic [DW-1:0] mem [DEPTH] and index it.
Write synchronously, in a clocked always_ff, with a write enable.
Read synchronously: assign the read data to a register on the clock edge. This matches the block RAM's registered output.
Don't reset the array. Reset the control logic around it.
One write per port per cycle. Multiple writes to the array from different branches become extra ports.
Keep the address and enable logic simple; wide muxes in front of the array may defeat the mapping.
Read the synthesis report: check the tool reports a RAM of the expected size and type, not thousands of flops.
ram_async_read into a block-RAM-compatible RAM?Register the read: always_ff @(posedge clk) dout <= mem[addr];, as in ram_good. The read data then appears one cycle after the address.