理論學(xué)習(xí)
上一篇介紹了常用的鎖相環(huán)IP,這一節(jié)將介紹一種較為常用的 存儲(chǔ)類IP核 ——ROM的使用方法。ROM是 只讀存儲(chǔ)器 (Read-Only Memory),顧名思義,我們只能讀出事先存放在固態(tài)中的數(shù)據(jù),一旦寫入不能再修改或刪除,斷電不丟失。我們知道FPGA只有RAM,因此事實(shí)上在 FPGA 中通過(guò) IP 核生成的 ROM 或 RAM掉電內(nèi)容都會(huì)丟失。用 IP 核生成的 ROM 模塊只是提前添加了數(shù)據(jù)文件(.mif 或.hex 格式),在 FPGA 運(yùn)行時(shí)通過(guò)數(shù)據(jù)文件給 ROM 模塊初始化,才使得 ROM 模塊 像 “真正”的掉電非易失存儲(chǔ)器;也正是這個(gè)原因,ROM 模塊的內(nèi)容必須提前在數(shù)據(jù)文件中寫死,無(wú)法在電路中修改。
Altera推出的ROM IP核分為兩種類型:?jiǎn)?a target="_blank">端口ROM和雙端口ROM。對(duì)于單端口ROM提供一個(gè)讀地址端口和一個(gè)讀數(shù)據(jù)端口,只能進(jìn)行讀操作;雙端口ROM提供兩個(gè)讀地址端口和兩個(gè)讀數(shù)據(jù)端口。其中不是每個(gè)端口都要用到,調(diào)用完IP核后,是可以生成其例化模塊的,到時(shí)候就可以看到我們需要控制的信號(hào)了。
ROM IP核配置
ROM要事先寫進(jìn)去.mif 或.hex 格式,因此我們需要先建一個(gè).mif文件,寫入我們想要存儲(chǔ)的數(shù)據(jù)。
File→New→在Memory Files下找到Memory Initialization File-選擇容量為256,位寬為8bit→選中表格的行或列右鍵可以更改進(jìn)制,默認(rèn)地址是十進(jìn)制,存儲(chǔ)器是無(wú)符號(hào)十進(jìn)制→手動(dòng)輸入數(shù)據(jù)/復(fù)制、粘貼/利用軟件自帶的功能,直接推譯出所有數(shù)據(jù)。
軟件自帶的填充功能用法
右鍵點(diǎn)擊任意單元格→Custom Fill Cells→容量為256,如果起始地址為0,結(jié)束地址就為255→可以看到表格中從0-255自動(dòng)填充好,由于位寬8bit,255也不會(huì)超→保存為.mif格式
創(chuàng)建好工程之后,還是和上一篇中一樣創(chuàng)建IP核,先試單端口
使用寫好的數(shù)據(jù),瀏覽文件夾,選擇.mif格式,找到剛才保存的.mif文件導(dǎo)入。
和上一篇一樣,顯示了我們?cè)?a target="_blank">仿真ROM IP核時(shí)需要的Altera仿真庫(kù),提示我們單獨(dú)使用第三方仿真工具時(shí)需要添加altera_mf的庫(kù)
和上一篇一樣,選擇inst.v實(shí)例化文件
雙端口有少許不同
1、可以看出有兩條地址線和兩條數(shù)據(jù)線了
2、設(shè)置定義ROM存儲(chǔ)器大小的方式,按字?jǐn)?shù)確定或按比特?cái)?shù)確定,用默認(rèn)就好
3、選擇ROM的容量,還是選擇256個(gè)數(shù)據(jù)(注意:選擇的容量需大于我們需要寫入的數(shù)據(jù)文件的數(shù)據(jù)量)
4、設(shè)置不同端口的位寬是否相同,默認(rèn)是關(guān)閉,即使用相同位寬
5、設(shè)置數(shù)據(jù)位寬,這里的數(shù)據(jù)位寬設(shè)置8bit
6、存儲(chǔ)單元類型的選擇,按默認(rèn)選擇自動(dòng)
1、選擇單時(shí)鐘/雙時(shí)鐘:?jiǎn)螘r(shí)鐘是用一個(gè)時(shí)鐘控制所有寄存器,雙時(shí)鐘是輸入和輸出時(shí)鐘分別控制存儲(chǔ)塊的數(shù)據(jù)輸入和輸出的相關(guān)寄存器:時(shí)鐘A控制端口A的所有寄存器,時(shí)鐘B控制端口B的所有寄存器。每個(gè)端口也支持獨(dú)立的時(shí)鐘使能
2、選擇是否創(chuàng)建‘rden_a’和‘rden_b’讀使能信號(hào)
1、選擇是否輸出‘q_a’和‘q_b’寄存器,選擇的話就會(huì)使輸出延遲一拍
2、選擇是否為時(shí)鐘信號(hào)創(chuàng)建使能信號(hào)
3、選擇是否創(chuàng)建“aclr”異步復(fù)位信號(hào)
后面的步驟都一樣。我們以單端口為例進(jìn)行設(shè)計(jì)調(diào)用,還是將生成的,qip文件添加到Files下。
設(shè)計(jì)規(guī)劃
首先我們ROM的初始化數(shù)據(jù)是0~255,每隔0.2s從0地址開(kāi)始往下讀取數(shù)據(jù)顯示在數(shù)碼管上,再利用兩個(gè)按鍵信號(hào)來(lái)讀取指定地址的數(shù)據(jù)(例如按下按鍵1顯示地址為99時(shí)的數(shù)據(jù),按下按鍵2顯示地址為199時(shí)的數(shù)據(jù),0-255隨意指定)。每按一個(gè)按鍵就讀取一個(gè)地址的數(shù)據(jù)顯示在數(shù)碼管上。再次按下按鍵后,以當(dāng)前地址繼續(xù)以0.2s的時(shí)間間隔往下讀取數(shù)據(jù)并顯示出來(lái)。
一共有5個(gè)模塊:按鍵消抖模塊(使用兩次),ROM控制模塊,IP核,數(shù)碼管動(dòng)態(tài)顯示模塊,頂層模塊。實(shí)際需要做的是ROM控制模塊,頂層只是實(shí)現(xiàn)實(shí)例化,其他模塊可以直接調(diào)用以前的,可能要做一些修改。
剛才建立rom的ip后生成了inst.v實(shí)例化文件,由內(nèi)容可以看出這個(gè)模塊出輸入輸出信號(hào)名稱,頂層模塊中關(guān)于rom的ip模塊的實(shí)例化可以直接復(fù)制這個(gè)
編寫代碼
ROM 控制模塊
讀操作是在時(shí)鐘的上升沿觸發(fā)的,而我們?cè)谡{(diào)用ROM時(shí)是沒(méi)有生成讀使能的,所以在讀時(shí)鐘上升沿只要給相應(yīng)的地址就能在時(shí)鐘的上升沿讀出該地址內(nèi)的數(shù)據(jù)了。我們只需要控制生成讀地址即可?,F(xiàn)在自定義的ROM IP的用法就是給從地址線輸入一個(gè)地址,ROM模塊從數(shù)據(jù)線輸出地址對(duì)應(yīng)的數(shù)據(jù)。
輸入有時(shí)鐘、復(fù)位、兩個(gè)按鍵標(biāo)志信號(hào),中間信號(hào)有兩個(gè)地址標(biāo)志信號(hào),200ms計(jì)數(shù)器,輸出是8位地址。某一個(gè)按鍵按下時(shí)對(duì)應(yīng)的按鍵標(biāo)志信號(hào)會(huì)拉高,當(dāng)檢測(cè)到某一個(gè)按鍵標(biāo)志信號(hào)拉高時(shí),對(duì)應(yīng)的地址標(biāo)志信號(hào)會(huì)拉高,直至下一個(gè)按鍵被按下。按一次按鍵是顯示規(guī)定地址存放的數(shù)據(jù),再按同一個(gè)按鍵會(huì)在該地址基礎(chǔ)上繼續(xù)顯示下一個(gè)地址的數(shù)據(jù),而按不同的按鍵就是顯示另一個(gè)按鍵規(guī)定的地址存放的數(shù)據(jù)。
module rom_ctrl
(
input wire sys_clk , //系統(tǒng)時(shí)鐘,頻率50MHz
input wire sys_rst_n , //復(fù)位信號(hào),低有效
input wire key1_flag , //按鍵1消抖后有效信號(hào)
input wire key2_flag , //按鍵2消抖后有效信號(hào)
output reg [7:0] addr //輸出讀ROM地址
);
//parameter define
parameter CNT_MAX = 9_999_999; //0.2s計(jì)數(shù)器最大值
//reg define
reg addr_flag1 ; //特定地址1標(biāo)志信號(hào)
reg addr_flag2 ; //特定地址2標(biāo)志信號(hào)
reg [23:0] cnt_200ms ; //0.2s計(jì)數(shù)器
//產(chǎn)生特定地址1標(biāo)志信號(hào)
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
addr_flag1 <= 1'b0;
else if(key2_flag == 1'b1)
addr_flag1 <= 1'b0;
else if(key1_flag == 1'b1)
addr_flag1 <= ~addr_flag1;
//產(chǎn)生特定地址2標(biāo)志信號(hào)
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
addr_flag2 <= 1'b0;
else if(key1_flag == 1'b1)
addr_flag2 <= 1'b0;
else if(key2_flag == 1'b1)
addr_flag2 <= ~addr_flag2;
//0.2s循環(huán)計(jì)數(shù)
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
cnt_200ms <= 24'd0;
else if(cnt_200ms == CNT_MAX)
cnt_200ms <= 24'd0;
else
cnt_200ms <= cnt_200ms + 1'b1;
//讓地址從0~255循環(huán),其中兩個(gè)按鍵控制兩個(gè)特定地址的跳轉(zhuǎn)
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
addr <= 8'd0;
else if(addr == 8'd255 && cnt_200ms == CNT_MAX)
addr <= 8'd0;
else if(addr_flag1 == 1'b1)
addr <= 8'd99;
else if(addr_flag2 == 1'b1)
addr <= 8'd199;
else if(cnt_200ms == CNT_MAX)
addr <= addr + 1'b1;
endmodule
產(chǎn)生addr_flag1:復(fù)位有效時(shí)addr_flag歸0;key2_flag拉高時(shí)addr_flag1歸0(因?yàn)閗ey1與key2是互斥的,同一時(shí)間只能亮一個(gè),所以不管key1現(xiàn)在是否按下去,key2一旦按下去就要以key2的地址為準(zhǔn)了);key_flag1拉高時(shí)addr_flag1取反(因?yàn)榭赡艽嬖谶B按兩次的情況,第一次按addr_flag1從第變高,第二次按就從高變低)。產(chǎn)生addr_flag1同理
200ms計(jì)數(shù)器:復(fù)位有效時(shí)歸0;計(jì)數(shù)到最大值時(shí)歸0;否則自加1
地址輸出:復(fù)位有效時(shí)addr為0;當(dāng)addr為255且cnt200ms計(jì)數(shù)為最大值CNT_MAX時(shí),addr為0(因?yàn)榈刂分赶?55后0.2ms要循環(huán)顯示地址0對(duì)應(yīng)的數(shù)據(jù));當(dāng)addr_flag1拉高時(shí)addr為99;當(dāng)addr_flag2拉高時(shí)addr為199;當(dāng)計(jì)數(shù)器計(jì)到最大值時(shí)addr自加1
頂層模塊
實(shí)質(zhì)是幾個(gè)模塊的實(shí)例化,需要注意的是key模塊使用了兩次,要實(shí)例化兩次,兩次實(shí)例化的模塊名字不能相同
之前的數(shù)碼管動(dòng)態(tài)顯示的模塊框圖做一下修正
對(duì)比一下現(xiàn)在的模塊
1、之前的給數(shù)碼管模塊的輸入數(shù)據(jù)是data_gen這個(gè)模塊的輸出產(chǎn)生的,現(xiàn)在的data是rom的IP模塊產(chǎn)生的。且這個(gè)IP的輸出只有8位,而我們之前的設(shè)置的數(shù)碼管模塊data是27位,因此要補(bǔ)19個(gè)0能保證位數(shù)一致且對(duì)顯示沒(méi)有影響。還需要修改一處是top_seg_595模塊中實(shí)例化了data_gen,現(xiàn)在不需要了
2、之前的data_gen的輸出信號(hào)seg_en與seg_595_dynamic模塊的輸入信號(hào)seg_en相連,用于給數(shù)碼管顯示使能。現(xiàn)在的rom_256x8模塊的實(shí)例化是系統(tǒng)IP自動(dòng)生成的,沒(méi)有提供seg_en信號(hào)接口,需要自行設(shè)置這個(gè)信號(hào)為高電平讓他使能
頂層模塊代碼
module rom
(
input wire sys_clk ,
input wire sys_rst_n ,
input wire [1:0] key ,
output wire stcp ,
output wire shcp ,
output wire ds
);
//wire define
wire [7:0] addr ; //地址線
wire [7:0] rom_data ; //讀出ROM數(shù)據(jù)
wire key1_flag ; //按鍵1消抖信號(hào)
wire key2_flag ; //按鍵2消抖信號(hào)
rom_ctrl rom_ctrl_inst
(
.sys_clk (sys_clk ),
.sys_rst_n (sys_rst_n ),
.key1_flag (key1_flag ),
.key2_flag (key2_flag ),
.addr (addr )
);
key_filter key1_filter_inst
(
.sys_clk (sys_clk ),
.sys_rst_n (sys_rst_n ),
.key_in (key[0] ),
.key_flag (key1_flag )
);
key_filter key2_filter_inst
(
.sys_clk (sys_clk ),
.sys_rst_n (sys_rst_n ),
.key_in (key[1] ),
.key_flag (key2_flag )
);
seg_595_dynamic seg_595_dynamic_inst
(
.sys_clk (sys_clk ),
.sys_rst_n (sys_rst_n ),
.data ({19'd0,rom_data}),
.seg_en (1'b1 ), //數(shù)碼管使能信號(hào),高電平有效
.stcp (stcp ), //輸出數(shù)據(jù)存儲(chǔ)寄時(shí)鐘
.shcp (shcp ), //移位寄存器的時(shí)鐘輸入
.ds (ds )//串行數(shù)據(jù)輸入
);
rom_256x8 rom_256x8_inst
(
.address (addr ),
.clock (sys_clk ),
.q (rom_data )
);
endmodule
rom_ctrl模塊的實(shí)例化沒(méi)有什么需要特別注意的
key_filter模塊需要注意的是這兩個(gè)模塊名不能一樣,模塊都用的是key_filter,實(shí)例化后一個(gè)叫key1_filter_inst,一個(gè)叫key2_filter_inst。rom頂層模塊中定義的Key是一個(gè)2位的變量,兩個(gè)模塊中的Key_in就分別接key的高位和低位
數(shù)碼管動(dòng)態(tài)顯示模塊:這個(gè)模塊是第三次使用了,因?yàn)橹暗膁ata是27位,現(xiàn)在只用了data的其中8位,剩下19位置0
rom_256x8模塊的實(shí)例化就是rom的ip核生成的inst.v實(shí)例化文件
Testbench
`timescale 1ns/1ns
module tb_rom();
//wire define
wire stcp;
wire shcp;
wire ds ;
//reg define
reg sys_clk ;
reg sys_rst_n ;
reg [1:0] key ;
//對(duì)sys_clk,sys_rst賦初值,并模擬按鍵抖動(dòng)
initial
begin
sys_clk = 1'b1 ;
sys_rst_n <= 1'b0 ;
key <= 2'b11;
#200 sys_rst_n <= 1'b1 ;
//按下按鍵key[0]
#2000 key[0] <= 1'b0;//按下按鍵
#20 key[0] <= 1'b1;//模擬抖動(dòng)
#20 key[0] <= 1'b0;//模擬抖動(dòng)
#20 key[0] <= 1'b1;//模擬抖動(dòng)
#20 key[0] <= 1'b0;//模擬抖動(dòng)
#200 key[0] <= 1'b1;//松開(kāi)按鍵
#20 key[0] <= 1'b0;//模擬抖動(dòng)
#20 key[0] <= 1'b1;//模擬抖動(dòng)
#20 key[0] <= 1'b0;//模擬抖動(dòng)
#20 key[0] <= 1'b1;//模擬抖動(dòng)
//按下按鍵key[1]
#2000 key[1] <= 1'b0;//按下按鍵
#20 key[1] <= 1'b1;//模擬抖動(dòng)
#20 key[1] <= 1'b0;//模擬抖動(dòng)
#20 key[1] <= 1'b1;//模擬抖動(dòng)
#20 key[1] <= 1'b0;//模擬抖動(dòng)
#200 key[1] <= 1'b1;//松開(kāi)按鍵
#20 key[1] <= 1'b0;//模擬抖動(dòng)
#20 key[1] <= 1'b1;//模擬抖動(dòng)
#20 key[1] <= 1'b0;//模擬抖動(dòng)
#20 key[1] <= 1'b1;//模擬抖動(dòng)
//按下按鍵key[1]
#2000 key[1] <= 1'b0;//按下按鍵
#20 key[1] <= 1'b1;//模擬抖動(dòng)
#20 key[1] <= 1'b0;//模擬抖動(dòng)
#20 key[1] <= 1'b1;//模擬抖動(dòng)
#20 key[1] <= 1'b0;//模擬抖動(dòng)
#200 key[1] <= 1'b1;//松開(kāi)按鍵
#20 key[1] <= 1'b0;//模擬抖動(dòng)
#20 key[1] <= 1'b1;//模擬抖動(dòng)
#20 key[1] <= 1'b0;//模擬抖動(dòng)
#20 key[1] <= 1'b1;//模擬抖動(dòng)
//按下按鍵key[1]
#2000 key[1] <= 1'b0;//按下按鍵
#20 key[1] <= 1'b1;//模擬抖動(dòng)
#20 key[1] <= 1'b0;//模擬抖動(dòng)
#20 key[1] <= 1'b1;//模擬抖動(dòng)
#20 key[1] <= 1'b0;//模擬抖動(dòng)
#200 key[1] <= 1'b1;//松開(kāi)按鍵
#20 key[1] <= 1'b0;//模擬抖動(dòng)
#20 key[1] <= 1'b1;//模擬抖動(dòng)
#20 key[1] <= 1'b0;//模擬抖動(dòng)
#20 key[1] <= 1'b1;//模擬抖動(dòng)
//按下按鍵key[0]
#2000 key[0] <= 1'b0;//按下按鍵
#20 key[0] <= 1'b1;//模擬抖動(dòng)
#20 key[0] <= 1'b0;//模擬抖動(dòng)
#20 key[0] <= 1'b1;//模擬抖動(dòng)
#20 key[0] <= 1'b0;//模擬抖動(dòng)
#200 key[0] <= 1'b1;//松開(kāi)按鍵
#20 key[0] <= 1'b0;//模擬抖動(dòng)
#20 key[0] <= 1'b1;//模擬抖動(dòng)
#20 key[0] <= 1'b0;//模擬抖動(dòng)
#20 key[0] <= 1'b1;//模擬抖動(dòng)
//按下按鍵key[0]
#2000 key[0] <= 1'b0;//按下按鍵
#20 key[0] <= 1'b1;//模擬抖動(dòng)
#20 key[0] <= 1'b0;//模擬抖動(dòng)
#20 key[0] <= 1'b1;//模擬抖動(dòng)
#20 key[0] <= 1'b0;//模擬抖動(dòng)
#200 key[0] <= 1'b1;//松開(kāi)按鍵
#20 key[0] <= 1'b0;//模擬抖動(dòng)
#20 key[0] <= 1'b1;//模擬抖動(dòng)
#20 key[0] <= 1'b0;//模擬抖動(dòng)
#20 key[0] <= 1'b1;//模擬抖動(dòng)
end
//sys_clk:模擬系統(tǒng)時(shí)鐘,每10ns電平取反一次,周期為20ns,頻率為50MHz
always #10 sys_clk = ~sys_clk;
//重新定義參數(shù)值,縮短仿真時(shí)間仿真
defparam rom_inst.key1_filter_inst.CNT_MAX = 5 ;
defparam rom_inst.key2_filter_inst.CNT_MAX = 5 ;
defparam rom_inst.rom_ctrl_inst.CNT_MAX = 99;
//---------------rom_inst--------------
rom rom_inst
(
.sys_clk (sys_clk ), //系統(tǒng)時(shí)鐘,頻率50MHz
.sys_rst_n (sys_rst_n ), //復(fù)位信號(hào),低電平有效
.key (key ), //輸入按鍵信號(hào)
.stcp (stcp ), //輸出數(shù)據(jù)存儲(chǔ)寄時(shí)鐘
.shcp (shcp ), //移位寄存器的時(shí)鐘輸入
.ds (ds ) //串行數(shù)據(jù)輸入
);
endmodule
初始化:時(shí)鐘為高電平,復(fù)位為低電平,按鍵都為高電平表示未按下
延遲200ns后復(fù)位釋放
延遲2000ns后按下按鍵1但是模擬抖動(dòng),抖動(dòng)中有200ns的按鍵是按下?tīng)顟B(tài)以便識(shí)別并拉高flag
再重復(fù)模擬按下按鍵2,2,2,1,1
重新定義參數(shù),縮短仿真時(shí)間
rom模塊實(shí)例化
波形變化
管腳分配
-
FPGA
+關(guān)注
關(guān)注
1624文章
21539瀏覽量
600526 -
鎖相環(huán)
+關(guān)注
關(guān)注
35文章
577瀏覽量
87626 -
存儲(chǔ)器
+關(guān)注
關(guān)注
38文章
7403瀏覽量
163398 -
ROM
+關(guān)注
關(guān)注
4文章
549瀏覽量
85568 -
IP核
+關(guān)注
關(guān)注
4文章
318瀏覽量
49335
發(fā)布評(píng)論請(qǐng)先 登錄
相關(guān)推薦
評(píng)論