【FPGA/数字IC】Multiport RAM,多读多写寄存器-——基于FPGA BRAM的多端口地址查找表与FPGA BRAM的资源分析

目录

背景

手写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 portinput  re1,input  [ADDR_WIDTH-1:0] rd_addr1,output reg [DATA_WIDTH-1:0] rd_data1,//read portinput                            re2,input  [ADDR_WIDTH-1:0]     rd_addr2,output reg [DATA_WIDTH-1:0] rd_data2,//read portinput                            re3,input  [ADDR_WIDTH-1:0]     rd_addr3,output reg [DATA_WIDTH-1:0] rd_data3,//read portinput                            re4,input  [ADDR_WIDTH-1:0]     rd_addr4,output reg [DATA_WIDTH-1:0] rd_data4,//read portinput                            re5,input  [ADDR_WIDTH-1:0]     rd_addr5,output reg [DATA_WIDTH-1:0] rd_data5,//read portinput                            re6,input  [ADDR_WIDTH-1:0]     rd_addr6,output reg [DATA_WIDTH-1:0] rd_data6,//read portinput                            re7,input  [ADDR_WIDTH-1:0]     rd_addr7,output reg [DATA_WIDTH-1:0] rd_data7,//read portinput                            re8,input  [ADDR_WIDTH-1:0]     rd_addr8,output reg [DATA_WIDTH-1:0] rd_data8,//read portinput                            re9,input  [ADDR_WIDTH-1:0]     rd_addr9,output reg [DATA_WIDTH-1:0] rd_data9,//read portinput                            re10,input  [ADDR_WIDTH-1:0]     rd_addr10,output reg [DATA_WIDTH-1:0] rd_data10,//read portinput                            re11,input  [ADDR_WIDTH-1:0]     rd_addr11,output reg [DATA_WIDTH-1:0] rd_data11,//write portinput  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];//read1always @(posedge clk)beginif(re1)rd_data1 <= bram[rd_addr1];elserd_data1 <= rd_data1;end//read2always @(posedge clk)beginif(re2)rd_data2 <= bram[rd_addr2];elserd_data2 <= rd_data2;end//read3always @(posedge clk)beginif(re3)rd_data3 <= bram[rd_addr3];elserd_data3 <= rd_data3;end//read4always @(posedge clk)beginif(re4)rd_data4 <= bram[rd_addr4];elserd_data4 <= rd_data4;end//read5always @(posedge clk)beginif(re5)rd_data5 <= bram[rd_addr5];elserd_data5 <= rd_data5;end//read6always @(posedge clk)beginif(re6)rd_data6 <= bram[rd_addr6];elserd_data6 <= rd_data6;end//read7always @(posedge clk)beginif(re7)rd_data7 <= bram[rd_addr7];elserd_data7 <= rd_data7;end//read8always @(posedge clk)beginif(re8)rd_data8 <= bram[rd_addr8];elserd_data8 <= rd_data8;end//read9always @(posedge clk)beginif(re9)rd_data9 <= bram[rd_addr9];elserd_data9 <= rd_data9;end//read10always @(posedge clk)beginif(re10)rd_data10 <= bram[rd_addr10];elserd_data10 <= rd_data10;end//read11always @(posedge clk)beginif(re11)rd_data11 <= bram[rd_addr11];elserd_data11 <= rd_data11;end//writealways @(posedge clk)beginif(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位表项:

ffc700645898444aaf14b2df7d7b4d2a.png

在bram输出中,每两个端口共用一个143位的bram行根据使能情况赋值:

98a256e31e03460ea98bcb48378b5fb6.png

具体代码如下:

module multi_bram #( parameter ADDR_WIDTH = 14,parameter DATA_WIDTH = 73,parameter DEPTH = 16384)(input  clk,//input  rst_n,//read portinput  re1,input  [ADDR_WIDTH-1:0] rd_addr1,output wire [DATA_WIDTH-1:0] rd_data1_wire,//read portinput                            re2,input  [ADDR_WIDTH-1:0]     rd_addr2,output wire [DATA_WIDTH-1:0] rd_data2_wire,//read portinput                            re3,input  [ADDR_WIDTH-1:0]     rd_addr3,output wire [DATA_WIDTH-1:0] rd_data3_wire,//read portinput                            re4,input  [ADDR_WIDTH-1:0]     rd_addr4,output wire [DATA_WIDTH-1:0] rd_data4_wire,//read portinput                            re5,input  [ADDR_WIDTH-1:0]     rd_addr5,output wire [DATA_WIDTH-1:0] rd_data5_wire,//read portinput                            re6,input  [ADDR_WIDTH-1:0]     rd_addr6,output wire [DATA_WIDTH-1:0] rd_data6_wire,//read portinput                            re7,input  [ADDR_WIDTH-1:0]     rd_addr7,output wire [DATA_WIDTH-1:0] rd_data7_wire,//read portinput                            re8,input  [ADDR_WIDTH-1:0]     rd_addr8,output wire [DATA_WIDTH-1:0] rd_data8_wire,//read portinput                            re9,input  [ADDR_WIDTH-1:0]     rd_addr9,output wire [DATA_WIDTH-1:0] rd_data9_wire,//read portinput                            re10,input  [ADDR_WIDTH-1:0]     rd_addr10,output wire [DATA_WIDTH-1:0] rd_data10_wire,//read portinput                            re11,input  [ADDR_WIDTH-1:0]     rd_addr11,output wire [DATA_WIDTH-1:0] rd_data11_wire,//write portinput  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;//read1assign rd_data1_wire = rd_data1[72:0]  ;assign rd_data2_wire = rd_data2[145:73];always @(posedge clk)beginif (re1 & re2) beginrd_data1 <=  bram[rd_addr1];rd_data2 <=  bram[rd_addr2];endelse if(re1) beginrd_data1 <=  bram [rd_addr1];endelse if (re2) beginrd_data2 <= bram [rd_addr2];end end//read2assign rd_data3_wire = rd_data3[72:0];assign rd_data4_wire = rd_data4[145:73];always @(posedge clk)beginif (re3 & re4) beginrd_data3 <=  bram[rd_addr3];rd_data4 <=  bram[rd_addr4];endelse if(re3)rd_data3 <=  bram [rd_addr3];else if (re4) beginrd_data4 <=  bram[rd_addr4];endend//read3assign rd_data5_wire = rd_data5[72:0];assign rd_data6_wire = rd_data6[145:73];always @(posedge clk)beginif (re5 & re6) beginrd_data5 <=  bram[rd_addr5];rd_data6 <=  bram[rd_addr6];endelse if(re5)rd_data5 <=  bram [rd_addr5];else if (re6) beginrd_data6 <=  bram[rd_addr6];endendassign rd_data7_wire = rd_data7[72:0];assign rd_data8_wire = rd_data8[145:73];always @(posedge clk)beginif (re7 & re8) beginrd_data7 <=  bram[rd_addr7];rd_data8 <=  bram[rd_addr8];endelse if(re7)rd_data7 <=  bram [rd_addr7];else if (re8) beginrd_data8 <=  bram[rd_addr8];endendassign rd_data9_wire = rd_data9  [72:0];assign rd_data10_wire = rd_data10[145:73];always @(posedge clk)beginif (re9 & re10) beginrd_data9 <=  bram[rd_addr9];rd_data10 <=  bram[rd_addr10];endelse if(re9)rd_data9 <=  bram [rd_addr9];else if (re10) beginrd_data10 <=  bram[rd_addr10];endendassign rd_data11_wire = rd_data11[72:0];always @(posedge clk)beginif(re11) beginrd_data11 <=  bram [rd_addr11];endend//writealways @(posedge clk)beginif(we)bram[wr_addr]<=wr_data;end
endmodule

资源评估

        利用vivado综合后,消耗的资源如下

81e22cb4084141b4840565c73719b0de.png

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,//200Minput  rst_n,//配表逻辑,write portinput                           multi_wr,//有效位为1写使能input  [ADDR_WIDTH-1:0]      multi_waddr,input  [DATA_WIDTH-1:0]      multi_wdata,//11条总线组播查找表,read portinput                           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) beginif (!rst_n) beginmulti_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 beginmulti_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;endend//防止读写冲突,且为写优先逻辑
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

读写冲突的仿真结果如下:

838ce99bce2c4271acc02e6d212dca68.png

仿真和时序

所有写端口都是一拍写入。读端口是第一拍读使能,读地址,第二拍读出数据。

单口写数据

17f1dc65999a46e0a5e6d2ec6e6efbaa.png

单端口读数据

19a1ccbaa0ce4d8dab0c97c5f57074cd.png

多口读相同数据

5701dc4c3068470a870449993210150b.png

多口同时读不同数据

53750207b73d41e09f4c7d5999e94975.png

        

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/691017.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

商务英语口语成人考级外语培训之BECkao考级口语篇

在口语考试中&#xff0c;不管实际内容你能说出多少&#xff0c;但准备一些套话&#xff0c;至少还能撑撑场子你们说是不是&#xff1f; 内容阐述 描述事实 1.Im going to describe/present/explain/give you some information about... 2.Id like to say a few words about...…

探索全画面塑料焊接透光率检测仪的科技魅力

在精密工业和科研领域中&#xff0c;对材料的光学性能有着严格的要求。全画面塑料焊接透光率检测仪是一种先进的设备&#xff0c;它能够精确测量塑料焊接接头的透光率&#xff0c;确保焊接质量符合高标准。本文将详细介绍这一设备的特点、工作原理以及它在实际应用中的重要性。…

简易留言板

目录 前端实现 数据库的使用 创建数据表 创建项目 连接数据库 后端实现 接口定义 持久层 业务逻辑层 控制层 前端代码完善 留言板是一个常见的功能&#xff0c;在本篇文章中&#xff0c;将实现一个简易的留言板&#xff1a; 页面中能够显示所有留言内容&#xff0c…

C++类和对象(5)static修饰的静态成员变量函数

1.静态成员函数和静态成员变量的引入 &#xff08;1&#xff09;我们通过以下面的这个例子逐步引出静态的成员变量和成员函数&#xff1a; 我们自己定义一个类&#xff0c;使用这个类创建对象&#xff0c;我们应该如何判断在这个程序执行的过程中&#xff0c;创建了多少个对象…

深度解析Nginx:高性能Web服务器的奥秘(上)

&#x1f407;明明跟你说过&#xff1a;个人主页 &#x1f3c5;个人专栏&#xff1a;《洞察之眼&#xff1a;ELK监控与可视化》&#x1f3c5; &#x1f516;行路有良友&#xff0c;便是天堂&#x1f516; 目录 一、前言 1、Nginx概述 2、Nginx的历史与发展 3、Nginx的…

软件全套资料梳理(需求、开发、实施、运维、安全、测试、交付、认证、评审、投标等)

软件全套精华资料包清单部分文件列表&#xff1a; 工作安排任务书&#xff0c;可行性分析报告&#xff0c;立项申请审批表&#xff0c;产品需求规格说明书&#xff0c;需求调研计划&#xff0c;用户需求调查单&#xff0c;用户需求说明书&#xff0c;概要设计说明书&#xff0c…

根据docker部署nginx并且实现https

目录 一、Docker中启用HTTPS有几个重要的原因 二、https介绍 三、https过程 四、安装docker-20.10.18 五、如何获取证书 通过阿里云获取证书 六、docker部署nginx并且实现https 6.1准备证书 6.2准备nginx.conf 和 index.html文件 6.3生成容器 6.4浏览器验证证书 一、…

2024年4月24日华为春招实习试题【三题】-题目+题解+在线评测,2024.4.24,华为机试

2024年4月24日华为春招实习试题【三题】-题目题解在线评测&#xff0c;2024.4.24&#xff0c;华为机试 &#x1f3e9;题目一描述&#xff1a;输入格式输出格式样例1样例2样例3数据范围解题思路一&#xff1a;dfs解题思路二&#xff1a;直接二分查找哇&#xff01;解题思路三&am…

MySQL数据库的初始化(创建库、创建表、向数据库添加测试数据)

MySQL数据库的初始化&#xff08;创建库、创建表、向数据库添加测试数据&#xff09; MySQL数据库简介MySQL创建一个新的数据库MySQL创建一张新的数据表简单&#xff08;设置&#xff09;表复杂&#xff08;设置&#xff09;表 填充测试数据SQL语句mysql>模式下输入的每句sq…

技术爱好者必看:如何用AI问答API彻底改变用户体验!

AI 问答 API 对接说明 我们知道&#xff0c;市面上一些问答 API 的对接还是相对没那么容易的&#xff0c;比如说 OpenAI 的 Chat Completions API&#xff0c;它有一个 messages 字段&#xff0c;如果要完成连续对话&#xff0c;需要我们把所有的上下文历史全部传递&#xff0…

FreeRtos内核源码分析(九)——协程

目录 一、协程简介 二、协程工作机制 2.1 协程控制块结构 2.2 协程管理方式 2.3 协程调度方式 2.4 协程通信机制 三、协程状态及状态切换 3.1 协程状态 3.2 状态切换 四、协程创建 五、协程调度分析 5.1 源码分析 5.2 逻辑图分析 六、协程通信 6.1 协程发送消息…

如何清除DNS缓存,刷新DNS

大家在使用域名访问服务器的时候&#xff0c;经常会遇到一个问题&#xff0c;同一个局域网里的两台电脑&#xff0c;一台可以访问而另一台不行。这是为什么呢&#xff1f;这里我要和大家说下DNS缓存的问题&#xff0c;顾名思义&#xff0c;每台电脑都有DNS缓存&#xff0c;在域…

ElasticSearch 8.X 源码导入idea并配置环境启动调试(mac环境)

主要是用于自己记录配置流程 环境 IntelliJ IDEA 2024.1.1 (Community Edition) jdk17&#xff08;可以安装jenv管理&#xff09; macos 14.4.1 gradle 8.5 资源准备 先在官网下载elasticsearch源码&#xff08;GitHub - elastic/elasticsearch: Free and Open, Distrib…

webservice和TCP类型接口测试

1.webservice类型接口 1.1.webservice类型接口介绍 Web服务&#xff08;WebService&#xff09;是一种基于网络的应用程序接口&#xff08;API&#xff09;&#xff0c;可通过网络来进行通信和交互。它们使用标准化的协议和格式来进行通信&#xff0c;最常见的是使用XML&#…

AI图书推荐:利用生成式AI实现业务流程超自动化

《利用生成式AI实现业务流程超自动化》&#xff08;Hyperautomation with Generative AI&#xff09;这本书探索了广泛的用例和示例&#xff0c;展示了超自动化在不同行业、领域和特定部门的多样化应用&#xff0c; 让您熟悉UiPath、Automation Anywhere和IBM等流行工具和平台&…

LeetCode 105.从前序与中序遍历序列构造二叉树

LeetCode 105.从前序与中序遍历序列构造二叉树 1、题目 题目链接&#xff1a;105. 从前序与中序遍历序列构造二叉树 给定两个整数数组 preorder 和 inorder &#xff0c;其中 preorder 是二叉树的先序遍历&#xff0c; inorder 是同一棵树的中序遍历&#xff0c;请构造二叉树…

MVCC 详解

介绍 MVCC&#xff0c;全称 Multi-Version Concurrency Control&#xff0c;即多版本并发控制 MVCC的目的主要是为了提高数据库并发性能&#xff0c;用更好的方式去处理读-写冲突&#xff0c;做到即使有读写冲突时&#xff0c;也能做到不加锁。 这里的多版本指的是数据库中同时…

【GO】go语言中的HTTP标准库 - http编程

上一节已经学习了HTTP的基础知识&#xff0c;本章将学习关于go语言的HTTP编程&#xff0c;最重要的是掌握 net/http 包的用法&#xff0c;以及如何自己编写一个简单的Web服务端&#xff0c;通过客户端访问Server端等。 编写简单的Web 服务器 http.ListenAndServe 启动 Http S…

Unity开发中导弹路径散射的原理与实现

Unity开发中导弹路径散射的原理与实现 前言逻辑原理代码实现导弹自身脚本外部控制脚本 应用效果结语 前言 前面我们学习了导弹的追踪的效果&#xff0c;但是在动画或游戏中&#xff0c;我们经常可以看到导弹发射后的弹道是不规则的&#xff0c;扭扭曲曲的飞行&#xff0c;然后击…

Reactor Netty 其他-响应式编程-018

&#x1f917; ApiHug {Postman|Swagger|Api...} 快↑ 准√ 省↓ GitHub - apihug/apihug.com: All abou the Apihug apihug.com: 有爱&#xff0c;有温度&#xff0c;有质量&#xff0c;有信任ApiHug - API design Copilot - IntelliJ IDEs Plugin | Marketplace The Nex…