一、背景
在多端口交換機的設計中,交換機的每個端口都會各自維護一張查找表,數據幀進入到交換機后,需要進行查表和轉發。但隨著端口數量和表項需求的增加,每個端口都單獨維護一張表使得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:
(*ram_style="block"*)reg [DATA_WIDTH-1:0] bram [0:DEPTH-1]; /*-------------復制讀端口11份---------------*/ always @(posedge clk) begin if(re1) rd_data1 <= bram[rd_addr1]; else rd_data1 <= rd_data1; ????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。
對比發現,在滿足11個端口同時讀地址查找表的條件下,多端口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];
(有同學會問了,這樣資源消耗不是翻倍了嗎?···別急?。?/p>
我們把需要寫入RAM的數據,73位寫data復制成兩份,同時寫進bram的高73位和低73位,地址不變,其中multi_wdata是我們要寫進表中的73位表項,代碼如下:
//bram例化模塊的寫使能、地址和數據 .we ( multi_wr), .wr_addr (multi_waddr), .wr_data({multi_wdata,multi_wdata})
在bram輸出中,每兩個端口共用一個143位的bram行,并根據使能情況賦值:
//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
***補充:具體代碼在文章開頭鏈接
資源評估
利用vivado綜合實現后,消耗的資源如下
MultiportRAM:16K深度,146位寬的單口寫,11口讀的RAM消耗的BRAM數為112個。
普通真雙口RAM:利用vivado IP核生成的16K深度,73bit位寬的真雙口RAM消耗的BRAM數為32個。即如果11個端口各自維護一張表共使用352個RAM
對比發現,在滿足11個端口同時讀地址查找表的條件下,多端口RAM比普通RAM節約了68%左右的BRAM資源
四、防止讀寫沖突的組合邏輯設計(寫優先)
代碼原理,利用組合邏輯時序,當寫入地址和讀地址相同時,寫入地址、數據正常進行,但讀端口不對RAM進行讀取,而是將寫入端的數據直接賦值給讀出端的數據。
下一拍,即讀寫沖突結束后的下一拍,再讀一拍RAM中的數據,使得讀端口數據保持這一次讀的結果(因為組合邏輯在讀寫沖突時沒有真正讀RAM,所以RAM輸出data會保持上一次輸出的data),但這一步不是必要的,純粹為了好看。
部分代碼如下:
//防止讀寫沖突,且為寫優先邏輯 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);
***補充:具體代碼在文章開頭鏈接
讀寫沖突的仿真結果如下:
五、Multiport RAM仿真和時序
所有寫端口都是一拍寫入。讀端口是第一拍讀使能,讀地址,第二拍讀出數據。
1.單口寫數據
2.單端口讀數據
3.多口讀相同數據
4.多口同時讀不同數據
-
FPGA
+關注
關注
1620文章
21507瀏覽量
598845 -
存儲器
+關注
關注
38文章
7365瀏覽量
163083 -
交換機
+關注
關注
20文章
2570瀏覽量
98218 -
BRAM
+關注
關注
0文章
41瀏覽量
10914 -
Vivado
+關注
關注
19文章
797瀏覽量
65850
原文標題:Multiport RAM,多讀多寫寄存器-——基于FPGA BRAM的多端口地址查找表與FPGA BRAM的資源分析
文章出處:【微信號:gh_cb8502189068,微信公眾號:網絡交換FPGA】歡迎添加關注!文章轉載請注明出處。
發布評論請先 登錄
相關推薦
評論