Trying to understand on-chip memory
Hi. I'm using the ROM: 1-PORT in the IP Catalog. I've been unable to find a code example of accessing that on-chip memory. I hope someone here can help.
The IP Catalog wizard generates code for a module:
module rom ( address, clock, q); input [10:0] address; input clock; output [7:0] q; `ifndef ALTERA_RESERVED_QIS // synopsys translate_off `endif tri1 clock; `ifndef ALTERA_RESERVED_QIS // synopsys translate_on `endif wire [7:0] sub_wire0; wire [7:0] q = sub_wire0[7:0]; altsyncram altsyncram_component ( .address_a (address), .clock0 (clock), .q_a (sub_wire0), .aclr0 (1'b0), .aclr1 (1'b0), .address_b (1'b1), .addressstall_a (1'b0), .addressstall_b (1'b0), .byteena_a (1'b1), .byteena_b (1'b1), .clock1 (1'b1), .clocken0 (1'b1), .clocken1 (1'b1), .clocken2 (1'b1), .clocken3 (1'b1), .data_a ({8{1'b1}}), .data_b (1'b1), .eccstatus (), .q_b (), .rden_a (1'b1), .rden_b (1'b1), .wren_a (1'b0), .wren_b (1'b0)); defparam altsyncram_component.address_aclr_a = "NONE", altsyncram_component.clock_enable_input_a = "BYPASS", altsyncram_component.clock_enable_output_a = "BYPASS", `ifdef NO_PLI altsyncram_component.init_file = "G:/My Drive/microcode1.rif" `else altsyncram_component.init_file = "G:/My Drive/microcode1.hex" `endif , altsyncram_component.intended_device_family = "MAX 10", altsyncram_component.lpm_hint = "ENABLE_RUNTIME_MOD=NO", altsyncram_component.lpm_type = "altsyncram", altsyncram_component.numwords_a = 2048, altsyncram_component.operation_mode = "ROM", altsyncram_component.outdata_aclr_a = "NONE", altsyncram_component.outdata_reg_a = "CLOCK0", altsyncram_component.widthad_a = 11, altsyncram_component.width_a = 8, altsyncram_component.width_byteena_a = 1; endmodule
and then I instantiate in my own Verilog code:
rom rom_inst (.address ( address ), .clock ( clock ), .q ( q ));
What I don't understand is what q should be. Should it be 'reg [7:0] q;' or 'wire [7:0] q;'?
It seems to me, it would have to be a reg, especially since we're using a clock to fetch memory. But look at lines 12-18 in the generated module:
12 wire [7:0] sub_wire0; 13 wire [7:0] q = sub_wire0[7:0]; 14 15 altsyncram altsyncram_component ( 16 .address_a (address), 17 .clock0 (clock), 18 .q_a (sub_wire0),
It's using a wire to receive the memory contents! I would've thought that, inside altsyncram, there's an always block that, on the rising edge of clock0, reads the memory contents into a register. But a wire makes me think it's just combinational.
And what does that mean for my code? Do I need an always block clocking the rom module's q output into a register? And when should I do that? I assume I cannot trust that q has valid data on it, at the same clock rising edge that triggered the memory read. I need to wait for the memory access time, right?
Thank you for your help.
You should declare the output q as a wire, because the ROM module already registers the output internally.
You do not need to add your own always @(posedge clk) just to capture q, unless you want to add pipeline stages or synchronize it further.
The altsyncram component is configured with:
altsyncram_component.outdata_reg_a = "CLOCK0"
This means the output is registered on the rising edge of clock0 inside the IP — so the ROM fetches data synchronously, with output changes one clock cycle after the address is applied.
So even though q is declared as a wire, it is driven by a register inside the ROM IP. That’s why your code can simply declare:
wire [7:0] q;
rom rom_inst (
.address (address),
.clock (clock),
.q (q)
);And it should works exactly as a synchronous ROM should.
So yes — you're absolutely right: you cannot use q on the same clock edge that you apply the address. You need to wait one clock cycle.
Regards,
Richard Tan