一. 簡(jiǎn)介
本例將介紹SDRAM的使用。SDRAM是一個(gè)存儲(chǔ)器件,存儲(chǔ)容量大,存儲(chǔ)速度比較快,速度可達(dá)100M,特別適合用來當(dāng)做視頻或者音頻中的存儲(chǔ)器件。
在采集到OV5640傳輸過來的圖像數(shù)據(jù)的時(shí)候,F(xiàn)PGA的片上資源是沒有那么大的存儲(chǔ)空間進(jìn)行存儲(chǔ)的,必須通過外部的存儲(chǔ)器件進(jìn)行存儲(chǔ)。恰好開發(fā)板上有一片SDRAM,所以用此來進(jìn)行存儲(chǔ),一般而言入門級(jí)的FPGA開發(fā)板上都是配置的SDRAM,中高級(jí)一點(diǎn)的是DDR2(alter開發(fā)板),DDR3(xilinx開發(fā)板)。
所以本例將實(shí)現(xiàn)一個(gè)完善的SDRAM存儲(chǔ)控制器,供大家查看。
二. SDRAM接口信號(hào)
從下面框圖中可以看出,SDRAM接口信號(hào)可以分為四大類:控制信號(hào),地址信號(hào),數(shù)據(jù)輸入輸出信號(hào),掩碼信號(hào)。下面將詳細(xì)介紹各個(gè)命令的作用。
以上就是SDRAM的全部接口信號(hào)了,并沒有特別復(fù)雜。
下圖是SDRAM的所有命令,在對(duì)SDRAM進(jìn)行操作的時(shí)候,需要使用到。
然后在。v文件中先將其定義出來,方便后續(xù)使用
三. SDRAM上電初始化
上電后,沒有任何時(shí)序上的操作,只需要延時(shí)100us(手冊(cè)上要求最小為100us),使輸入輸出電平達(dá)到穩(wěn)定,即可,在此期間,發(fā)送的命令最好為NOP。
這里初始化包括了初始化和加載模式寄存器,我認(rèn)為初始化,就是加載模式寄存器。
?。?)模式寄存器
模式寄存器的定義如下,通過地址線給出,每位都有其具體的含義。
0-2 bit:定義突發(fā)長(zhǎng)度,每給一個(gè)讀/寫命令后,輸出/輸入的數(shù)據(jù)大小
4-6bit :定義潛伏期,發(fā)出讀命令后,延時(shí)多少個(gè)周期給讀數(shù)據(jù),僅對(duì)讀操作有效
10-12bit: 保留,始終置高即可
其余位始終保持為0即可。模式寄存器的內(nèi)容就這么多。
?。?)初始化
下圖是初始化的過程,按照?qǐng)D示要求依次發(fā)送對(duì)應(yīng)的命令即可,命令與命令之間的間隔時(shí)間手冊(cè)上都有說明,取的時(shí)候,可以適當(dāng)取大。模式寄存器的A信號(hào)被分成了兩部分A10和其他,可以看到A10在PRECHARGE階段有特殊作用,一般為1,對(duì)所有的bank都進(jìn)行預(yù)充電。
在模式寄存器中A10也是為1的,所以在整個(gè)初始化過程中,A可以直接賦值為模式寄存器的值。
實(shí)現(xiàn)過程如下,也是非常簡(jiǎn)潔的,編寫好初始化模塊看,可以直接仿真,這里多虧了大佬寫的sdram模型(就是一個(gè)。v文件),不用上板,可以直接仿真看代碼編寫是否正確。
仿真輸出如下,可以看到和時(shí)序圖中,命令發(fā)送過程是一樣,同時(shí)也可以看到,模式寄存器配置的具體參數(shù),非常方便,初始化模塊就順利的編寫完成了。
四. 刷新模塊
由于sdram的特殊結(jié)構(gòu),sdram在使用的過程中,需要每隔一段時(shí)間,對(duì)所有的存儲(chǔ)區(qū)域進(jìn)行一次刷新操作(充電),否則內(nèi)部存儲(chǔ)的數(shù)據(jù)會(huì)丟失,這將會(huì)成為后面設(shè)計(jì)的一大難點(diǎn)。
根據(jù)手冊(cè)得知每64ms需要完成8192次刷新操作,也就是下面的時(shí)序圖需要在64ms內(nèi)運(yùn)行8192次,平均下來7us就要進(jìn)行一次,這個(gè)時(shí)間需要記住非常重要。
同樣也是根據(jù)手冊(cè)給出的時(shí)序圖進(jìn)行編寫代碼,一共需要發(fā)送三個(gè)命令
代碼實(shí)現(xiàn)如下,也是非常容易實(shí)現(xiàn)的。
從圖中可以看出,每隔7090ns進(jìn)行一次刷新,滿足要求。
至此,初始化和刷新模塊編寫完成,這兩部分只需要按照手冊(cè)上給出的時(shí)序圖來編寫代碼即可,比較容易即可完成,后面的讀寫模塊會(huì)復(fù)雜一些。
五. 寫模塊
讀模塊的時(shí)序圖如下,截取的是沒有auto precharge操作的,也就是在數(shù)據(jù)寫完后,需要手動(dòng)發(fā)送一個(gè)precharge命令。同樣可以讓sdram自動(dòng)完成這個(gè)操作,只需要在發(fā)送write命令的時(shí)候,將A10拉高即可,這樣在發(fā)送完數(shù)據(jù)后,就可以直接結(jié)束了,不用發(fā)送precharge命令。本次介紹的是需要發(fā)送precharge命令。
設(shè)計(jì)時(shí)需要清楚以下兩個(gè)問題
發(fā)送過程中,需要切換行地址或者bank的時(shí)候,應(yīng)該怎樣操作
發(fā)送過程中,突然來了刷新請(qǐng)求時(shí),該如何處理
先對(duì)第一個(gè)問題進(jìn)行說明一下,在sdram中,行地址和bank是發(fā)送ACTIVE命令時(shí)指定的,發(fā)送write命令時(shí),就可以指定列地址了,如下BL=1。也就是說切換行地址或bank時(shí)需要重新發(fā)送ACTIVE命令。ps:寫操作是沒有潛伏期的。
手冊(cè)中也給出了這部分的時(shí)序圖,如下。需要注意的一點(diǎn)是,它這是使能了auto precharge,所以數(shù)據(jù)發(fā)送完成后,沒有發(fā)送precharge命令,就發(fā)送了ACTIVE命令來切換行地址或bank了。沒有使能的情況下,需要加上precharge地址,然后再延時(shí)tRCD,發(fā)送ACTIVE命令,這點(diǎn)需要注意。
第二個(gè)問題,當(dāng)刷新請(qǐng)求來時(shí),這個(gè)時(shí)候當(dāng)然是要暫停發(fā)送數(shù)據(jù),需要保存已經(jīng)發(fā)送數(shù)據(jù)的個(gè)數(shù),以及當(dāng)前發(fā)送的地址和bank。然后在刷新結(jié)束后,繼續(xù)發(fā)送數(shù)據(jù)。
模塊框圖和狀態(tài)機(jī)如下。在write_data_en使能的情況下,外部輸入數(shù)據(jù)進(jìn)來,其余時(shí)刻輸入的數(shù)據(jù)無效,相當(dāng)于一個(gè)握手信號(hào)。sdram模式寄存器配置的是突發(fā)長(zhǎng)度為1,所以這里單次寫突發(fā)長(zhǎng)度是沒有大小限制的。
sdram_write
?。?/p>
input sdram_clk,
input rst_n,
input sdram_write_req, //寫請(qǐng)求
output sdram_write_ack, //寫響應(yīng)
input sdram_write_pause, //寫暫停信號(hào),轉(zhuǎn)去刷新操作
output reg sdram_write_pause_ack, //寫暫停響應(yīng),成功暫停
input[24:0] write_addr, //寫入地址
input[15:0] write_data, //寫入數(shù)據(jù) {bank[1:0s],row[12:0],clo[9:0]}
input[9:0] write_burst_length, //單次寫突發(fā)長(zhǎng)度 //可以為sdram大小
output write_data_en, //寫入數(shù)據(jù)有效輸出
//sdram接口
output[3:0] sdram_write_cmd,
output[12:0] sdram_write_addr,
output[1:0] sdram_write_ba,
output[15:0] sdram_write_data
?。?/p>
localparam S_IDEL = 'd0; //空閑態(tài)
localparam S_ACTIVE = 'd1; //激活態(tài)
localparam S_WRITE = 'd2; //寫數(shù)據(jù)
localparam S_PAUSE = 'd3; //寫暫停
localparam S_ALTERNATE = 'd4; //換行換bank緩存
localparam S_PRECHARGE = 'd5; //寫結(jié)束后或切換行列地址,發(fā)送precharge命令
localparam S_END = 'd6; //寫結(jié)束
always@(*)
begin
case(state)
S_IDEL:
if( sdram_write_req == 1'b1 )
next_state <= S_ACTIVE;
else
next_state <= S_IDEL;
S_ACTIVE:
if( sdram_write_pause == 1'b1 ) //寫暫停信號(hào),轉(zhuǎn)去刷新操作
next_state <= S_PAUSE;
else if( time_cnt == `tRCD )
next_state <= S_WRITE;
else
next_state <= S_ACTIVE;
S_WRITE:
if( sdram_write_pause == 1'b1 ) //寫暫停信號(hào),轉(zhuǎn)去刷新操作
next_state <= S_PAUSE;
else if( alternating_bank_row_en == 1'b1)
next_state <= S_PRECHARGE;
else if( write_burst_length_cnt == write_burst_length )
next_state <= S_PRECHARGE;
else
next_state <= S_WRITE;
S_PAUSE:
if( sdram_write_pause == 1'b0 ) //刷新操作結(jié)束
next_state <= S_ACTIVE;
else
next_state <= S_PAUSE;
S_ALTERNATE:
if( time_cnt == `tRCD)
next_state <= S_ACTIVE;
else
next_state <= S_ALTERNATE;
S_PRECHARGE:
if( time_cnt == `tRP + `tWR)
if( write_burst_length_cnt >= write_burst_length)
next_state <= S_END;
else
next_state <= S_ALTERNATE;
else
next_state <= S_PRECHARGE;
S_END:
next_state <= S_IDEL;
default : next_state <= S_IDEL;
endcase
end
最后通過仿真,確認(rèn)實(shí)現(xiàn)正確,第一幅圖是寫過程進(jìn)行刷新操作,第二幅圖是,寫過程切換行地址
六. 讀模塊
讀模塊過程的編寫和寫模塊是一模一樣的,不過需要注意的是讀模塊有潛伏期,命令發(fā)送和數(shù)據(jù)輸出相差CL個(gè)時(shí)鐘周期,讀數(shù)據(jù)的時(shí)候,需要將這個(gè)延時(shí)加入其中,可以看到接口信號(hào)和寫模塊是一樣。不過對(duì)數(shù)據(jù)進(jìn)行采樣的時(shí)候,需要使用輸入到sdram中的時(shí)鐘,這需要注意。
sdram_read
(
input sdram_clk,
input rst_n,
input sdram_read_req, //讀請(qǐng)求
output sdram_read_ack, //讀響應(yīng)
input sdram_read_pause, //讀暫停信號(hào),轉(zhuǎn)去刷新操作
output reg sdram_read_pause_ack, //讀暫停響應(yīng),成功暫停
input[24:0] read_addr, //讀入地址
output[15:0] read_data, //讀出數(shù)據(jù) {bank[1:0s],row[12:0],clo[9:0]}
input[9:0] read_burst_length, //單次讀突發(fā)長(zhǎng)度 //可以為sdram大小
output read_data_en, //讀數(shù)據(jù)有效輸出
//sdram接口
output[3:0] sdram_read_cmd,
output[12:0] sdram_read_addr,
output[1:0] sdram_read_ba,
input[15:0] sdram_read_data
);
通過仿真輸出,確定突發(fā)讀期間,換行以及刷新完全正確
至此SDRAM模塊的編寫就完成了,頂層框圖如下,至于在外部如何進(jìn)行封裝,那就看不同的需求了。
SDRAM_TOP(
input sys_clk, //sdram的系統(tǒng)時(shí)鐘 100M
input rst_n, //異步復(fù)位信號(hào)
//讀接口
input read_req,
output read_ack,
input[24:0] read_addr,
input[9:0] read_burst_length,
output[15:0] read_data,
output read_data_en,
//寫接口
input write_req,
output write_ack,
input[24:0] write_addr,
input[9:0] write_burst_length,
input[15:0] write_data,
output write_data_en,
//sdram接口
output sdram_clk, //sdram clock
output sdram_cke, //sdram clock enable
output sdram_cs_n, //sdram chip select
output sdram_we_n, //sdram write enable
output sdram_cas_n, //sdram column address strobe
output sdram_ras_n, //sdram row address strobe
output[1:0] sdram_dqm, //sdram data enable
output[1:0] sdram_ba, //sdram bank address
output[12:0] sdram_addr, //sdram address
inout[15:0] sdram_dq //sdram data
?。?/p>
結(jié)束
更多信息可以來這里獲取==>>電子技術(shù)應(yīng)用-AET<<