理論學(xué)習(xí)
我們知道除了只讀存儲器外還有隨機(jī)存取存儲器,這一篇將介紹另一種 存儲類IP核 ——RAM的使用方法。RAM是 隨機(jī)存取存儲器 (Random Access Memory),是一個(gè)易失性存儲器,斷電丟失。RAM工作時(shí)可以隨時(shí)從任何一個(gè)指定的地址寫入或讀出數(shù)據(jù)。
同樣的,Altera推出的RAM IP核分為兩種類型:單端口RAM和雙端口RAM。其中雙端口RAM又分為簡單雙端口RAM(Simple dual port RAM)和真正雙端口RAM(True dual port RAM)。對于單端口RAM,讀寫操作共用一組地址線,讀寫操作不能同時(shí)進(jìn)行;對于簡單雙端口RAM,讀操作和寫操作有專用地址端口(一個(gè)讀端口和一個(gè)寫端口),即寫端口只能寫不能讀,而讀 端口只能讀不能寫;對于真正雙端口RAM,有兩個(gè)地址端口用于讀寫操作(兩個(gè)讀/寫端口),即兩個(gè)端口都可以進(jìn)行讀寫。
RAM IP核配置
一端口的配置幾乎和rom沒有區(qū)別
勾選上創(chuàng)建“aclr”異步復(fù)位信號以及是否創(chuàng)建“rden”讀使能信號
簡單雙端口和真正雙端口都是在雙端口中去配置的,配置的選項(xiàng)的定義和ROM中一樣
真正雙端口的配置除了選擇真正雙端口以外,其他的選項(xiàng)和簡單雙端口的配置一樣
設(shè)計(jì)規(guī)劃
按下按鍵1時(shí)往RAM地址0255里寫入數(shù)據(jù)0255;按下按鍵2時(shí)讀取RAM內(nèi)的數(shù)據(jù),從地址0開始每隔0.2s地址加1往下進(jìn)行讀取;再次按下按鍵1時(shí)停止讀取重新寫入數(shù)據(jù)0~255;再次按下按鍵2時(shí)從頭開始讀取數(shù)據(jù)
一共有5個(gè)模塊:按鍵消抖模塊(使用兩次),RAM控制模塊,IP核,數(shù)碼管動(dòng)態(tài)顯示模塊,頂層模塊。實(shí)際需要做的是RAM控制模塊
編寫代碼
ROM控制模塊
RAM的寫時(shí)序?yàn)椋寒?dāng)RAM寫時(shí)鐘上升沿采到寫使能為高時(shí),就能將該上升沿采到的數(shù)據(jù)寫入該上升沿采到的地址中。
RAM的讀時(shí)序?yàn)椋寒?dāng)RAM讀時(shí)鐘上升沿采到讀使能為高時(shí),就能讀出該上升沿采到的地址中的數(shù)據(jù)。若是我們配置IP核時(shí) 沒有生成RAM讀使能 ,那么RAM就能直接讀出讀時(shí)鐘上升沿采到的地址中的數(shù)據(jù)。
按鍵1按下key_flag1拉高一個(gè)時(shí)鐘,寫使能有效,地址從0-255,寫數(shù)據(jù)從0-255,寫完后寫使能釋放;按鍵2按下時(shí)key_flag2拉高一個(gè)時(shí)鐘,讀使能有效,cnt_200開始計(jì)數(shù),每0.2s讀取一次給出地址對應(yīng)的數(shù)據(jù)。讀完255后回到0繼續(xù)讀取,如此循環(huán),直至再次按下按鍵1,則讀使能釋放,再次寫入數(shù)據(jù)。
若寫使能有效時(shí),按下按鍵2并不能讀數(shù)據(jù)。寫使能有效時(shí)再次按下按鍵1,地址歸0重新寫入。
module ram_ctrl
(
input wire sys_clk ,
input wire sys_rst_n ,
input wire key1_flag ,
input wire key2_flag ,
output reg wr_en , //寫RAM使能,高電平有效
output reg rd_en , //讀RAM使能,高電平有效
output reg [7:0] addr , //讀寫RAM地址
output wire [7:0] wr_data //寫RAM數(shù)據(jù)
);
//parameter define
parameter CNT_MAX = 9_999_999; //0.2s計(jì)數(shù)器最大值
//reg define
reg [23:0] cnt_200ms ; //0.2s計(jì)數(shù)器
//讓寫入的數(shù)據(jù)等于地址數(shù),即寫入數(shù)據(jù)0~255
assign wr_data = (wr_en == 1'b1) ? addr : 8'd0;
//wr_en:產(chǎn)生寫RAM使能信號
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
wr_en <= 1'b0;
else if(addr == 8'd255)
wr_en <= 1'b0;
else if(key1_flag == 1'b1)
wr_en <= 1'b1;
//rd_en:產(chǎn)生讀RAM使能信號
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
rd_en <= 1'b0;
else if(key2_flag == 1'b1 && wr_en == 1'b0)
rd_en <= 1'b1;
else if(key1_flag == 1'b1)
rd_en <= 1'b0;
else
rd_en <= rd_en;
//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 || key2_flag == 1'b1)
cnt_200ms <= 24'd0;
else if(rd_en == 1'b1)
cnt_200ms <= cnt_200ms + 1'b1;
//寫使能有效時(shí),
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'd255 && wr_en == 1'b1) ||
(key2_flag == 1'b1) || (key1_flag == 1'b1))
addr <= 8'd0;
else if((wr_en == 1'b1) || (rd_en == 1'b1 && cnt_200ms == CNT_MAX))
addr <= addr + 1'b1;
endmodule
輸入信號有時(shí)鐘,復(fù)位,按鍵1標(biāo)志信號,按鍵2標(biāo)志信號,輸出信號有寫使能,讀使能,讀寫地址,寫數(shù)據(jù)
參數(shù)定義:計(jì)數(shù)器的最大計(jì)數(shù)值是9_999_999
寫入數(shù)據(jù)和地址一致,用三目運(yùn)算符進(jìn)行選擇,如果寫使能有效,寫數(shù)據(jù)就是地址,無效寫數(shù)據(jù)就為0
寫使能信號:復(fù)位有效時(shí)寫使能歸0;地址為255時(shí)寫使能歸0;按鍵1標(biāo)志信號拉高時(shí)寫使能拉高
讀使能信號:復(fù)位有效時(shí)讀使能歸0;按鍵1標(biāo)志信號拉高時(shí)讀使能歸0(即使在讀數(shù)據(jù),寫使能有效時(shí)優(yōu)先寫入數(shù)據(jù));按鍵2標(biāo)志信號拉高且寫使能無效時(shí),讀信號拉高(寫數(shù)據(jù)優(yōu)先,寫使能無效時(shí)才能讀數(shù)據(jù));其他情況讀使能保持
計(jì)數(shù)cnt_200ms:復(fù)位有效時(shí)計(jì)數(shù)器歸0;計(jì)數(shù)到最大值且按鍵2標(biāo)志信號拉高時(shí)計(jì)數(shù)器歸0;讀使能有效時(shí)計(jì)數(shù)器+1
地址信號:復(fù)位有效時(shí)地址歸0;地址為255且計(jì)數(shù)到最大值或者地址為255且寫使能有效或者按鍵2標(biāo)志信號拉高或者按鍵1標(biāo)志信號拉高時(shí)地址歸0;寫使能有效或讀使能為1且計(jì)數(shù)到最大值時(shí)地址+1
**頂層模塊
**
module ram
(
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 wr_en ;
wire rd_en ;
wire [7:0] addr ;
wire [7:0] wr_data ;
wire [7:0] rd_data ;
wire key1_flag ;
wire key2_flag ;
ram_ctrl ram_ctrl_inst
(
.sys_clk (sys_clk ),
.sys_rst_n (sys_rst_n ),
.key1_flag (key1_flag ),
.key2_flag (key2_flag ),
.wr_en (wr_en ),
.rd_en (rd_en ),
.addr (addr ),
.wr_data (wr_data )
);
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 ({12'd0,rd_data} ),
.seg_en (1'b1 ), //數(shù)碼管使能信號,高電平有效
.stcp (stcp ), //輸出數(shù)據(jù)存儲寄時(shí)鐘
.shcp (shcp ), //移位寄存器的時(shí)鐘輸入
.ds (ds ), //串行數(shù)據(jù)輸入
);
//---------------rom_256x8_inst--------------
ram_256x8 ram_256x8_inst
(
.aclr (~sys_rst_n ), //異步清零信號
.address (addr ), //讀寫地址線
.clock (sys_clk ), //使用系統(tǒng)時(shí)鐘作為讀寫時(shí)鐘
.data (wr_data ), //輸入寫入RAM的數(shù)據(jù)
.rden (rd_en ), //讀RAM使能
.wren (wr_en ), //寫RAM使能
.q (rd_data ) //輸出讀RAM數(shù)據(jù)
);
endmodule
實(shí)際上是幾個(gè)模塊的實(shí)例化:ram控制模塊,按鍵消抖模塊要實(shí)例化兩次,第一個(gè)模塊的key_in對應(yīng)著key[0],flag對應(yīng)key1_flag,第二個(gè)模塊的key_in對應(yīng)著key[1],flag對應(yīng)key2_flag,數(shù)碼管動(dòng)態(tài)顯示模塊將使能信號置1,ram的ip核模塊的異步清零信號置為復(fù)位信號取反,其他信號名和控制模塊中定義的信號名對接
Testbench
`timescale 1ns/1ns
module tb_ram();
//wire define
wire stcp;
wire shcp;
wire ds ;
//reg define
reg sys_clk ;
reg sys_rst_n ;
reg [1:0] key ;
//對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[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;//松開按鍵
#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;//松開按鍵
#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;//松開按鍵
#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;//松開按鍵
#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;//松開按鍵
#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;//松開按鍵
#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)
end
//sys_clk:模擬系統(tǒng)時(shí)鐘,每10ns電平取反一次,周期為20ns,頻率為50MHz
always #10 sys_clk = ~sys_clk;
//重新定義參數(shù)值,縮短仿真時(shí)間仿真
defparam ram_inst.key1_filter_inst.CNT_MAX = 5 ;
defparam ram_inst.key2_filter_inst.CNT_MAX = 5 ;
defparam ram_inst.ram_ctrl_inst.CNT_MAX = 99;
//---------------ram_inst--------------
ram ram_inst
(
.sys_clk (sys_clk ), //系統(tǒng)時(shí)鐘,頻率50MHz
.sys_rst_n (sys_rst_n ), //復(fù)位信號,低電平有效
.key (key ), //輸入按鍵信號
.stcp (stcp ), //輸出數(shù)據(jù)存儲寄時(shí)鐘
.shcp (shcp ), //移位寄存器的時(shí)鐘輸入
.ds (ds ), //串行數(shù)據(jù)輸入
);
endmodule
初始化:時(shí)鐘為高電平,復(fù)位為低電平,按鍵都為高電平表示未按下
延遲200ns后復(fù)位釋放
延遲2000ns后按下按鍵2但是模擬抖動(dòng),抖動(dòng)中有200ns的按鍵是按下狀態(tài)以便識別并拉高flag
再重復(fù)模擬按下按鍵1,2,2,1,2
重新定義參數(shù),縮短仿真時(shí)間
ram模塊實(shí)例化
波形變化
需要注意的是地址和數(shù)據(jù)需要用unsigned格式,如果用十進(jìn)制會出現(xiàn)負(fù)數(shù)
先按下的是key2,因?yàn)檫€沒寫入所以讀出的q暫時(shí)沒有數(shù)據(jù)
按下按鍵1,寫使能拉高,地址和數(shù)據(jù)都開始+1,且因?yàn)檎趯憯?shù)據(jù),讀出的q暫時(shí)沒有數(shù)據(jù)
地址線和數(shù)據(jù)255后歸0保持0且寫使能拉低
寫數(shù)據(jù)后再讀數(shù)據(jù)就能讀出數(shù)據(jù)
管腳分配
和上一篇一樣
全編譯后上板驗(yàn)證
-
FPGA
+關(guān)注
關(guān)注
1629文章
21736瀏覽量
603419 -
存儲器
+關(guān)注
關(guān)注
38文章
7492瀏覽量
163842 -
RAM
+關(guān)注
關(guān)注
8文章
1368瀏覽量
114701 -
端口
+關(guān)注
關(guān)注
4文章
964瀏覽量
32080 -
IP核
+關(guān)注
關(guān)注
4文章
327瀏覽量
49498
發(fā)布評論請先 登錄
相關(guān)推薦
評論