1.雙口RAM概述
雙口RAM(dual port RAM)在異構(gòu)系統(tǒng)中應(yīng)用廣泛,通過(guò)雙口RAM,不同硬件架構(gòu)的芯片可以實(shí)現(xiàn)數(shù)據(jù)的交互,從而實(shí)現(xiàn)通信。例如,一般情況下,ARM與DSP之間的通信,可以利用雙口RAM實(shí)現(xiàn),ARM通過(guò)EBI總線連接到雙口RAM的A口,DSP通過(guò)EMIF總線(也可以是uPP總線,取決于速度需求)連接到雙口RAM的B口,兩者對(duì)同一塊存儲(chǔ)區(qū)域進(jìn)行操作,即可實(shí)現(xiàn)兩者的數(shù)據(jù)交互。
但是,因?yàn)殡p口RAM的A口和B口都可以對(duì)相同的內(nèi)存地址進(jìn)行操作,這就引出了一個(gè)問(wèn)題——假如通信雙方在兩個(gè)端口對(duì)同一地址同時(shí)讀寫(xiě),就會(huì)引發(fā)沖突。要解決這個(gè)問(wèn)題,辦法有二。一是通信雙方在時(shí)序上保證不會(huì)同時(shí)讀寫(xiě)同一地址,將ARM和DSP可寫(xiě)地址范圍進(jìn)行分區(qū),無(wú)論任何一方寫(xiě)完數(shù)據(jù)后都通過(guò)IO發(fā)送中斷通知對(duì)方,對(duì)方進(jìn)行數(shù)據(jù)讀?。ㄆ古襌AM操作),這樣是比較可靠的;另外一個(gè)辦法就是在fpga里設(shè)置寫(xiě)busy信號(hào),實(shí)現(xiàn)兩端寫(xiě)同步[]。在FPGA中,構(gòu)建雙口RAM可以通過(guò)兩種方法,一種是利用distributed RAM構(gòu)建,另一種是利用Block RAM構(gòu)建,關(guān)于兩者的具體區(qū)別,可以參考這兩篇文章[][]。簡(jiǎn)而言之,Block RAM是是使用FPGA中的整塊雙口RAM資源,而distributed RAM則是用FPGA中的邏輯資源拼湊形成的。一般的原則是,較大的存儲(chǔ)應(yīng)用,建議用bram;零星的小ram,一般就用dram。
在Vivado中,RAM IP核在Memories & Strorage Elements\RAM & ROMs和RAM & ROMs & BRAM文件夾下,如圖所示,下面簡(jiǎn)要介紹一下Vivado的雙口RAM IP核。
(圖1.1)
2.Vivado 雙口RAM IP核
2.1 Block Memory Generator概述
點(diǎn)擊圖1.1的Block Memory Generator項(xiàng),利用BRAM來(lái)構(gòu)建雙口RAM。Block Memory Generator窗口如圖2.1所示。
圖中,第1部分,在IP symbol選項(xiàng)卡,點(diǎn)擊“+”號(hào)可以展開(kāi)端口具體信號(hào),如圖2.2所示。第2部分,Component Name可以設(shè)置IP核的名字。第3部分,Basic選項(xiàng)卡,在Memory Type下拉列表中,可以設(shè)置內(nèi)存的類(lèi)型,如圖2.3所示。Block Memory Gnerator一共可以產(chǎn)生5種不同類(lèi)型的內(nèi)存空間,其中block RAM有三種:?jiǎn)慰赗AM、簡(jiǎn)化雙口RAM和真雙口RAM[]。單口RAM只有一個(gè)端口(A端口),可以對(duì)A端口進(jìn)行讀寫(xiě)。簡(jiǎn)化雙口RAM有兩個(gè)端口(A和B端口),但是A端口只能進(jìn)行寫(xiě)入操作,不能進(jìn)行讀出操作,而B(niǎo)端口則只能進(jìn)行讀出操作,不能進(jìn)行寫(xiě)入操作。真雙口RAM有兩個(gè)端口(A和B端口),A和B端口都能進(jìn)行讀寫(xiě)操作[]。
(圖2.1)
(圖2.2)
(圖2.3)
2.2 真雙口RAM的設(shè)置
2.2.1 Basic設(shè)置
在Basic選項(xiàng)卡的Memory type選項(xiàng)中選擇真雙口RAM,IP Symbol如圖2.4所示。ECC Options為默認(rèn)設(shè)置,Write Enable中也選擇默認(rèn)設(shè)置,不使能字節(jié)寫(xiě),Algorithm Options選擇默認(rèn)設(shè)置。
(圖2.4)
2.2.2 Port設(shè)置
點(diǎn)擊Port A Options選項(xiàng)卡,對(duì)A端口進(jìn)行設(shè)置, 設(shè)置Write Width為16(即RAM單元為16位),Write Width為1024(即內(nèi)存深度為1024,該端口可讀寫(xiě)的RAM單元有1024個(gè)),Operating Mode(操作模式)一共有三種:Write First,Read First,No Change。在Write First模式中,在一個(gè)時(shí)鐘周期里,寫(xiě)入內(nèi)存單元的數(shù)據(jù)被同步輸出到輸出數(shù)據(jù)總線上;在Read First模式中,在一個(gè)時(shí)鐘周期里,寫(xiě)入到內(nèi)存單元的數(shù)據(jù)是當(dāng)前輸入數(shù)據(jù)總線上的數(shù)據(jù),而輸出到輸出數(shù)據(jù)總線上的數(shù)據(jù)則是上一個(gè)時(shí)鐘周期存儲(chǔ)在內(nèi)存單元中的數(shù)據(jù)。細(xì)節(jié)可參考PG058的49到50頁(yè)4。Enable Port Type設(shè)置為Always Enabled,一直使能端口A。其它設(shè)置使用默認(rèn)設(shè)置。如圖2.5所示。
(圖2.5)
端口B設(shè)置為與A一致。在Other Options選項(xiàng)卡中,保留默認(rèn)設(shè)置。Load Init File設(shè)置是否用Coe文件對(duì)內(nèi)存區(qū)域初始化,這個(gè)在初始化ROM的時(shí)候會(huì)用到,這里不勾選,保持默認(rèn)。最后,在Summary選項(xiàng)卡會(huì)顯示消耗的資源。
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)后開(kāi)始往地址4到20寫(xiě)入1-5,然后在count為111(》110)的時(shí)候讀出寫(xiě)入的數(shù)據(jù)。
4.仿真
下面利用Modelsim和Vivado進(jìn)行聯(lián)合仿真,關(guān)于vivado如何與modelsim進(jìn)行聯(lián)合仿真可以參考這篇文章:
vivado與modelsim的關(guān)聯(lián)以及器件庫(kù)編譯
有一點(diǎn)要注意的是,我用的是Vivado2017.1版本,這個(gè)版本只支持Modelsim10.5及以上的版本,如果是低版本的Modelsim,在用Vivado2017.1編譯Modelsim的仿真庫(kù)時(shí),會(huì)出錯(cuò)。Modelsim10.5版本可以在這里下載:
modelsim 10.5 適用vivado 2017.1
用Modelsim仿真時(shí),會(huì)在sim_1/behav文件夾下產(chǎn)生3個(gè).do文件,分別是xx_compile.do,xx_simulate.do,xx _wave.do文件。在設(shè)計(jì)的verilog文件修改之后,如果在Modelsim中直接restart,仿真的其實(shí)還是沒(méi)有修改前的文件,要使修改的.v文件在Modelsim中生效,可以在Modelsim的命令窗口輸入do xx_compile.do文件,對(duì)仿真的庫(kù)文件以及設(shè)計(jì)文件(.v文件)重新編譯,然后在輸入do xx_simulate.do文件,才能仿真修改后的文件。輸入do xx_compile.do命令對(duì)設(shè)計(jì)文件重新編譯的時(shí)候,Modelsim會(huì)強(qiáng)制退出,這時(shí)由最后一句force quit命令引起的,只要把它刪掉就行了。如果要保存波形文件,可以save format,另存為xx_wave.do文件。
參考上面雙口RAM的例程3進(jìn)行功能仿真,RAM IP使用Write First模式,設(shè)計(jì)文件代碼如下:
`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 時(shí)鐘信號(hào)
reg clk =0;
always # 10 clk 《=~clk;
//調(diào)用dual_port_ram_demo模塊
dual_port_ram_demodemo1(clk);
endmodule
仿真結(jié)果如下:
(圖4.1)
程序在1時(shí)刻準(zhǔn)備好地址和要寫(xiě)入RAM的數(shù)據(jù),在2時(shí)刻寫(xiě)入RAM中,在3時(shí)刻端口才會(huì)輸出2時(shí)刻寫(xiě)入RAM的數(shù)據(jù),注意與PG058的圖稍有不同。
(圖4.2)
4.后記
關(guān)于BRAM,推薦一個(gè)youtube視頻,里面講的非常清晰易懂。
What is a Block RAM in an FPGA?
評(píng)論
查看更多