1.雙口RAM概述
雙口RAM(dual port RAM)在異構(gòu)系統(tǒng)中應用廣泛,通過雙口RAM,不同硬件架構(gòu)的芯片可以實現(xiàn)數(shù)據(jù)的交互,從而實現(xiàn)通信。例如,一般情況下,ARM與DSP之間的通信,可以利用雙口RAM實現(xiàn),ARM通過EBI總線連接到雙口RAM的A口,DSP通過EMIF總線(也可以是uPP總線,取決于速度需求)連接到雙口RAM的B口,兩者對同一塊存儲區(qū)域進行操作,即可實現(xiàn)兩者的數(shù)據(jù)交互。
但是,因為雙口RAM的A口和B口都可以對相同的內(nèi)存地址進行操作,這就引出了一個問題——假如通信雙方在兩個端口對同一地址同時讀寫,就會引發(fā)沖突。要解決這個問題,辦法有二。一是通信雙方在時序上保證不會同時讀寫同一地址,將ARM和DSP可寫地址范圍進行分區(qū),無論任何一方寫完數(shù)據(jù)后都通過IO發(fā)送中斷通知對方,對方進行數(shù)據(jù)讀?。ㄆ古襌AM操作),這樣是比較可靠的;另外一個辦法就是在fpga里設(shè)置寫busy信號,實現(xiàn)兩端寫同步[]。在FPGA中,構(gòu)建雙口RAM可以通過兩種方法,一種是利用distributed RAM構(gòu)建,另一種是利用Block RAM構(gòu)建,關(guān)于兩者的具體區(qū)別,可以參考這兩篇文章[][]。簡而言之,Block RAM是是使用FPGA中的整塊雙口RAM資源,而distributed RAM則是用FPGA中的邏輯資源拼湊形成的。一般的原則是,較大的存儲應用,建議用bram;零星的小ram,一般就用dram。
在Vivado中,RAM IP核在Memories & Strorage Elements\RAM & ROMs和RAM & ROMs & BRAM文件夾下,如圖所示,下面簡要介紹一下Vivado的雙口RAM IP核。
(圖1.1)
2.Vivado 雙口RAM IP核
2.1 Block Memory Generator概述
點擊圖1.1的Block Memory Generator項,利用BRAM來構(gòu)建雙口RAM。Block Memory Generator窗口如圖2.1所示。
圖中,第1部分,在IP symbol選項卡,點擊“+”號可以展開端口具體信號,如圖2.2所示。第2部分,Component Name可以設(shè)置IP核的名字。第3部分,Basic選項卡,在Memory Type下拉列表中,可以設(shè)置內(nèi)存的類型,如圖2.3所示。Block Memory Gnerator一共可以產(chǎn)生5種不同類型的內(nèi)存空間,其中block RAM有三種:單口RAM、簡化雙口RAM和真雙口RAM[]。單口RAM只有一個端口(A端口),可以對A端口進行讀寫。簡化雙口RAM有兩個端口(A和B端口),但是A端口只能進行寫入操作,不能進行讀出操作,而B端口則只能進行讀出操作,不能進行寫入操作。真雙口RAM有兩個端口(A和B端口),A和B端口都能進行讀寫操作[]。
(圖2.1)
(圖2.2)
(圖2.3)
2.2 真雙口RAM的設(shè)置
2.2.1 Basic設(shè)置
在Basic選項卡的Memory type選項中選擇真雙口RAM,IP Symbol如圖2.4所示。ECC Options為默認設(shè)置,Write Enable中也選擇默認設(shè)置,不使能字節(jié)寫,Algorithm Options選擇默認設(shè)置。
(圖2.4)
2.2.2 Port設(shè)置
點擊Port A Options選項卡,對A端口進行設(shè)置, 設(shè)置Write Width為16(即RAM單元為16位),Write Width為1024(即內(nèi)存深度為1024,該端口可讀寫的RAM單元有1024個),Operating Mode(操作模式)一共有三種:Write First,Read First,No Change。在Write First模式中,在一個時鐘周期里,寫入內(nèi)存單元的數(shù)據(jù)被同步輸出到輸出數(shù)據(jù)總線上;在Read First模式中,在一個時鐘周期里,寫入到內(nèi)存單元的數(shù)據(jù)是當前輸入數(shù)據(jù)總線上的數(shù)據(jù),而輸出到輸出數(shù)據(jù)總線上的數(shù)據(jù)則是上一個時鐘周期存儲在內(nèi)存單元中的數(shù)據(jù)。細節(jié)可參考PG058的49到50頁4。Enable Port Type設(shè)置為Always Enabled,一直使能端口A。其它設(shè)置使用默認設(shè)置。如圖2.5所示。
(圖2.5)
端口B設(shè)置為與A一致。在Other Options選項卡中,保留默認設(shè)置。Load Init File設(shè)置是否用Coe文件對內(nèi)存區(qū)域初始化,這個在初始化ROM的時候會用到,這里不勾選,保持默認。最后,在Summary選項卡會顯示消耗的資源。
3.雙口RAM例程
例程1,該例程是Altera官方例程[],采用寄存器構(gòu)建雙口RAM,代碼如下:
moduletrue_dpram_sclk
(
input [7:0] data_a, data_b,
input [5:0] addr_a, addr_b,
input we_a, we_b, clk,
outputreg [7:0] q_a, q_b
);
// Declare the RAM variable
reg [7:0] ram[63:0];
// Port A
always @ (posedge clk)
begin
if (we_a)
begin
ram[addr_a] 《= data_a;
q_a 《= data_a;
end
else
begin
q_a 《= ram[addr_a];
end
end
// Port B
always @ (posedge clk)
begin
if (we_b)
begin
ram[addr_b] 《= data_b;
q_b 《= data_b;
end
else
begin
q_b 《= ram[addr_b];
end
end
endmodule
例程2,該例程是Xilinx官方例程[],采用寄存器構(gòu)建真雙口RAM,代碼如下:
// Dual-Port Block RAM with Two Write Ports
// File: rams_16.v
modulev_rams_16 (clka,clkb,ena,enb,wea,web,addra,addrb,dia,dib,doa,dob);
input clka,clkb,ena,enb,wea,web;
input [9:0] addra,addrb;
input [15:0] dia,dib;
output [15:0] doa,dob;
reg[15:0] ram [1023:0];
reg[15:0] doa,dob;
always @(posedge clka) beginif (ena)
begin
if (wea)
ram[addra] 《= dia;
doa 《= ram[addra];
end
end
always @(posedge clkb) beginif (enb)
begin
if (web)
ram[addrb] 《= dib;
dob 《= ram[addrb];
end
end
endmodule
例程3,該例程是網(wǎng)友博客中的例程[],代碼如下:
moduleTOP(
input USER_CLK
);
`define DLY #1
reg FPGA_Enable=0;
reg[3:0] FPGA_Write_Enable=4‘h0;
reg[31:0] FPGA_Address=0;
reg[31:0] FPGA_Write_Data=0;
reg[31:0] FPGA_Read_Data_reg=0;
wire[31:0] FPGA_Read_Data;
reg[10:0] count=0;
always @ (posedge USER_CLK)
begin
count 《= count +1;
if(count《=100)
begin
FPGA_Enable 《=0;
FPGA_Write_Enable 《=4’h0;
end
elseif((count 《=105)&&(count 》100))
begin
FPGA_Enable 《=1;
FPGA_Write_Enable 《=4‘hf;
FPGA_Address 《= FPGA_Address +4;
FPGA_Write_Data 《= FPGA_Write_Data +1;
end
elseif((count 《=110)&&(count 》105))
begin
FPGA_Enable 《=0;
FPGA_Write_Enable 《=4’h0;
FPGA_Address 《=0;
FPGA_Write_Data 《=0;
end
elseif((count 《=117)&&(count 》110))
begin
FPGA_Enable 《=1;
FPGA_Write_Enable 《=4‘h0;
FPGA_Read_Data_reg 《= FPGA_Read_Data;
FPGA_Address 《= FPGA_Address +4;
end
elseif(count ==118)
begin
FPGA_Enable 《=0;
count 《= count;
end
end
BBBByour_instance_name (
.clka(USER_CLK), // input clka
.ena(FPGA_Enable), // input ena
.wea(FPGA_Write_Enable), // input [3 : 0] wea
.addra(FPGA_Address), // input [31 : 0] addra
.dina(FPGA_Write_Data), // input [31 : 0] dina
.douta(FPGA_Read_Data), // output [31 : 0] douta
.clkb(clkb), // input clkb
.enb(enb), // input enb
.web(web), // input [3 : 0] web
.addrb(addrb), // input [31 : 0] addrb
.dinb(dinb), // input [31 : 0] dinb
.doutb(doutb) // output [31 : 0] doutb
);
endmodule
該例程中,在count為101(》100)后開始往地址4到20寫入1-5,然后在count為111(》110)的時候讀出寫入的數(shù)據(jù)。
4.仿真
下面利用Modelsim和Vivado進行聯(lián)合仿真,關(guān)于vivado如何與modelsim進行聯(lián)合仿真可以參考這篇文章:
vivado與modelsim的關(guān)聯(lián)以及器件庫編譯
有一點要注意的是,我用的是Vivado2017.1版本,這個版本只支持Modelsim10.5及以上的版本,如果是低版本的Modelsim,在用Vivado2017.1編譯Modelsim的仿真庫時,會出錯。Modelsim10.5版本可以在這里下載:
modelsim 10.5 適用vivado 2017.1
用Modelsim仿真時,會在sim_1/behav文件夾下產(chǎn)生3個.do文件,分別是xx_compile.do,xx_simulate.do,xx _wave.do文件。在設(shè)計的verilog文件修改之后,如果在Modelsim中直接restart,仿真的其實還是沒有修改前的文件,要使修改的.v文件在Modelsim中生效,可以在Modelsim的命令窗口輸入do xx_compile.do文件,對仿真的庫文件以及設(shè)計文件(.v文件)重新編譯,然后在輸入do xx_simulate.do文件,才能仿真修改后的文件。輸入do xx_compile.do命令對設(shè)計文件重新編譯的時候,Modelsim會強制退出,這時由最后一句force quit命令引起的,只要把它刪掉就行了。如果要保存波形文件,可以save format,另存為xx_wave.do文件。
參考上面雙口RAM的例程3進行功能仿真,RAM IP使用Write First模式,設(shè)計文件代碼如下:
`timescale 1ns / 1ps
//////////////////////////////////////////////////////////////////////////////////
// Company:
// Engineer:
//
// Create Date: 2017/12/09 22:36:48
// Design Name:
// Module Name: dual_port_ram_demo
// Project Name:
// Target Devices:
// Tool Versions:
// Description:
//
// Dependencies:
//
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
//
//////////////////////////////////////////////////////////////////////////////////
moduledual_port_ram_demo(
input USER_CLK
);
`define DLY #1
//Port A declaration
reg FPGA_Enable=0;
reg FPGA_Write_Enable=0;
reg[31:0] FPGA_Address=0;
reg[31:0] FPGA_Write_Data=0;
reg[31:0] FPGA_Read_Data_reg=0;
wire[31:0] FPGA_Read_Data;
//Port B declaration
reg enb=0;
reg[3:0] web=4’h0;
reg[31:0] addrb=0;
reg[31:0] dinb=0;
reg[31:0] doutb_reg=0;
wire[31:0] doutb=0;
reg[10:0] count=0;
always @ (posedge USER_CLK)
begin
count 《= count +1;
if(count《=100)
begin
FPGA_Enable 《=1;
FPGA_Write_Enable 《=0;
end
elseif((count 《=105)&&(count 》100))
begin
FPGA_Enable 《=1;
FPGA_Write_Enable 《=1;
FPGA_Address 《= FPGA_Address +4;
FPGA_Write_Data 《= FPGA_Write_Data +1;
end
elseif((count 《=110)&&(count 》105))
begin
FPGA_Enable 《=1;
FPGA_Write_Enable 《=0;
FPGA_Address 《=0;
FPGA_Write_Data 《=0;
end
elseif((count 《=117)&&(count 》110))
begin
FPGA_Enable 《=1;
FPGA_Write_Enable 《=1;
FPGA_Read_Data_reg 《= FPGA_Read_Data;
FPGA_Address 《= FPGA_Address +4;
end
elseif(count ==118)
begin
FPGA_Enable 《=0;
count 《= count;
end
end
dpRAMu1 (
.clka(USER_CLK), // input clka
.ena(FPGA_Enable), // input ena
.wea(FPGA_Write_Enable), // input [3 : 0] wea
.addra(FPGA_Address), // input [31 : 0] addra
.dina(FPGA_Write_Data), // input [31 : 0] dina
.douta(FPGA_Read_Data), // output [31 : 0] douta
.clkb(USER_CLK), // input clkb
.enb(enb), // input enb
.web(web), // input [3 : 0] web
.addrb(addrb), // input [31 : 0] addrb
.dinb(dinb), // input [31 : 0] dinb
.doutb(doutb) // output [31 : 0] doutb
);
endmodule
testbench文件如下:
`timescale 1ns / 1ps
//////////////////////////////////////////////////////////////////////////////////
// Company:
// Engineer:
//
// Create Date: 2017/12/09 22:47:26
// Design Name:
// Module Name: simu
// Project Name:
// Target Devices:
// Tool Versions:
// Description:
//
// Dependencies:
//
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
//
//////////////////////////////////////////////////////////////////////////////////
modulesimu(
);
//testbench 時鐘信號
reg clk =0;
always # 10 clk 《=~clk;
//調(diào)用dual_port_ram_demo模塊
dual_port_ram_demodemo1(clk);
endmodule
仿真結(jié)果如下:
(圖4.1)
程序在1時刻準備好地址和要寫入RAM的數(shù)據(jù),在2時刻寫入RAM中,在3時刻端口才會輸出2時刻寫入RAM的數(shù)據(jù),注意與PG058的圖稍有不同。
(圖4.2)
4.后記
關(guān)于BRAM,推薦一個youtube視頻,里面講的非常清晰易懂。
What is a Block RAM in an FPGA?
評論
查看更多