網上有很多的SDR SDRAM控制器的代碼,但都是基於burst1/2/4/8模式下的,這種模式下傳輸高速的相機數據還是有點拮據的,所以花了幾天把這些模式改造成了頁突發模式。我的這個控制器模型是這樣的:
圖一
這里的有兩個緩沖Wrfifo和Rdfifo,它們都是dcfifo(混合寬度異步時鍾FIFO)。上面的圖我畫的很丑,但是有些細節要注意,Wrfifo進來是8bit出去是16bit。不管是手機那種攝像頭還是工業相機camera_clk都不會很高,在30hz的640x480輸出情況下,8進16出,還是比SDRAM要慢,不會把Wrfifo塞滿。工業應用上,往往很強調實時性,所以相機的幀率會達到上百幀,這樣保證處理的數據盡量是最新的一幀。
好了,進入正題了,我們在已有的SDRAM burst1/2/4/8的控制器代碼下怎么修改成full-page burst 模式,需要注意點什么細節。
先來了解一下器件,我用的是美光的一款SDRAM,MT48LC64M4A2,資源如下圖橢圓框標示:
圖二
4Meg x 16 x 4banks ,行地址[12:0] ,列地址[8:0]。
模式寄存器配置圖:
圖三
從上面得到一個信息:full-page模式下必須把register[2:0]配置成111。
突發定義表格:
圖四
從上圖,我們可以得到兩個信息:
- full page burst不支持Interleaved,只支持順序sequential模式,這可以在Mode register設置。
- 從注釋的第一條和第五條可知道,在16位數據線模式下,頁模式可訪問的長度是512個16bit數據,也就是一行的長度column[8:0]。
要把burst1/2/4/8改成full page,我們知道最大的不同就是要改讀寫時序,先來看一下頁突發模式的寫時序圖:
圖五
這張圖說起來啰嗦,我用verilog表述一下:
reg[23:0] Moni_Addr;//[23:22] bank;[21:9] row;[8:0] clmn
reg [14:0]Address;//地址線
reg[15:0] rData;//寄存器獲取SDRAM數據總線數據
always@(posedge CLK_100Mor negedge RSTn)
……//忽略
else if( SDRAM_READ )
case( i )
0: // Send Active command with Bank and Row address
begin Command <= _ACT; Address <= Moni_Addr[23:9]; DQM <= 2'b11; i <= i + 1'b1; end
1: // Send 1 nop Clk for tRCD-20ns
begin Command <= _NOP; DQM <= 2'b11; i <= i + 1'b1; end
/***************************************/
2: // Send Read command and column address, bank address ,pull down DQM clock
begin Command <= _RD; Address <= { Moni_Addr[23:22], 4'b0000, Moni_Addr[8:0]};
DQM <= 2'b00; i <= i + 1'b1; end
3:
begin Command <= _NOP; DQM <= 2'b00; i <= i + 1'b1; end
4: // Send 2 nop Clk for CAS Latency
begin Command <= _NOP; DQM <= 2'b00; i <= i + 1'b1; end
/******************************************/
5: // Read Data
if(C1 == 511) begin C1 <= 10'd0; i <= i + 1'b1; end
else begin C1 <= C1 + 1'b1; rData <= SDRAM_DATA; isRead_Ack <= 1'b1;
DQM <= 2'b00; Command <= _NOP;
end
/******************************************/
6:// Send BurstTerm
begin Command <= _BURSTTERM; DQM <= 2'b00; isRead_Ack <= 1'b0; i <= i + 1'b1; end
7:
begin Command <= _NOP; DQM <= 2'b11; i <= i + 1'b1; end
8: // return i to 0
begin i <= 4'd0; end
/*******************************************/
上面的表述,有幾點下面說明一下:
1.full-page模式不支持Auto-precharge.這點手冊里面原文表述如下:A precharge of the bank/row that is addressed with the READ or WRITE command is automatically performed upon completion of the READ or WRITE burst, except
in the continuous page burst mode where auto precharge does not apply.
所以在i=2時刻,發送的地址里對A10置1是無效的。
2.full-page模式需要突發中斷命令來終止它,為什么?我們來看手冊原文表述:A continuous page burst continues until terminated;at the end of the page, it wraps to column 0 and continues.
它的意思是說,一個page burst操作必須在 burstterm下才停止,不然它會重新在地址(column 0)開始獲取數據。通俗點說它就會循環獲取數據!
3.在i=5的操作很有看點,也是重點:if(C1 == 511) begin C1 <= 10'd0; i <= i + 1'b1; end else begin C1 <= C1 + 1'b1; rData <= SDRAM_DATA; isRead_Ack <= 1'b1 ; DQM <= 2'b00; Command <= _NOP; end
我循環操作511次去獲取數據,即rData <= SDRAM_DATA。循環操作511次去拉高isRead_Ack,即isRead_Ack <= 1'b1;。這個isRead_Ack就是給Rdfifo的rdreq信號,就是為了拉高寫進閥門511次,把512個數據都寫進Rdfifo去。為什么拉高511次操作寫進了512個數據??呵呵,因為verilog語言是有后續效果的。Fifo的特點我這里也不多說了,用用就知道了。
再來看看寫時序圖吧:
圖六
這張寫圖感覺和讀時序圖差不多啊,呵呵,也用verilog表述一下:
always@(posedge CLK_100Mor negedge RSTn)
……//忽略
else if( SDRAM_WRITE )
case( i )
0: // Send Active Command with Bank and Row address
begin Command <= _ACT; Address <= Moni_Addr[23:9]; DQM <= 2'b11; i <= i + 1'b1; end
1: // Send 1 nop Clk for tRCD-20ns
begin Command <= _NOP; DQM <= 2'b11; i <= i + 1'b1;end
/*********************************************/
2: // Send Write command with row address, bank addr,pull down DQM 1 clk
begin Command <= _WR; Address <= { Moni_Addr[23:22], 4'b0000, Moni_Addr[8:0] };
DQM <= 2'b00; isWrite_Ack <= 1'b1; i <= i + 1'b1;end
3:
if(C1 == 510) begin C1 <= 10'd0; i <= i + 1'b1;j <= j + 1'b1;end
else begin Command <= _NOP; DQM <= 2'b00; isWrite_Ack <= 1'b1; C1 <= C1 + 1'b1; end
4: // Send BurstTerm
begin Command <= _BURSTTERM; DQM <= 2'b00; isWrite_Ack <= 1'b0; i <= i + 1'b1; end
/**********************************************/
5:
begin Command <= _NOP; DQM <= 2'b11; i <= i + 1'b1; end
6: // return i to 0
begin i <= 4'd0; end
/*********************************************/
這里也說明一下:
1.和發送SDRAM_READ命令一樣,發送SDRAM_WRITE命令時和之后的空命令NOP,DQM都是需要拉低處理的,而在之前的發送ACT命令是DON’T CARE(無所謂)。
2.isWrite_Ack是給Wrififo的wrreq信號,就是控制閥門打開,要把數據放出去,和讀的isRead_Ack性質一樣。
3.在i=2的時候,發送_WR的時候也拉高了一個時鍾的isWrite_Ack,再加上i=3時產生效果的511個高效果isWrite_Ack,這樣就一共拉高512個時鍾節拍,就是把512個16bit的數據給放出去給SDRAM。
4.頁突發寫結束時,照樣也要發送_BURSTTERM來終止寫操作!
好了,總結一下,把burst1/2/4/8修改成full-page burst,我們需要做的工作是:
- 修改Mode register的burst length和保證BT為sequential。
- 修改控制器里面的讀和寫時序,很重要這點,而且老實說要很細心,因為有些代碼的計時控制,比如我們需要滿足tRCD,CAS latency等等這些時間,一般是用計時器來控制的,這些計時器在狀態機里面循環,希望不要繞暈你。我上面對讀和寫只是做了一個順序操作的流程,讓你知道一個讀寫操作下來需要的小操作,知道這些之后就好添加修改了。
- 我這里是有Wrfifo和Rdfifo兩個dcfifo,數據在剛開始是放在fifo里面的,流程是先網Wrfifo里面寫進512個以上的16bit數據,因為是8進16出,所以其實我們要網Wrfifo的寫端至少寫1024個8bit的數據!然后對控制器發送寫請求,寫完之后,再發出讀請求,然后512個數據嘩啦啦的被裝進了Rdfifo。
說了上面三點還有最需要注意的一點,定時刷新,很重要!手冊里面原文:
Regardless of device width,the 256Mb SDRAM requires 8192 AUTO REFRESH cycles every 64ms (commercial and
industrial) or 16ms (automotive). Providing a distributed AUTO REFRESH command every 7.813μs (commercial and industrial) or 1.953μs (automotive) will meet the refresh requirement and ensure that each row is refreshed.
這款芯片,必須在64ms內完成8192次刷新。也就是說每隔7.813us就必須發一個Auto-Refresh命令。在100M的時鍾下7.813us就是781.3個CLK,my god!?這么迫切,這是什么概念,從上面的讀寫時序表述中知道:我們僅僅數據耗費了512個CLK!!!准確來說,上面讀操作耗費520個時鍾,寫操作耗費516個時鍾,還不算上真正操作時傳遞信號的消費!這說明了什么,這說明我們在一個讀或寫操作之后必須進行刷新操作,不然時間來不及,等不了兩個讀或寫操作。
所以第四點,我們必須懂得控制好自動刷新電容的操作!
好了,修改了這么多,但是修改之后怎么測試呢,外圍的我們可以加個串口模塊來打印SDRAM讀取的數據,內部信號的測試用SignalTapII,Altera自帶的邏輯分析儀,這個工具很好用。來一張測試的截圖:
圖七
這張圖CMD命令,16是_BUSTTERM,17是_NOP,11是_AUTOREFLESH.這里有個很明顯的錯誤,黑線框畫出來了,11,17,16,17,說明自動刷新電容命令之后是突發終止命令,這是很奇怪的,違背了我們的本意,刷新電容之后必然是等待讀寫命令,不可能出現終止命令。這只是個例子,來說明怎么觀察信號來修改代碼。
下面看一下,正確的信號觀察:
橢圓區1,是往Wrfifo寫600個16bit數據,Wrfifo_Req是寫請求信號,Wrfifo_Data_In是數據
橢圓區2,isWrite拉高向控制器發出寫SDRAM請求,SDRAM_Write_Ack是控制器回饋的應答信號,對應給Wrfifo的rdreq,拉高了512個周期,打開閘門放512個16bit數據。
橢圓區3,向控制器發出讀請求,isRead拉高,控制器SDRAM_Read_Ack反饋應答給Rdfifo的wrreq,拉高512個周期,放入512個數據給Rdfifo。
圖八
這個SingnalTapII文件最好采樣深度設2K,把全部信號都能觀察到。外部直觀點的,最好在Rdfifo的讀出端連個串口模塊,直接觀察打印…
圖九
結束語:在剛開始做視覺項目的時候就想弄個頁突發模式的控制器,雖然Altera的Sdram_Control_4Port還能湊合着用,但是我表示很難適應它這代碼的風格,很難改。后面在網上得了個burst8模式的控制器,這幾天試着改成了頁模式,把這些細節記錄下來。
(歡迎轉載,請注明出處 ---憤怒de狂奔)