0

How can I make a memory module in which DATA bus width are passed as parameter to each instances and my design re-configure itself according to the parameter? For example, assuming I have byte addressable memory and DATA-IN bus width is 32 bit (4 bytes written in each cycle) and DATA-OUT is 16 bits (2 bytes read each cycle). For other instance DATA-IN is 64 bits and DATA-OUT is 16 bits. For all such instances my design should work.

What I have tried is to generate write pointer values according to design parameters, e.g. DATA-IN 32 bit, write pointer will increment 4 every cycle while writing. For 64 bit -increment will be by 8 and so on.

Problem is: how to make 4 or 8 or 16 bytes to be written in single cycle according to parameters passed to instance?

//Something as following I want to implement. This memory instance can be considered as internal memory of FIFO having different datawidth for reading and writing in case you think of an application of such memory

module mem#(parameter DIN=16, parameter DOUT=8, parameter ADDR=4,parameter BYTE=8)
(
  input  [DIN-1:0]  din,
  output [DOUT-1:0] dout,
  input             wen,ren,clk
);

localparam DEPTH = (1<<ADDR);
reg [BYTE-1:0] mem [0:DEPTH-1];
reg wpointer=5'b00000;
reg rpointer=5'b00000;
reg [BYTE-1:0] tmp [0:DIN/BYTE-1];

function [ADDR:0] ptr;
input [4:0] index;
integer i;
  begin
    for(i=0;i<DIN/BYTE;i=i+1)  begin 
      mem[index] = din[(BYTE*(i+1)-1):BYTE*(i)]; // something like this I want to implement, I know this line is not allowed in verilog, but is there any alternative to this?
      index=index+1;
    end
    ptr=index;
  end
endfunction

always @(posedge clk) begin 
  if(wen==1)
    wpointer <= wptr(wpointer);
end

always @(posedge clk) begin
  if(ren==1)
    rpointer <= ptr(rpointer);
end

endmodule
Qiu
  • 5,222
  • 10
  • 45
  • 53

1 Answers1

0

din[(BYTE*(i+1)-1):BYTE*(i)] will not compile in Verilog because the MSB and LSB select bits are both variables. Verilog requires a known range. +: is for part-select (also known as a slice) allows a variable select index and a constant range value. It was introduced in IEEE Std 1364-2001 § 4.2.1. You can also read more about it in IEEE Std 1800-2012 § 11.5.1, or refer to previously asked questions: What is `+:` and `-:`? and Indexing vectors and arrays with +:.

din[BYTE*i +: BYTE] should work for you, alternatively you can use din[BYTE*(i+1)-1 -: BYTE].

Also, you should use non-blocking assignments (<=) to mem. In your code read and write can happen at the same time. With blocking there is a race condition between when accessing the same byte. It may synthesize, but your RTL and gate simulation may generated different results. I also strongly advice agent using functions for assigning memory. Functions in synthesizable code without nasty surprises need to self contained without references on anything outside of the function and any internal variables are always reset to a static constant at the start of the function.

With the guidelines mentioned above, I'd recommend recoding to something like the below. This is a template to start with, not a free lunch. I left out the out-of-range index compensation for you to figure out on your own.

...
localparam DEPTH = (1<<ADDR);
reg [BYTE-1:0] mem [0:DEPTH-1];
reg [ADDR-1:0] wpointer, rpointer;
integer i;
initial begin // init values for pointers (FPGA, not ASIC)
  wpointer = {ADDR{1'b0}};
  rpointer = {ADDR{1'b0}};
end
always @(posedge clk) begin
  if (ren==1) begin
    for(i=0; i < DOUT/BYTE; i=i+1) begin
        dout[BYTE*i +: BYTE] <= mem[rpointer+i];
    end
    rpointer <= rpointer + (DOUT/BYTE);
  end
  if (wen==1) begin
    for(i=0; i < DIN/BYTE; i=i+1) begin
        mem[wpointer+i] <= din[BYTE*i +: BYTE];
    end
    wpointer <= wpointer + (DIN/BYTE);
  end
end
Greg
  • 16,013
  • 5
  • 43
  • 61