Sign In

Don't have a Xilinx account yet?

  • Choose to receive important news and product information
  • Gain access to special content
  • Personalize your web experience on Xilinx.com

Create Account

Username

Password

Forgot your password?
XClose Panel
Xilinx Home
Reply
Expert Contributor
rcingham
Posts: 2,010
Registered: ‎09-09-2010
0

Re: Multi ported RAM in FPGA

What FPGA are you targetting?
What version of tools?

------------------------------------------
"If it don't work in simulation, it won't work on the board."
Regular Visitor
anikau31
Posts: 19
Registered: ‎01-13-2012
0

Re: Multi ported RAM in FPGA

I no longer getting that error.

 

I am targetting the design on VIRTEX 5 and I am using Xilinx ISE v13.

 

In my previous posts about designing the banked memory structure, I am viewing another anamoly in my design. The testbench is attached with this. 

 

I am writing data into address 0 using write_data_0 and I would like to read from the same address. However on doing that, I am getting two outputs, one from read_data_0 of bank0 and read_data_0 of bank1( which is read_data_2 of enitre banked structure). y RTL schematic shows the proper routing of address signals and mux working. What am I doing wrong?

screenshot.bmp
Expert Contributor
eteam00
Posts: 7,505
Registered: ‎07-21-2009
0

UPDATED: Multi ported RAM in FPGA

[ Edited ]

In your code, please consider the highlighted lines and comments:

 

module try1(clock, we_0,we_1, data_0,data_1, addr_0,addr_1, read_data0, read_data1);

input clock;
input we_0;
input we_1;
input [7:0] addr_0;  // 8 address bits implies 256-word (deep) RAM
input [7:0] addr_1;  // 8 address bits implies 256-word (deep) RAM
input [31:0] data_0;
input [31:0] data_1;

output reg [31:0] read_data0;
output reg [31:0] read_data1;


reg [31:0] ram [0:1023];  // this infers a 32-bit wide, 1K deep RAM

 

always @(posedge clock)
  begin
    if (we_0) begin
      ram[addr_0] <= data_0; // addressing a 1K deep RAM with only 8-bit address
      read_data0 <= data_0;
      end
    else
      read_data0 <= ram[addr_0]; // addressing a 1K deep RAM with only 8-bit address
  end


always @(posedge clock)
  begin
    if (we_1) begin
      ram[addr_1] <= data_1; // addressing a 1K deep RAM with only 8-bit address
      read_data1 <= data_1;
      end
    else
      read_data1 <= ram[addr_1]; // addressing a 1K deep RAM with only 8-bit address
  end


endmodule

 

Note the mismatch in RAM depth (1K, a 10bit address space) vs. addresses (8-bit, or 256-deep memory)

 

Also, it is not permitted to assign values to a single register (or RAM) in two different processes.  You must combine the memory assignments into a single process, or instantiate separate memories for each process.  UPDATE:  Please ignore this warning, it is incorrect.  See post #15 in this thread for details.

 

I am writing data into address 0 using write_data_0 and I would like to read from the same address. However on doing that, I am getting two outputs, one from read_data_0 of bank0 and read_data_0 of bank1( which is read_data_2 of enitre banked structure). y RTL schematic shows the proper routing of address signals and mux working. What am I doing wrong?

 

Please post your updated code.  Comments in your code would be helpful as well.

 

Please, as a favour to everyone reading your posts, format your code in the fixed-pitch COURIER font or use the code button (second icon left of the smiley face in the toolbar).  This will make your posts much more readable, with less effort.

 

Are any warning or error messages reported when you synthesise your design?  There are likely some valuable clues in the warning messages.

 

-- Bob Elkind

SIGNATURE:
README for newbies is here: http://forums.xilinx.com/t5/New-Users-Forum/README-first-Help-for-new-users/td-p/219369

Summary:
1. Read the manual or user guide. Have you read the manual? Can you find the manual?
2. Search the forums (and search the web) for similar topics.
3. Do not post the same question on multiple forums.
4. Do not post a new topic or question on someone else's thread, start a new thread!
5. Students: Copying code is not the same as learning to design.
6 "It does not work" is not a question which can be answered. Provide useful details (with webpage, datasheet links, please).
7. You are not charged extra fees for comments in your code.
8. I am not paid for forum posts. If I write a good post, then I have been good for nothing.
Regular Visitor
anikau31
Posts: 19
Registered: ‎01-13-2012
0

Re: Multi ported RAM in FPGA

The updated code for my banked memory module:

 

module memory_2w8r(clock, write_en, addr_0, addr_1, addr_2, addr_3, addr_4, addr_5, addr_6, addr_7, write_data_0, write_data_1,
read_data_0, read_data_1, read_data_2, read_data_3, read_data_4, read_data_5, read_data_6, read_data_7);
    

input clock;
input write_en;

input [7:0] addr_0;
input [7:0] addr_1;
input [7:0] addr_2;
input [7:0] addr_3;
input [7:0] addr_4;
input [7:0] addr_5;
input [7:0] addr_6;
input [7:0] addr_7;

input [31:0] write_data_0;
input [31:0] write_data_1;

output [31:0] read_data_0;
output [31:0] read_data_1;
output [31:0] read_data_2;
output [31:0] read_data_3;
output [31:0] read_data_4;
output [31:0] read_data_5;
output [31:0] read_data_6;
output [31:0] read_data_7;

reg [7:0] muxed_addr_0;
reg [7:0] muxed_addr_1;
reg [7:0] muxed_addr_2;
reg [7:0] muxed_addr_3;
reg [7:0] muxed_addr_4;
reg [7:0] muxed_addr_5;


true_dual bank0 (
    .clock(clock),        
    .we_0(write_en),
    .we_1(write_en),
    .addr_0(addr_0),
    .addr_1(addr_1),
    .data_0(write_data_0),
    .data_1(write_data_1),
    .read_data0(read_data_0),
    .read_data1(read_data_1)
);

true_dual bank_1 (
     .clock(clock),        
    .we_0(write_en),
    .we_1(write_en),
    .addr_0(muxed_addr_0),
    .addr_1(muxed_addr_1),
    .data_0(write_data_0),
    .data_1(write_data_1),
    .read_data0(read_data_2),
    .read_data1(read_data_3)
);


true_dual bank_2 (
     .clock(clock),        
    .we_0(write_en),
    .we_1(write_en),
    .addr_0(muxed_addr_2),
    .addr_1(muxed_addr_3),
    .data_0(write_data_0),
    .data_1(write_data_1),
    .read_data0(read_data_4),
    .read_data1(read_data_5)
);


true_dual bank_3 (
     .clock(clock),        
    .we_0(write_en),
    .we_1(write_en),
    .addr_0(muxed_addr_4),
    .addr_1(muxed_addr_5),
    .data_0(write_data_0),
    .data_1(write_data_1),
    .read_data0(read_data_6),
    .read_data1(read_data_7)
);

    always @(*) begin
    case(write_en)

        1'b1: begin
          muxed_addr_0 <= addr_0;
	       muxed_addr_1 <= addr_1;
	       muxed_addr_2 <= addr_0;
          muxed_addr_3 <= addr_1;
          muxed_addr_4 <= addr_0;
          muxed_addr_5 <= addr_1;
        end

        1'b0: begin
	muxed_addr_0 <= addr_2;
        muxed_addr_1 <= addr_3;
	muxed_addr_2 <= addr_4;
	muxed_addr_3 <= addr_5;
	muxed_addr_4 <= addr_6;
	muxed_addr_5 <= addr_7;
	end
    endcase

end


endmodule

 The verilog code for the true dual port memory is below:

module true_dual(clock, we_0,we_1, data_0,data_1, addr_0,addr_1, read_data0, read_data1
    );

input clock;
input we_0;
input we_1;
input [7:0] addr_0;
input [7:0] addr_1;
input [31:0] data_0;
input [31:0] data_1;

output reg [31:0] read_data0;
output reg [31:0] read_data1;

reg [31:0] ram [0:255];

always @(posedge clock)
begin
    if (we_0) 
	   ram[addr_0] <= data_0; 
		
	   read_data0 <= ram[addr_0] ;
	end
	
	
always @(posedge clock)
begin
    if (we_1) 
	   ram[addr_1] <= data_1; 
		
	   read_data1 <= ram[addr_1] ;
	end
	
endmodule

 There are no warnings or errors. 

 

Also, could you explain the below statement:

 

Also, it is not permitted to assign values to a single register (or RAM) in two different processes.  You must combine the memory assignments into a single process, or instantiate separate memories for each process.

 

Does this mean I cannot write data into address 8'b10101010 at different clock cycles? Why?

 

Thank you

Expert Contributor
eteam00
Posts: 7,505
Registered: ‎07-21-2009
0

Re: Multi ported RAM in FPGA

[ Edited ]

This is complicated...  Please read this all the way through to the end...

 

Also, could you explain the below statement:

 

Also, it is not permitted to assign values to a single register (or RAM) in two different processes.  You must combine the memory assignments into a single process, or instantiate separate memories for each process.

 

Hardware and software are different.  In software you can write to a register in any line of code, or in any code module.  In HDL (Verilog or VHDL) describing hardware, the description of any hardware register must be contained within a single process.

 

Here is one process:

always @(posedge clock)
  begin

    if (we_0) ram[addr_0] <= data_0;

    read_data0 <= ram[addr_0] ;
  end


Here is another process:

always @(posedge clock)
  begin
    if (we_1) ram[addr_1] <= data_1;

    read_data1 <= ram[addr_1] ;
  end

 

The same RAM cannot be described twice, in two different processes.  As a self-identified guru of everything (I am being sarcastic, and you will see why in the next few lines), this cannot synthesise correctly, and the two processes must be combined into a single process.

 

But...  it does synthesise and it does "build" under XST, and the mighty "guru of everything" (referring to me) is quite wrong.  The RAM array is recognised by XST as a dual-port RAM with two independent write ports.  This makes complete sense.  There is no "legal" way to infer a dual-port RAM with independent clocks in a single process -- because a single process is limited to a single clock.

 

And this is the reason for my self-mocking as "guru of everything".  It turns out that something I understood with complete confidence apparently has an exception which is entirely useful and reasonable in the context of Xilinx FPGA design.  I "knew" something I didn't actually know, in other words.

 

If you tried to do the same thing (infer a register in multiple processes) with a single register (rather than RAM), synthesis would fail and report a "multiple drivers" error.

 

Summary:  After explaining in moderate detail why I must be right, it turns out that I am wrong ...  in the case of a dual-port RAM with independent clocks.  So, please ignore my warning.

 

Does this mean I cannot write data into address 8'b10101010 at different clock cycles? Why?

 

No, please ignore my warning.  I was mistaken.

 

-- Bob Elkind

SIGNATURE:
README for newbies is here: http://forums.xilinx.com/t5/New-Users-Forum/README-first-Help-for-new-users/td-p/219369

Summary:
1. Read the manual or user guide. Have you read the manual? Can you find the manual?
2. Search the forums (and search the web) for similar topics.
3. Do not post the same question on multiple forums.
4. Do not post a new topic or question on someone else's thread, start a new thread!
5. Students: Copying code is not the same as learning to design.
6 "It does not work" is not a question which can be answered. Provide useful details (with webpage, datasheet links, please).
7. You are not charged extra fees for comments in your code.
8. I am not paid for forum posts. If I write a good post, then I have been good for nothing.
Expert Contributor
eteam00
Posts: 7,505
Registered: ‎07-21-2009
0

Re: Multi ported RAM in FPGA

Your updated code looks good, to me.  Have the simulation results changed?

 

-- Bob Elkind

SIGNATURE:
README for newbies is here: http://forums.xilinx.com/t5/New-Users-Forum/README-first-Help-for-new-users/td-p/219369

Summary:
1. Read the manual or user guide. Have you read the manual? Can you find the manual?
2. Search the forums (and search the web) for similar topics.
3. Do not post the same question on multiple forums.
4. Do not post a new topic or question on someone else's thread, start a new thread!
5. Students: Copying code is not the same as learning to design.
6 "It does not work" is not a question which can be answered. Provide useful details (with webpage, datasheet links, please).
7. You are not charged extra fees for comments in your code.
8. I am not paid for forum posts. If I write a good post, then I have been good for nothing.
Regular Visitor
anikau31
Posts: 19
Registered: ‎01-13-2012
0

Re: Multi ported RAM in FPGA

Thank you Mr Bob for clarifying ,my doubt.

 

I was able to understand the error but I am not able to understand why it occurs. I will explain it along with the attached screenshot of my testbench results.

 

Initially, I write data into two address ports when write_en is high. Later I read from address port 0 and address port 7 when write_en goes low.

 

Now, if you have a look at the screenshot, you will observe outputs from read_data_0 and read_data_1 and a clock cycle later you will observe output from read_data_7 which is the same value as from read_data_1. 

 

In other words, it takes one clock cycle extra for the multiplier to change addresses depending on the write_en signal.

 

However my coding has non blocking statements and I am not able to understand the one clock cycle delay. Is there any coding alternative to rectify this clock delay?

 

Thanks and regards

imp.bmp
Expert Contributor
eteam00
Posts: 7,505
Registered: ‎07-21-2009
0

Re: Multi ported RAM in FPGA

[ Edited ]

You should add the muxed_addr_n signals to your simulation trace.

 

-- Bob Elkind

SIGNATURE:
README for newbies is here: http://forums.xilinx.com/t5/New-Users-Forum/README-first-Help-for-new-users/td-p/219369

Summary:
1. Read the manual or user guide. Have you read the manual? Can you find the manual?
2. Search the forums (and search the web) for similar topics.
3. Do not post the same question on multiple forums.
4. Do not post a new topic or question on someone else's thread, start a new thread!
5. Students: Copying code is not the same as learning to design.
6 "It does not work" is not a question which can be answered. Provide useful details (with webpage, datasheet links, please).
7. You are not charged extra fees for comments in your code.
8. I am not paid for forum posts. If I write a good post, then I have been good for nothing.
Regular Visitor
anikau31
Posts: 19
Registered: ‎01-13-2012
0

Re: Multi ported RAM in FPGA

The complete code for my multiported design is as follows

module my_multipumped_memory(clock, write_en, write_data_0, write_data_1, write_data_2, write_data_3, 
write_addr2, write_addr3, addr_0, addr_1, addr_2, addr_3, addr_4, addr_5, addr_6, addr_7, read_data_0, read_data_1, read_data_2,
read_data_3, read_data_4, read_data_5, read_data_6, read_data_7);
 
input clock;
input write_en;

input [7:0] addr_0;
input [7:0] addr_1;
input [7:0] addr_2;
input [7:0] addr_3;
input [7:0] addr_4;
input [7:0] addr_5;
input [7:0] addr_6;
input [7:0] addr_7;
input [7:0] write_addr2;
input [7:0] write_addr3;

input [31:0] write_data_0;
input [31:0] write_data_1;
input [31:0] write_data_2;
input [31:0] write_data_3;

output  [31:0] read_data_0;
output  [31:0] read_data_1;
output  [31:0] read_data_2;
output  [31:0] read_data_3;
output  [31:0] read_data_4;
output  [31:0] read_data_5;
output  [31:0] read_data_6;
output  [31:0] read_data_7; 

wire [31:0] read_data_0_0;
wire [31:0] read_data_0_1;
wire [31:0] read_data_0_2;
wire [31:0] read_data_0_3;
wire [31:0] read_data_0_4;
wire [31:0] read_data_0_5;
wire [31:0] read_data_0_6;
wire [31:0] read_data_0_7;

wire [31:0] read_data_1_0;
wire [31:0] read_data_1_1;
wire [31:0] read_data_1_2;
wire [31:0] read_data_1_3;
wire [31:0] read_data_1_4;
wire [31:0] read_data_1_5;
wire [31:0] read_data_1_6;
wire [31:0] read_data_1_7;

reg [7:0] muxeaddr_2;
reg [7:0] muxeaddr_3;

wire read_0;
wire read_1;
wire read_2;
wire read_3;
wire read_4;
wire read_5;
wire read_6;
wire read_7;

always @(*) begin

if(write_en == 1'b1) begin
muxeaddr_2 <= write_addr2;
muxeaddr_3 <= write_addr3;
end

else begin
muxeaddr_2 <= addr_0;
muxeaddr_3 <= addr_1;
end

end

lvt_4w8r lvt(
.clock(clock),
.enable(write_en),
.write_address_0(addr_0),
.write_address_1(addr_1),
.write_address_2(muxeaddr_2),
.write_address_3(muxeaddr_3),
.read_addr_4(addr_4),
.read_addr_5(addr_5),
.read_addr_6(addr_6),
.read_addr_7(addr_7),
.read_addr_tag_0(read_0),
.read_addr_tag_1(read_1),
.read_addr_tag_2(read_2),
.read_addr_tag_3(read_3),
.read_addr_tag_4(read_4),
.read_addr_tag_5(read_5),
.read_addr_tag_6(read_6),
.read_addr_tag_7(read_7)
);


memory_2w8r mem0 (
.clock(clock),
.write_en(write_en),
.write_data_0(write_data_0),
.write_data_1(write_data_1),
.addr_0(addr_0),
.addr_1(addr_1),
.addr_2(addr_2),
.addr_3(addr_3),
.addr_4(addr_4),
.addr_5(addr_5),
.addr_6(addr_6),
.addr_7(addr_7),
.read_data_0(read_data_0_0),
.read_data_1(read_data_0_1),
.read_data_2(read_data_0_2),
.read_data_3(read_data_0_3),
.read_data_4(read_data_0_4),
.read_data_5(read_data_0_5),
.read_data_6(read_data_0_6),
.read_data_7(read_data_0_7)
);

memory_2w8r mem1 (
.clock(clock),
.write_en(write_en),
.write_data_0(write_data_2),
.write_data_1(write_data_3),
.addr_0(muxeaddr_2),
.addr_1(muxeaddr_3),
.addr_2(addr_2),
.addr_3(addr_3),
.addr_4(addr_4),
.addr_5(addr_5),
.addr_6(addr_6),
.addr_7(addr_7),
.read_data_0(read_data_1_0),
.read_data_1(read_data_1_1),
.read_data_2(read_data_1_2),
.read_data_3(read_data_1_3),
.read_data_4(read_data_1_4),
.read_data_5(read_data_1_5),
.read_data_6(read_data_1_6),
.read_data_7(read_data_1_7)
);

MUX_data_2to1 m0(
.clock(clock),
.selector(read_0),
.data_0(read_data_0_0),
.data_1(read_data_1_0),
.output_data(read_data_0)
);


MUX_data_2to1 m1(
.clock(clock),
.selector(read_1),
.data_0(read_data_0_1),
.data_1(read_data_1_1),
.output_data(read_data_1)
);


MUX_data_2to1 m2(
.clock(clock),
.selector(read_2),
.data_0(read_data_0_2),
.data_1(read_data_1_2),
.output_data(read_data_2)
);


MUX_data_2to1 m3(
.clock(clock),
.selector(read_3),
.data_0(read_data_0_3),
.data_1(read_data_1_3),
.output_data(read_data_3)
);


MUX_data_2to1 m4(
.clock(clock),
.selector(read_4),
.data_0(read_data_0_4),
.data_1(read_data_1_4),
.output_data(read_data_4)
);


MUX_data_2to1 m5(
.clock(clock),
.selector(read_5),
.data_0(read_data_0_5),
.data_1(read_data_1_5),
.output_data(read_data_5)
);


MUX_data_2to1 m6(
.clock(clock),
.selector(read_6),
.data_0(read_data_0_6),
.data_1(read_data_1_6),
.output_data(read_data_6)
);


MUX_data_2to1 m7(
.clock(clock),
.selector(read_7),
.data_0(read_data_0_7),
.data_1(read_data_1_7),
.output_data(read_data_7)
);



endmodule

 In the above code, there are two registers voz. muxeaddr_2 and muxeaddr_3. These two 8bit address signals ae connected to write_addr2 and write_addr3 when write_en is high and to addr_0 and addr_1 when write_en is low.

 

When I creat the test bench, muxeaddr_2 and muxeaddr_3 show unknown values despite writing address values into write_addr2 and write_addr3. I used the same muxing concept in 2W/8R banked structure and it works perfectly fine. The code to my 2W/8R is given below:

module memory_2w8r(clock, write_en, addr_0, addr_1, addr_2, addr_3, addr_4, addr_5, addr_6, addr_7, write_data_0, write_data_1,
read_data_0, read_data_1, read_data_2, read_data_3, read_data_4, read_data_5, read_data_6, read_data_7);
    

input clock;
input write_en;

input [7:0] addr_0;
input [7:0] addr_1;
input [7:0] addr_2;
input [7:0] addr_3;
input [7:0] addr_4;
input [7:0] addr_5;
input [7:0] addr_6;
input [7:0] addr_7;

input [31:0] write_data_0;
input [31:0] write_data_1;

output [31:0] read_data_0;
output [31:0] read_data_1;
output [31:0] read_data_2;
output [31:0] read_data_3;
output [31:0] read_data_4;
output [31:0] read_data_5;
output [31:0] read_data_6;
output [31:0] read_data_7;

reg [7:0] muxed_addr_0;
reg [7:0] muxed_addr_1;
reg [7:0] muxed_addr_2;
reg [7:0] muxed_addr_3;
reg [7:0] muxed_addr_4;
reg [7:0] muxed_addr_5;


true_dual bank0 (
    .clock(clock),        
    .we_0(write_en),
    .we_1(write_en),
    .addr_0(addr_0),
    .addr_1(addr_1),
    .data_0(write_data_0),
    .data_1(write_data_1),
    .read_data0(read_data_0),
    .read_data1(read_data_1)
);

true_dual bank_1 (
     .clock(clock),        
    .we_0(write_en),
    .we_1(write_en),
    .addr_0(muxed_addr_0),
    .addr_1(muxed_addr_1),
    .data_0(write_data_0),
    .data_1(write_data_1),
    .read_data0(read_data_2),
    .read_data1(read_data_3)
);


true_dual bank_2 (
     .clock(clock),        
    .we_0(write_en),
    .we_1(write_en),
    .addr_0(muxed_addr_2),
    .addr_1(muxed_addr_3),
    .data_0(write_data_0),
    .data_1(write_data_1),
    .read_data0(read_data_4),
    .read_data1(read_data_5)
);


true_dual bank_3 (
     .clock(clock),        
    .we_0(write_en),
    .we_1(write_en),
    .addr_0(muxed_addr_4),
    .addr_1(muxed_addr_5),
    .data_0(write_data_0),
    .data_1(write_data_1),
    .read_data0(read_data_6),
    .read_data1(read_data_7)
);


always @(*) begin
    if(write_en == 1'b1) begin
        muxed_addr_0 <= addr_0;
        muxed_addr_1 <= addr_1;
		  muxed_addr_2 <= addr_0;
        muxed_addr_3 <= addr_1;
        muxed_addr_4 <= addr_0;
        muxed_addr_5 <= addr_1;
    end
    else begin
        muxed_addr_0 <= addr_2;
        muxed_addr_1 <= addr_3;
		  muxed_addr_2 <= addr_4;
	     muxed_addr_3 <= addr_5;
	     muxed_addr_4 <= addr_6;
	     muxed_addr_5 <= addr_7;
    end
end
endmodule

 What is the mistake I am making?

 

Thanks and regards

Expert Contributor
eteam00
Posts: 7,505
Registered: ‎07-21-2009
0

unknown values in sim

[ Edited ]

When I creat the test bench, muxeaddr_2 and muxeaddr_3 show unknown values...

 

Both muxeaddr_2 and muxeaddr_3 are generated by combinatorial assignments from signals which are all module inputs.

 

always @(*)
  if(write_en == 1'b1) begin
    muxeaddr_2 <= write_addr2;
    muxeaddr_3 <= write_addr3;
    end
  else begin
    muxeaddr_2 <= addr_0;
    muxeaddr_3 <= addr_1;
    end

 

The signals muxeaddr_2 and muxeaddr_3 cannot be unknown unless one or more of these signals (all of which are module inputs) is also unknown:

  • write_en
  • write_addr2
  • write_addr3
  • addr_0
  • addr_1

Does this make sense?

Are there any error or warning messages from the synthesis tool?

 

-- Bob Elkind

SIGNATURE:
README for newbies is here: http://forums.xilinx.com/t5/New-Users-Forum/README-first-Help-for-new-users/td-p/219369

Summary:
1. Read the manual or user guide. Have you read the manual? Can you find the manual?
2. Search the forums (and search the web) for similar topics.
3. Do not post the same question on multiple forums.
4. Do not post a new topic or question on someone else's thread, start a new thread!
5. Students: Copying code is not the same as learning to design.
6 "It does not work" is not a question which can be answered. Provide useful details (with webpage, datasheet links, please).
7. You are not charged extra fees for comments in your code.
8. I am not paid for forum posts. If I write a good post, then I have been good for nothing.