Electronics > FPGA
Dual port ram with different data widths
ali_asadzadeh:
Hi,
I want to have a dual port ram with different data port widths, how can I impalement that, I want to model it in Modelsim, here is a simple code that I'm using for a dual port ram with the same data widths on both port A and port B
--- Code: ---module DualPortRam #(
//=============
// Parameters
//=============
parameter RAM_DATA_WIDTH = 16, // width of the data
parameter RAM_ADDR_WIDTH = 4 // number of address bits
) (
//================
// General Ports
//================
input wire rst,
//=========
// Port A
//=========
input wire a_clk,
input wire a_wr, // pulse a 1 to write and 0 reads
input wire [RAM_ADDR_WIDTH-1:0] a_addr,
input wire [RAM_DATA_WIDTH-1:0] a_data_in,
output reg [RAM_DATA_WIDTH-1:0] a_data_out,
//=========
// Port B
//=========
input wire b_clk,
input wire b_wr, // pulse a 1 to write and 0 reads
input wire [RAM_ADDR_WIDTH-1:0] b_addr,
input wire [RAM_DATA_WIDTH-1:0] b_data_in,
output reg [RAM_DATA_WIDTH-1:0] b_data_out
);
//===============
// Local Params
//===============
localparam RAM_DATA_DEPTH = 2**RAM_ADDR_WIDTH; // depth of the ram, this is tied to the number of address bits
//================
// Shared memory
//================
reg [RAM_DATA_WIDTH-1:0] mem [RAM_DATA_DEPTH-1:0];
//=========
// Port A
//=========
always @(posedge a_clk) begin
if (`ifdef ACTIVE_LOW_RST !rst `else rst `endif)
a_data_out <= {RAM_DATA_WIDTH{1'b0}};
else begin
a_data_out <= mem[a_addr];
if (a_wr) begin
mem[a_addr] <= a_data_in;
end
end
end
//=========
// Port B
//=========
always @(posedge b_clk) begin
if (`ifdef ACTIVE_LOW_RST !rst `else rst `endif)
b_data_out <= {RAM_DATA_WIDTH{1'b0}};
else begin
b_data_out <= mem[b_addr];
if (b_wr) begin
mem[b_addr] <= b_data_in;
end
end
end
initial begin
mem[0] = 16'hCD11;
mem[1] = 16'h6C28;
mem[2] = 16'h050B;
mem[3] = 16'h962E;
mem[4] = 16'h21D8;
mem[5] = 16'hAE7C;
mem[6] = 16'h409C;
mem[7] = 16'hDE95;
mem[8] = 16'h841B;
end
endmodule
--- End code ---
I want to have a 8bit width on port A and a 16bit data width on port B
Daixiwen:
Which FPGA are you targetting? Even if you want to write generic code, it can be a good idea to see what the FPGA synthesizer expects, to be sure that your code can be recognized correctly and will generate hard RAM blocks.
If you are targetting Altera/Intel, there is a guide here: https://www.intel.com/content/dam/www/programmable/us/en/pdfs/literature/hb/qts/qts_qii51007.pdf
Pages 13-28 and 13-29 talk about implementing mixed width dual port RAMs. Apparently this is not possible in Verilog, because of the lack of support for multi-dimensional arrays, but there is a way in SystemVerilog.
The whole "inferring memory from HDL code" chapter is interesting and gives lots of different examples.
ali_asadzadeh:
my Target FPGA is Gowin, But for now and purely for simulation I want to write a generic code, how can I write it with SystemVerilog?
ali_asadzadeh:
I have written some code for now, and it's not working as expected, for some reasons, the modelsim would not detect posedge clocks after a few clock cycles! do you have any Idea?
Dual port ram with 8bit data on port A and 16bit on port B
--- Code: ---module myDualPortRam #(
//=============
// Parameters portA has always 8bit data and port B has 16 bit data
//=============
parameter RAM_DATA_WIDTHA = 8, // width of the data A
parameter RAM_ADDR_WIDTHA = 5, // number of address bits A
parameter RAM_DATA_WIDTHB = 16, // width of the data A
parameter RAM_ADDR_WIDTHB = 4 // number of address bits A
) (
//================
// General Ports
//================
input wire rst,
//=========
// Port A
//=========
input wire a_clk,
input wire a_wr, // pulse a 1 to write and 0 reads
input wire [RAM_ADDR_WIDTHA-1:0] a_addr,
input wire [RAM_DATA_WIDTHA-1:0] a_data_in,
output reg [RAM_DATA_WIDTHA-1:0] a_data_out,
//=========
// Port B
//=========
input wire b_clk,
input wire b_wr, // pulse a 1 to write and 0 reads
input wire [RAM_ADDR_WIDTHB-1:0] b_addr,
input wire [RAM_DATA_WIDTHB-1:0] b_data_in,
output reg [RAM_DATA_WIDTHB-1:0] b_data_out
);
//===============
// Local Params
//===============
localparam RAM_DATA_DEPTH = 2**RAM_ADDR_WIDTHA; // depth of the ram, this is tied to the number of address bits
//================
// Shared memory
//================
reg [RAM_DATA_WIDTHA-1:0] mem [RAM_DATA_DEPTH-1:0];
//=========
// Port A
//=========
always @(posedge a_clk) begin
if (`ifdef ACTIVE_LOW_RST !rst `else rst `endif)
a_data_out <= {RAM_DATA_WIDTHA{1'b0}};
else begin
a_data_out <= mem[a_addr];
if (a_wr) begin
mem[a_addr] <= a_data_in;
end
end
end
//=========
// Port B
//=========
wire wAddrBL = {b_addr,1'b0};
wire wAddrBH = {b_addr,1'b1};
always @(posedge b_clk) begin
if (`ifdef ACTIVE_LOW_RST !rst `else rst `endif)
b_data_out <= {RAM_DATA_WIDTHB{1'b0}};
else begin
b_data_out <= {mem[wAddrBH],mem[wAddrBL]};
if (b_wr) begin
mem[wAddrBL] <= b_data_in[7:0];
mem[wAddrBH] <= b_data_in[15:8];
end
end
end
initial begin
mem[0] = 8'h11;
mem[1] = 8'h28;
mem[2] = 8'h0B;
mem[3] = 8'h2E;
mem[4] = 8'hD8;
mem[5] = 8'h7C;
mem[6] = 8'h9C;
mem[7] = 8'h95;
mem[8] = 8'h1B;
mem[9] = 8'h00;
mem[10] = 8'h00;
mem[11] = 8'h00;
mem[12] = 8'h00;
mem[13] = 8'h00;
mem[14] = 8'h00;
mem[15] = 8'h00;
mem[16] = 8'h00;
mem[17] = 8'h00;
mem[18] = 8'h00;
mem[19] = 8'h00;
mem[20] = 8'h00;
mem[21] = 8'h00;
mem[22] = 8'h00;
mem[23] = 8'h00;
mem[24] = 8'h00;
mem[25] = 8'h00;
mem[26] = 8'h00;
mem[27] = 8'h00;
mem[28] = 8'h00;
mem[29] = 8'h00;
mem[30] = 8'h00;
mem[31] = 8'h00;
end
endmodule
--- End code ---
The simple test bench for reading from port B
--- Code: ---`timescale 1ns/1ns
module myDualPortRam_tb ();
parameter MAIN_CLK_DELAY = 5; // 100 MHz
reg r_Rst_L = 1'b0;
reg r_Clk = 1'b0;
// Clock Generators:
always #(MAIN_CLK_DELAY) r_Clk = ~r_Clk;
reg [4:0] r_a_addr;
reg [7:0] r_a_data_in;
wire [7:0] w_a_data_out;
reg [3:0] r_b_addr;
reg [15:0] r_b_data_in;
wire [15:0] w_b_data_out;
myDualPortRam dut(
.rst(1'b0),
.a_clk(r_Clk),
.a_wr(1'b0),
.a_addr(r_a_addr),
.a_data_in(r_a_data_in),
.a_data_out(w_a_data_out),
.b_clk(r_Clk),
.b_wr(1'b0),
.b_addr(r_b_addr),
.b_data_in(r_b_data_in),
.b_data_out(w_b_data_out)
);
initial
begin
r_Rst_L = 1'b1;
repeat(2) @(posedge r_Clk);
r_Rst_L = 1'b0;
repeat(1) @(posedge r_Clk);
//read from port B
r_b_addr = 0;
repeat(1) @(posedge r_Clk);
r_b_addr = 1;
repeat(1) @(posedge r_Clk);
r_b_addr = 2;
repeat(1) @(posedge r_Clk);
r_b_addr = 3;
repeat(1) @(posedge r_Clk);
r_b_addr = 4;
repeat(1) @(posedge r_Clk);
r_b_addr = 5;
repeat(1) @(posedge r_Clk);
r_b_addr = 6;
repeat(1) @(posedge r_Clk);
r_b_addr = 7;
repeat(1) @(posedge r_Clk);
end // initial begin
endmodule
--- End code ---
The modelsim output, only showing 0x2811 as the output! I have stepped in the code and after chaining the r_b_addr = 0; in testbench, the dual port ram module would not detect any posedge clocks! :-\
asmi:
There are so many things wrong with your code, I don't know where to start :)
1. You allow your module to be instantiated with configurable widths yet the code clearly assumes that width of the port A is always half that of the port B. Think about what happens if you instantiate your module with RAM_DATA_WIDTHA of 16 and RAM_DATA_WIDTHB of 8 ;D
2. You use the same reset signal for both ports, which implies that it has to be synchronous to both clocks simultaneously, which is only possible if clocks are the same, or one is a phase-aligned multiple of the other.
3. You use a single-bit addresses for your second port, which is why it only ever reads the first two values. That is why simulation doesn't do what you think it should.
4. You attempt to read the same memory array from two different addresses in the same clock. This is going to have problems during synthesis because true three-port memory does not exist inside FPGAs.
Navigation
[0] Message Index
[#] Next page
Go to full version