I have a Cyclone V GX starter kit. It comes with 4884 bits on-chip memory. I want to write a module for accessing the on-chip memory. So, I have generated the RAM IP reference design from the IP catalog like this:
module onchipmem (
aclr,
address,
clken,
clock,
data,
rden,
wren,
q);
input aclr;
input [7:0] address;
input clken;
input clock;
input [15:0] data;
input rden;
input wren;
output [15:0] q;
`ifndef ALTERA_RESERVED_QIS
// synopsys translate_off
`endif
tri0 aclr;
tri1 clken;
tri1 clock;
tri1 rden;
`ifndef ALTERA_RESERVED_QIS
// synopsys translate_on
`endif
wire [15:0] sub_wire0;
wire [15:0] q = sub_wire0[15:0];
altsyncram altsyncram_component (
...
);
I can write / read data by creating an instance of this module:
onchipmem write_mem (
.aclr(reset),
.address(address),
.clken(ce),
.clock(clk),
.data(data),
.rden(1'b0),
.wren(1'b1),
.q()
);
I want to save data by onchipmem
in one module and read data from the same instance of onchipmem
from another module. However, creating multiple instances of onchipmem
will allocate duplicated block of RAM per instance.
In other words, a single RAM module cannot be called by multiple modules. So how to implement a share RAM module for multiple modules to access?
I found this answer that suggested using an arbiter
. He provided some sample codes of an arbiter but it is not completed.
I am new to FPGA and cannot get how it works.
So I am looking for a real and completed example so that I can learn and test it in ModelSim.
Please advise.
-
\$\begingroup\$ well, you will need an arbiter, yes. So, the easiest way is to learn enough HDL to understand what the code does in principle. It's really not that much code, and I don't think you'll have much success elsewhere when you can't formulate a follow-up question to the code you're given. So work on it! \$\endgroup\$Marcus Müller– Marcus Müller2018年08月17日 11:57:59 +00:00Commented Aug 17, 2018 at 11:57
-
\$\begingroup\$ You need to understand that a verilog module is not called by another module. Modules are connected by wires and those connections are fixed. \$\endgroup\$Elliot Alderson– Elliot Alderson2018年08月17日 14:33:16 +00:00Commented Aug 17, 2018 at 14:33
1 Answer 1
An Arbiter is the general way of doing it.
An Arbiter is essentially a block that is connected to your RAM and reads/writes data from/to the RAM.
The arbiter exports interfaces (can be an arbitrary count) to other modules. Through these interfaces, modules can access the RAM via the arbiter. The arbiter internally handles the request from its input. There are several ways of handling requests:
- The different modules have different priorities.
- The requests are processed in a round-robin-loop
- etc.
The specific implementation highly depends on the interface used. The interface from the arbiter to the RAM is given by the RAM module you use. However, these interface standards are mostly not usable for the input ports of an arbiter as they are not able to handle variable timings. If a module wants to write to the RAM but another is given access at the moment the arbiter has to stall the interface until it can process the request.
On Altera systems, the AvalonMM bus is a common way and compatible with many Altera IP cores. But you could also go with Wishbone or a custom bus.
I don't have a complete example because it highly depends on your requirements. However, a simple arbiter is a nice project for learning.
Another quick way to connect two modules to a RAM is generating a dual-port RAM with the Altera Wizard. This RAM has 2 independently usable RAM ports.