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

这篇具有很好参考价值的文章主要介绍了【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),二者都是写同步的。主要区别在于读数据,前者为异步,后者为同步的。

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

        下面给出一种手写多端口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综合后,消耗的资源如下

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

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位表项:

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

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

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

具体代码如下:

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综合后,消耗的资源如下

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

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

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

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

仿真和时序

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

单口写数据

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

单端口读数据

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

多口读相同数据

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

多口同时读不同数据

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

        文章来源地址https://www.toymoban.com/news/detail-858436.html

到了这里,关于【FPGA/数字IC】Multiport RAM,多读多写寄存器-——基于FPGA BRAM的多端口地址查找表与FPGA BRAM的资源分析的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 数字IC/FPGA面试题目合集解析(一)

    1,计算题:计算该触发器等效的建立保持时间(西安某Fabless面试笔试题) 2,计算题:计算组合逻辑的延时时间范围 3,选择题:Which of following ways cannot be used to improve timing of a hold violation path 问题:原触发器,即对于D点的建立时间,保持时间均为2ns,先由于存在线延时,对

    2024年02月06日
    浏览(47)
  • 【数字 IC / FPGA】 有关建立/保持时间计算的思考

    最近准备一些数字IC的机试,刷到了一些有关静态时序分析的题目。有一些比较经典的题目,在这里整理分享一下。 有什么疑问可以在评论区交流~互相进步 假设时钟周期为Tcycle,Tsetup,Thold分别为触发器建立保持时间,为保证时需满足要求,需要满足什么样的时序关系?(T1~

    2024年02月06日
    浏览(47)
  • 数字IC/FPGA面试宝典--经典60道例题详解

    1.关于亚稳态的描述错误的是(A) A、多用几级寄存器打拍可以消除亚稳态。 B、亚稳态是极不稳定的,理论上来讲处在亚稳态的时间可以无限长。 C、亚稳态稳定到0或者1,是随机的,与输入没有必然的关系。 D、如果数据传输中不满足触发器的建文时间Tsu和保持时间Th,可能

    2024年01月16日
    浏览(54)
  • 【数字IC/FPGA】Verilog中的force和release

    在Verilog中,将 force 用于variable会覆盖掉 过程赋值 ,或者assign引导的 连续(procedural assign)赋值 ,直到 release 。 下面通过一个简单的例子展示其用法: 加法器代码 测试平台代码(主要用于产生激励) 如上所示,正常情况下,u_adder模块的a和b端口由testbench中的a和b信号驱动,

    2024年02月09日
    浏览(46)
  • 【数字IC/FPGA】百度昆仑芯手撕代码--累加器

    已知一个加法器IP,其功能是计算两个数的和,但这个和延迟两个周期才会输出。现在有一串连续的数据输入,每个周期都不间断,试问最少需要例化几个上述的加法器IP,才可以实现累加的功能。 由于加法器两个周期后才能得到结果(再将该结果作为加法器的输入进行累加

    2024年02月09日
    浏览(40)
  • 【数字IC/FPGA】什么是无符号数?什么是有符号数?

    虽然在日常生活中,我们已经习惯了使用10进制数字,但在由数字电路构成的数字世界中,2进制才是效率更高的选择。 10进制(decimal)计数法(一般也叫阿拉伯计数法)是在日常生活中使用得最多的一种计数法,它是一种 位值记数法 (positional notation)。位值计数法的意思是

    2024年04月09日
    浏览(43)
  • 应届生谈薪技巧和注意事项,怎么为自己多争取1~2k(FPGA,芯片谈薪,数字IC,嵌入式,模拟IC,FPGA探索者)

      找工作的终极目标:谈薪!谈高薪!今天【FPGA探索者】给大家分享一下谈薪的技巧和注意事项,别被HR轻易压价。   本文适用人群: 应届毕业生 。 FPGA探索者 FPGA+数字IC笔试面试,无线通信物理层及数字信号处理,半导体芯片行业求职,校招社招实习,职场趣事,行业动

    2024年01月25日
    浏览(58)
  • IC验证-寄存器专项测试

    关键点:对粘连的理解和验证 DUT初始化后,读取寄存器值,判断读取的值是否等于设计时所配置的初始值,不等则证明寄存器有错。 通常,对一个寄存器进行赋值,不会影响到其他寄存器值,但是当发生寄存器之间的粘连时,寄存器之间就不再有独立性,对其中某个寄存器

    2024年02月09日
    浏览(46)
  • 【IC前端虚拟项目】MVU寄存器文档编写与RTL代码生成

    【IC前端虚拟项目】数据搬运指令处理模块前端实现虚拟项目说明-CSDN博客 兜兜转转又回到了寄存器,但是没有办法,虚拟项目肯定要贴近真实真实项目的,而一个真实项目里不管是做模块设计还是SOC设计,寄存器总是难以避免的。关于寄存器的生成使用和验证,有一个专栏

    2024年01月18日
    浏览(55)
  • 【单周期CPU】LoongArch | 32位寄存器DR | 32位的程序计数器PC | 通用寄存器堆Registers | 32位RAM存储器

    前言: 本章内容主要是演示在vivado下利用Verilog语言进行单周期简易CPU的设计。一步一步自己实现模型机的设计。本章先介绍单周期简易CPU中基本时序逻辑部件设计。 💻环境:一台内存4GB以上,装有64位Windows操作系统和Vivado 2017.4以上版本软件的PC机。 💎本章所采用的指令为

    2024年02月12日
    浏览(49)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包