Electronics > FPGA

Dual port ram with different data widths

(1/3) > >>

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

There was an error while thanking
Thanking...
Go to full version