- 背景
- 手写Multiport Ram
Multiport RAM 代码方案
- 资源评估
Multiport RAM 资源利用的优化
- 资源评估
- 防止读写冲突的组合逻辑设计(写优先)
仿真和时序
- 单口写数据
- 单端口读数据
- 多口读相同数据
- 多口同时读不同数据
背景
在多端口交换机的设计中,交换机的每个端口都会各自维护一张查找表,数据帧进入到交换机后,需要进行查表和转发。但随着端口数量和表项需求的增加,每个端口都单独维护一张表使得FPGA的资源变得非常紧张。因此,需要一张查找表(本质是可读可写的RAM),能够满足多读多写的功能。但在Xilinx FPGA上,Xilinx提供的BRAM IP最高只能实现真双端口RAM。不能满足多读多写的需求。
补充:这里不使用其他RAM类型如URAM的原因是,BRAM拥有更好的时序,更适合在高速交换中用于查找表。
手写Multiport Ram
Multiport Ram,即多读多写存储器,本工程实现的是1口写,同时满足11口读的BRAM。
为了让vivado在综合的时候把手写ram例化为BRAM,我们需要按照官方手册的要求编写multiport ram。这时需要通过(ram _ style="block")对array进行修饰。
查看Vivado的官方手册ug901可知,对于Distributed RAM(LUTRAM)和Dedicated Block RAM(BRAM),二者都是写同步的。主要区别在于读数据,前者为异步,后者为同步的。
下面给出一种手写多端口bram的方案并给出一种优化FPGA bram资源利用的方法。
Multiport RAM 代码方案
实现多端口bram最简单的方法就是把读数据部分的逻辑复制11份,写数据部分的逻辑保留1份。以下代码实现了位宽73bit,深度为16K的multiport ram:
module multi_bram #( parameter ADDR_WIDTH = 14,
parameter DATA_WIDTH = 73,
parameter DEPTH = 16384)
(
input clk,
//input rst_n,
//read port
input re1,
input [ADDR_WIDTH-1:0] rd_addr1,
output reg [DATA_WIDTH-1:0] rd_data1,
//read port
input re2,
input [ADDR_WIDTH-1:0] rd_addr2,
output reg [DATA_WIDTH-1:0] rd_data2,
//read port
input re3,
input [ADDR_WIDTH-1:0] rd_addr3,
output reg [DATA_WIDTH-1:0] rd_data3,
//read port
input re4,
input [ADDR_WIDTH-1:0] rd_addr4,
output reg [DATA_WIDTH-1:0] rd_data4,
//read port
input re5,
input [ADDR_WIDTH-1:0] rd_addr5,
output reg [DATA_WIDTH-1:0] rd_data5,
//read port
input re6,
input [ADDR_WIDTH-1:0] rd_addr6,
output reg [DATA_WIDTH-1:0] rd_data6,
//read port
input re7,
input [ADDR_WIDTH-1:0] rd_addr7,
output reg [DATA_WIDTH-1:0] rd_data7,
//read port
input re8,
input [ADDR_WIDTH-1:0] rd_addr8,
output reg [DATA_WIDTH-1:0] rd_data8,
//read port
input re9,
input [ADDR_WIDTH-1:0] rd_addr9,
output reg [DATA_WIDTH-1:0] rd_data9,
//read port
input re10,
input [ADDR_WIDTH-1:0] rd_addr10,
output reg [DATA_WIDTH-1:0] rd_data10,
//read port
input re11,
input [ADDR_WIDTH-1:0] rd_addr11,
output reg [DATA_WIDTH-1:0] rd_data11,
//write port
input we,
input [ADDR_WIDTH-1:0] wr_addr,
input [DATA_WIDTH-1:0] wr_data
);
(*ram_style="block"*)reg [DATA_WIDTH-1:0] bram [0:DEPTH-1];
//read1
always @(posedge clk)
begin
if(re1)
rd_data1 <= bram[rd_addr1];
else
rd_data1 <= rd_data1;
end
//read2
always @(posedge clk)
begin
if(re2)
rd_data2 <= bram[rd_addr2];
else
rd_data2 <= rd_data2;
end
//read3
always @(posedge clk)
begin
if(re3)
rd_data3 <= bram[rd_addr3];
else
rd_data3 <= rd_data3;
end
//read4
always @(posedge clk)
begin
if(re4)
rd_data4 <= bram[rd_addr4];
else
rd_data4 <= rd_data4;
end
//read5
always @(posedge clk)
begin
if(re5)
rd_data5 <= bram[rd_addr5];
else
rd_data5 <= rd_data5;
end
//read6
always @(posedge clk)
begin
if(re6)
rd_data6 <= bram[rd_addr6];
else
rd_data6 <= rd_data6;
end
//read7
always @(posedge clk)
begin
if(re7)
rd_data7 <= bram[rd_addr7];
else
rd_data7 <= rd_data7;
end
//read8
always @(posedge clk)
begin
if(re8)
rd_data8 <= bram[rd_addr8];
else
rd_data8 <= rd_data8;
end
//read9
always @(posedge clk)
begin
if(re9)
rd_data9 <= bram[rd_addr9];
else
rd_data9 <= rd_data9;
end
//read10
always @(posedge clk)
begin
if(re10)
rd_data10 <= bram[rd_addr10];
else
rd_data10 <= rd_data10;
end
//read11
always @(posedge clk)
begin
if(re11)
rd_data11 <= bram[rd_addr11];
else
rd_data11 <= rd_data11;
end
//write
always @(posedge clk)
begin
if(we)
bram[wr_addr]<=wr_data;
end
endmodule
资源评估
利用vivado综合后,消耗的资源如下
MultiportRAM:16K深度,73位宽的单口写,11口读的RAM消耗的BRAM数为192个。
普通真双口RAM:利用vivado IP核生成的16K深度,73bit位宽的真双口RAM消耗的BRAM数为32个。即如果11个端口各自维护一张表共使用352个RAM
对比发现,多端口RAM比普通RAM节约了45%左右的BRAM资源
Multiport RAM 资源利用的优化
可能有的同学说,在某些大工程里面,192个BRAM还是有点多。下面我给出了一种降低BRAM资源消耗的方法。
首先我们把例化的ram array的位宽翻倍
原本:
(*ram_style="block"*)reg [DATA_WIDTH-1:0] bram [0:DEPTH-1];
现在:
(*ram_style="block"*)reg [DATA_WIDTH+DATA_WIDTH-1:0] bram [0:DEPTH-1];
(有同学会问了,这样资源消耗不是翻倍了吗?···别急!)
我们把需要写入RAM的数据,73位写data复制成两份,同时写进bram的高73位和低73位,地址不变,代码如下:
其中multi_wdata是我们要写进表中的73位表项:
在bram输出中,每两个端口共用一个143位的bram行根据使能情况赋值:
具体代码如下:
module multi_bram #( parameter ADDR_WIDTH = 14,
parameter DATA_WIDTH = 73,
parameter DEPTH = 16384)
(
input clk,
//input rst_n,
//read port
input re1,
input [ADDR_WIDTH-1:0] rd_addr1,
output wire [DATA_WIDTH-1:0] rd_data1_wire,
//read port
input re2,
input [ADDR_WIDTH-1:0] rd_addr2,
output wire [DATA_WIDTH-1:0] rd_data2_wire,
//read port
input re3,
input [ADDR_WIDTH-1:0] rd_addr3,
output wire [DATA_WIDTH-1:0] rd_data3_wire,
//read port
input re4,
input [ADDR_WIDTH-1:0] rd_addr4,
output wire [DATA_WIDTH-1:0] rd_data4_wire,
//read port
input re5,
input [ADDR_WIDTH-1:0] rd_addr5,
output wire [DATA_WIDTH-1:0] rd_data5_wire,
//read port
input re6,
input [ADDR_WIDTH-1:0] rd_addr6,
output wire [DATA_WIDTH-1:0] rd_data6_wire,
//read port
input re7,
input [ADDR_WIDTH-1:0] rd_addr7,
output wire [DATA_WIDTH-1:0] rd_data7_wire,
//read port
input re8,
input [ADDR_WIDTH-1:0] rd_addr8,
output wire [DATA_WIDTH-1:0] rd_data8_wire,
//read port
input re9,
input [ADDR_WIDTH-1:0] rd_addr9,
output wire [DATA_WIDTH-1:0] rd_data9_wire,
//read port
input re10,
input [ADDR_WIDTH-1:0] rd_addr10,
output wire [DATA_WIDTH-1:0] rd_data10_wire,
//read port
input re11,
input [ADDR_WIDTH-1:0] rd_addr11,
output wire [DATA_WIDTH-1:0] rd_data11_wire,
//write port
input we,
input [ADDR_WIDTH-1:0] wr_addr,
input [DATA_WIDTH+DATA_WIDTH-1:0] wr_data
);
(*ram_style="block"*)reg [DATA_WIDTH+DATA_WIDTH-1:0] bram [0:DEPTH-1];
reg [DATA_WIDTH+DATA_WIDTH-1:0] rd_data1 ;
reg [DATA_WIDTH+DATA_WIDTH-1:0] rd_data2 ;
reg [DATA_WIDTH+DATA_WIDTH-1:0] rd_data3 ;
reg [DATA_WIDTH+DATA_WIDTH-1:0] rd_data4 ;
reg [DATA_WIDTH+DATA_WIDTH-1:0] rd_data5 ;
reg [DATA_WIDTH+DATA_WIDTH-1:0] rd_data6 ;
reg [DATA_WIDTH+DATA_WIDTH-1:0] rd_data7 ;
reg [DATA_WIDTH+DATA_WIDTH-1:0] rd_data8 ;
reg [DATA_WIDTH+DATA_WIDTH-1:0] rd_data9 ;
reg [DATA_WIDTH+DATA_WIDTH-1:0] rd_data10;
reg [DATA_WIDTH+DATA_WIDTH-1:0] rd_data11;
//read1
assign rd_data1_wire = rd_data1[72:0] ;
assign rd_data2_wire = rd_data2[145:73];
always @(posedge clk)
begin
if (re1 & re2) begin
rd_data1 <= bram[rd_addr1];
rd_data2 <= bram[rd_addr2];
end
else
if(re1) begin
rd_data1 <= bram [rd_addr1];
end
else if (re2) begin
rd_data2 <= bram [rd_addr2];
end
end
//read2
assign rd_data3_wire = rd_data3[72:0];
assign rd_data4_wire = rd_data4[145:73];
always @(posedge clk)
begin
if (re3 & re4) begin
rd_data3 <= bram[rd_addr3];
rd_data4 <= bram[rd_addr4];
end
else if(re3)
rd_data3 <= bram [rd_addr3];
else if (re4) begin
rd_data4 <= bram[rd_addr4];
end
end
//read3
assign rd_data5_wire = rd_data5[72:0];
assign rd_data6_wire = rd_data6[145:73];
always @(posedge clk)
begin
if (re5 & re6) begin
rd_data5 <= bram[rd_addr5];
rd_data6 <= bram[rd_addr6];
end
else if(re5)
rd_data5 <= bram [rd_addr5];
else if (re6) begin
rd_data6 <= bram[rd_addr6];
end
end
assign rd_data7_wire = rd_data7[72:0];
assign rd_data8_wire = rd_data8[145:73];
always @(posedge clk)
begin
if (re7 & re8) begin
rd_data7 <= bram[rd_addr7];
rd_data8 <= bram[rd_addr8];
end
else if(re7)
rd_data7 <= bram [rd_addr7];
else if (re8) begin
rd_data8 <= bram[rd_addr8];
end
end
assign rd_data9_wire = rd_data9 [72:0];
assign rd_data10_wire = rd_data10[145:73];
always @(posedge clk)
begin
if (re9 & re10) begin
rd_data9 <= bram[rd_addr9];
rd_data10 <= bram[rd_addr10];
end
else if(re9)
rd_data9 <= bram [rd_addr9];
else if (re10) begin
rd_data10 <= bram[rd_addr10];
end
end
assign rd_data11_wire = rd_data11[72:0];
always @(posedge clk)
begin
if(re11) begin
rd_data11 <= bram [rd_addr11];
end
end
//write
always @(posedge clk)
begin
if(we)
bram[wr_addr]<=wr_data;
end
endmodule
资源评估
利用vivado综合后,消耗的资源如下
MultiportRAM:16K深度,146位宽的单口写,11口读的RAM消耗的BRAM数为112个。
普通真双口RAM:利用vivado IP核生成的16K深度,73bit位宽的真双口RAM消耗的BRAM数为32个。即如果11个端口各自维护一张表共使用352个RAM
对比发现,多端口RAM比普通RAM节约了68%左右的BRAM资源
防止读写冲突的组合逻辑设计(写优先)
代码原理,利用组合逻辑时序,当写入地址和读地址相同时,写入地址、数据正常进行,但读端口不对RAM进行读取,而是将写入端的数据直接赋值给读出端的数据。下一拍,即读写冲突结束后的下一拍,再读一拍RAM中的数据,使得读端口数据保持这一次读的结果(因为组合逻辑在读写冲突时没有真正读RAM,所以RAM输出data会保持上一次输出的data),但这一步不是必要的,纯粹为了好看。
代码如下:
//同一条总线不允许连续读两次
module multi_bram_top # (
parameter ADDR_WIDTH=14,
parameter DATA_WIDTH=73,
parameter DEPTH=16384
)
(
input clk,//200M
input rst_n,
//配表逻辑,write port
input multi_wr,//有效位为1写使能
input [ADDR_WIDTH-1:0] multi_waddr,
input [DATA_WIDTH-1:0] multi_wdata,
//11条总线组播查找表,read port
input multi_rd0 ,
input [ADDR_WIDTH-1:0] multi_raddr0 ,
output wire [DATA_WIDTH-1:0] multi_rdata0 ,
input multi_rd1 ,
input [ADDR_WIDTH-1:0] multi_raddr1 ,
output wire [DATA_WIDTH-1:0] multi_rdata1 ,
input multi_rd2 ,
input [ADDR_WIDTH-1:0] multi_raddr2 ,
output wire [DATA_WIDTH-1:0] multi_rdata2 ,
input multi_rd3 ,
input [ADDR_WIDTH-1:0] multi_raddr3 ,
output wire [DATA_WIDTH-1:0] multi_rdata3 ,
input multi_rd4 ,
input [ADDR_WIDTH-1:0] multi_raddr4 ,
output wire [DATA_WIDTH-1:0] multi_rdata4 ,
input multi_rd5 ,
input [ADDR_WIDTH-1:0] multi_raddr5 ,
output wire [DATA_WIDTH-1:0] multi_rdata5 ,
input multi_rd6 ,
input [ADDR_WIDTH-1:0] multi_raddr6 ,
output wire [DATA_WIDTH-1:0] multi_rdata6 ,
input multi_rd7 ,
input [ADDR_WIDTH-1:0] multi_raddr7 ,
output wire [DATA_WIDTH-1:0] multi_rdata7 ,
input multi_rd8 ,
input [ADDR_WIDTH-1:0] multi_raddr8 ,
output wire [DATA_WIDTH-1:0] multi_rdata8 ,
input multi_rd9 ,
input [ADDR_WIDTH-1:0] multi_raddr9 ,
output wire [DATA_WIDTH-1:0] multi_rdata9 ,
input multi_rd10,
input [ADDR_WIDTH-1:0] multi_raddr10,
output wire [DATA_WIDTH-1:0] multi_rdata10
);
wire [DATA_WIDTH-1:0] multi_rdata0_ram ;
wire [DATA_WIDTH-1:0] multi_rdata1_ram ;
wire [DATA_WIDTH-1:0] multi_rdata2_ram ;
wire [DATA_WIDTH-1:0] multi_rdata3_ram ;
wire [DATA_WIDTH-1:0] multi_rdata4_ram ;
wire [DATA_WIDTH-1:0] multi_rdata5_ram ;
wire [DATA_WIDTH-1:0] multi_rdata6_ram ;
wire [DATA_WIDTH-1:0] multi_rdata7_ram ;
wire [DATA_WIDTH-1:0] multi_rdata8_ram ;
wire [DATA_WIDTH-1:0] multi_rdata9_ram ;
wire [DATA_WIDTH-1:0]multi_rdata10_ram ;
wire [ADDR_WIDTH-1:0] multi_raddr0_ram ;
wire [ADDR_WIDTH-1:0] multi_raddr1_ram ;
wire [ADDR_WIDTH-1:0] multi_raddr2_ram ;
wire [ADDR_WIDTH-1:0] multi_raddr3_ram ;
wire [ADDR_WIDTH-1:0] multi_raddr4_ram ;
wire [ADDR_WIDTH-1:0] multi_raddr5_ram ;
wire [ADDR_WIDTH-1:0] multi_raddr6_ram ;
wire [ADDR_WIDTH-1:0] multi_raddr7_ram ;
wire [ADDR_WIDTH-1:0] multi_raddr8_ram ;
wire [ADDR_WIDTH-1:0] multi_raddr9_ram ;
wire [ADDR_WIDTH-1:0] multi_raddr10_ram;
wire multi_rd0_ram ;
wire multi_rd1_ram ;
wire multi_rd2_ram ;
wire multi_rd3_ram ;
wire multi_rd4_ram ;
wire multi_rd5_ram ;
wire multi_rd6_ram ;
wire multi_rd7_ram ;
wire multi_rd8_ram ;
wire multi_rd9_ram ;
wire multi_rd10_ram;
//输入地址打一拍
reg [DATA_WIDTH-1:0] multi_wdata_f;
reg [ADDR_WIDTH-1:0] multi_waddr_f;
reg multi_rd0_f ;
reg multi_rd1_f ;
reg multi_rd2_f ;
reg multi_rd3_f ;
reg multi_rd4_f ;
reg multi_rd5_f ;
reg multi_rd6_f ;
reg multi_rd7_f ;
reg multi_rd8_f ;
reg multi_rd9_f ;
reg multi_rd10_f;
reg [ADDR_WIDTH-1:0] multi_raddr0_f;
reg [ADDR_WIDTH-1:0] multi_raddr1_f;
reg [ADDR_WIDTH-1:0] multi_raddr2_f;
reg [ADDR_WIDTH-1:0] multi_raddr3_f;
reg [ADDR_WIDTH-1:0] multi_raddr4_f;
reg [ADDR_WIDTH-1:0] multi_raddr5_f;
reg [ADDR_WIDTH-1:0] multi_raddr6_f;
reg [ADDR_WIDTH-1:0] multi_raddr7_f;
reg [ADDR_WIDTH-1:0] multi_raddr8_f;
reg [ADDR_WIDTH-1:0] multi_raddr9_f;
reg [ADDR_WIDTH-1:0] multi_raddr10_f;
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
multi_rd0_f <='b0;
multi_rd1_f <='b0;
multi_rd2_f <='b0;
multi_rd3_f <='b0;
multi_rd4_f <='b0;
multi_rd5_f <='b0;
multi_rd6_f <='b0;
multi_rd7_f <='b0;
multi_rd8_f <='b0;
multi_rd9_f <='b0;
multi_rd10_f<='b0;
multi_waddr_f<='b0;
multi_raddr0_f <='b0;
multi_raddr1_f <='b0;
multi_raddr2_f <='b0;
multi_raddr3_f <='b0;
multi_raddr4_f <='b0;
multi_raddr5_f <='b0;
multi_raddr6_f <='b0;
multi_raddr7_f <='b0;
multi_raddr8_f <='b0;
multi_raddr9_f <='b0;
multi_raddr10_f<='b0;
multi_wdata_f<='b0;
end else begin
multi_rd0_f <=multi_rd0;
multi_rd1_f <=multi_rd1;
multi_rd2_f <=multi_rd2;
multi_rd3_f <=multi_rd3;
multi_rd4_f <=multi_rd4;
multi_rd5_f <=multi_rd5;
multi_rd6_f <=multi_rd6;
multi_rd7_f <=multi_rd7;
multi_rd8_f <=multi_rd8;
multi_rd9_f <=multi_rd9;
multi_rd10_f<=multi_rd10;
multi_waddr_f<=multi_waddr;
multi_raddr0_f <= multi_raddr0;
multi_raddr1_f <= multi_raddr1;
multi_raddr2_f <= multi_raddr2;
multi_raddr3_f <= multi_raddr3;
multi_raddr4_f <= multi_raddr4;
multi_raddr5_f <= multi_raddr5;
multi_raddr6_f <= multi_raddr6;
multi_raddr7_f <= multi_raddr7;
multi_raddr8_f <= multi_raddr8;
multi_raddr9_f <= multi_raddr9;
multi_raddr10_f<=multi_raddr10;
multi_wdata_f <=multi_wdata;
end
end
//防止读写冲突,且为写优先逻辑
assign multi_rdata0 =(multi_raddr0_f ==multi_waddr_f && multi_raddr0_f !='b0 )?multi_wdata_f:multi_rdata0_ram ;
assign multi_rdata1 =(multi_raddr1_f ==multi_waddr_f && multi_raddr1_f !='b0 )?multi_wdata_f:multi_rdata1_ram ;
assign multi_rdata2 =(multi_raddr2_f ==multi_waddr_f && multi_raddr2_f !='b0 )?multi_wdata_f:multi_rdata2_ram ;
assign multi_rdata3 =(multi_raddr3_f ==multi_waddr_f && multi_raddr3_f !='b0 )?multi_wdata_f:multi_rdata3_ram ;
assign multi_rdata4 =(multi_raddr4_f ==multi_waddr_f && multi_raddr4_f !='b0 )?multi_wdata_f:multi_rdata4_ram ;
assign multi_rdata5 =(multi_raddr5_f ==multi_waddr_f && multi_raddr5_f !='b0 )?multi_wdata_f:multi_rdata5_ram ;
assign multi_rdata6 =(multi_raddr6_f ==multi_waddr_f && multi_raddr6_f !='b0 )?multi_wdata_f:multi_rdata6_ram ;
assign multi_rdata7 =(multi_raddr7_f ==multi_waddr_f && multi_raddr7_f !='b0 )?multi_wdata_f:multi_rdata7_ram ;
assign multi_rdata8 =(multi_raddr8_f ==multi_waddr_f && multi_raddr8_f !='b0 )?multi_wdata_f:multi_rdata8_ram ;
assign multi_rdata9 =(multi_raddr9_f ==multi_waddr_f && multi_raddr9_f !='b0 )?multi_wdata_f:multi_rdata9_ram ;
assign multi_rdata10=(multi_raddr10_f==multi_waddr_f && multi_raddr10_f!='b0 )?multi_wdata_f:multi_rdata10_ram;
assign multi_raddr0_ram =(multi_raddr0_f ==multi_waddr_f && multi_raddr0_f !='b0 )?multi_waddr_f: multi_raddr0;
assign multi_raddr1_ram =(multi_raddr1_f ==multi_waddr_f && multi_raddr1_f !='b0 )?multi_waddr_f: multi_raddr1;
assign multi_raddr2_ram =(multi_raddr2_f ==multi_waddr_f && multi_raddr2_f !='b0 )?multi_waddr_f: multi_raddr2;
assign multi_raddr3_ram =(multi_raddr3_f ==multi_waddr_f && multi_raddr3_f !='b0 )?multi_waddr_f: multi_raddr3;
assign multi_raddr4_ram =(multi_raddr4_f ==multi_waddr_f && multi_raddr4_f !='b0 )?multi_waddr_f: multi_raddr4;
assign multi_raddr5_ram =(multi_raddr5_f ==multi_waddr_f && multi_raddr5_f !='b0 )?multi_waddr_f: multi_raddr5;
assign multi_raddr6_ram =(multi_raddr6_f ==multi_waddr_f && multi_raddr6_f !='b0 )?multi_waddr_f: multi_raddr6;
assign multi_raddr7_ram =(multi_raddr7_f ==multi_waddr_f && multi_raddr7_f !='b0 )?multi_waddr_f: multi_raddr7;
assign multi_raddr8_ram =(multi_raddr8_f ==multi_waddr_f && multi_raddr8_f !='b0 )?multi_waddr_f: multi_raddr8;
assign multi_raddr9_ram =(multi_raddr9_f ==multi_waddr_f && multi_raddr9_f !='b0 )?multi_waddr_f: multi_raddr9;
assign multi_raddr10_ram=(multi_raddr10_f==multi_waddr_f && multi_raddr10_f!='b0 )?multi_waddr_f: multi_raddr10;
assign multi_rd0_ram =(multi_raddr0 ==multi_waddr && multi_raddr0!='b0 )? 1'b0:((multi_raddr0_f ==multi_waddr_f && multi_raddr0_f !='b0 )?multi_rd0_f :multi_rd0 );
assign multi_rd1_ram =(multi_raddr1 ==multi_waddr && multi_raddr1!='b0 )? 1'b0:((multi_raddr1_f ==multi_waddr_f && multi_raddr1_f !='b0 )?multi_rd1_f :multi_rd1 );
assign multi_rd2_ram =(multi_raddr2 ==multi_waddr && multi_raddr2!='b0 )? 1'b0:((multi_raddr2_f ==multi_waddr_f && multi_raddr2_f !='b0 )?multi_rd2_f :multi_rd2 );
assign multi_rd3_ram =(multi_raddr3 ==multi_waddr && multi_raddr3!='b0 )? 1'b0:((multi_raddr3_f ==multi_waddr_f && multi_raddr3_f !='b0 )?multi_rd3_f :multi_rd3 );
assign multi_rd4_ram =(multi_raddr4 ==multi_waddr && multi_raddr4!='b0 )? 1'b0:((multi_raddr4_f ==multi_waddr_f && multi_raddr4_f !='b0 )?multi_rd4_f :multi_rd4 );
assign multi_rd5_ram =(multi_raddr5 ==multi_waddr && multi_raddr5!='b0 )? 1'b0:((multi_raddr5_f ==multi_waddr_f && multi_raddr5_f !='b0 )?multi_rd5_f :multi_rd5 );
assign multi_rd6_ram =(multi_raddr6 ==multi_waddr && multi_raddr6!='b0 )? 1'b0:((multi_raddr6_f ==multi_waddr_f && multi_raddr6_f !='b0 )?multi_rd6_f :multi_rd6 );
assign multi_rd7_ram =(multi_raddr7 ==multi_waddr && multi_raddr7!='b0 )? 1'b0:((multi_raddr7_f ==multi_waddr_f && multi_raddr7_f !='b0 )?multi_rd7_f :multi_rd7 );
assign multi_rd8_ram =(multi_raddr8 ==multi_waddr && multi_raddr8!='b0 )? 1'b0:((multi_raddr8_f ==multi_waddr_f && multi_raddr8_f !='b0 )?multi_rd8_f :multi_rd8 );
assign multi_rd9_ram =(multi_raddr9 ==multi_waddr && multi_raddr9!='b0 )? 1'b0:((multi_raddr9_f ==multi_waddr_f && multi_raddr9_f !='b0 )?multi_rd9_f :multi_rd9 );
assign multi_rd10_ram=(multi_raddr10==multi_waddr && multi_raddr1!='b0 )? 1'b0:((multi_raddr10_f==multi_waddr_f && multi_raddr10_f!='b0 )?multi_rd10_f:multi_rd10);
//手写bram存在读写冲突,需要在外部解决
//re,raddr一拍,下一拍出数据
//we+waddr+wdata一拍出
multi_bram # (
.ADDR_WIDTH(ADDR_WIDTH),
.DATA_WIDTH(DATA_WIDTH),
.DEPTH(DEPTH)
)
bram_in_fst (
.clk(clk),
// .rst_n(rst_n),
//11条总线的读
.re1 ( multi_rd0_ram ),
.rd_addr1 (multi_raddr0_ram ),
.rd_data1_wire (multi_rdata0_ram ),
.re2 ( multi_rd1_ram ),
.rd_addr2 (multi_raddr1_ram ),
.rd_data2_wire (multi_rdata1_ram ),
.re3 ( multi_rd2_ram ),
.rd_addr3 (multi_raddr2_ram ),
.rd_data3_wire (multi_rdata2_ram ),
.re4 ( multi_rd3_ram ),
.rd_addr4 (multi_raddr3_ram ),
.rd_data4_wire (multi_rdata3_ram ),
.re5 ( multi_rd4_ram ),
.rd_addr5 (multi_raddr4_ram ),
.rd_data5_wire (multi_rdata4_ram ),
.re6 ( multi_rd5_ram ),
.rd_addr6 (multi_raddr5_ram ),
.rd_data6_wire (multi_rdata5_ram ),
.re7 ( multi_rd6_ram ),
.rd_addr7 (multi_raddr6_ram ),
.rd_data7_wire (multi_rdata6_ram ),
.re8 ( multi_rd7_ram ),
.rd_addr8 (multi_raddr7_ram ),
.rd_data8_wire (multi_rdata7_ram ),
.re9 ( multi_rd8_ram ),
.rd_addr9 (multi_raddr8_ram ),
.rd_data9_wire (multi_rdata8_ram ),
.re10 ( multi_rd9_ram ),
.rd_addr10(multi_raddr9_ram ),
.rd_data10_wire(multi_rdata9_ram ),
.re11 ( multi_rd10_ram),
.rd_addr11(multi_raddr10_ram),
.rd_data11_wire(multi_rdata10_ram),
//配表
.we ( multi_wr),
.wr_addr (multi_waddr),
.wr_data ({multi_wdata,multi_wdata})
);
endmodule
读写冲突的仿真结果如下:
仿真和时序
所有写端口都是一拍写入。读端口是第一拍读使能,读地址,第二拍读出数据。
单口写数据
单端口读数据
多口读相同数据
多口同时读不同数据
作者:云归山_Lin
原文:CSDN
相关文章推荐
- DDR 学习时间 (Part C - 6):DFI 协议功能-MC 停止 DRAM 时钟
- DDR 学习时间 (Part S - 1):运行 Micron DDR3 仿真模型
- DDR 学习时间: DDR 开源实现 高云 GW2A FPGA 的 DDR3 低延迟控制器
- DDR 学习时间 (Part I - OS1):DDR IP 开源实现 DDR5 PHY 数据通路
- IC 工程师视角:恒玄科技 2023 年报
更多FPGA干货请关注FPGA的逻辑技术专栏。欢迎添加极术小姐姐微信(id:aijishu20)加入技术交流群,请备注研究方向。