SDRAM是個啥?
SDRAM(Synchronous Dynamic Random Access Memory),同步動态随機存儲器。
同步
是指 Memory工作需要同步時鐘,内部的指令的發送與資料的傳輸都以它為基準。
動态
是指存儲陣列需要不斷的重新整理來保證存儲的資料不丢失,因為SDRAM是通過電容來中存儲資料,而電容自然放置狀态是會有放電的,為了保證SDRAM中的資料不丢失,是以SDRAM需要在電容的電量放完之前進行重新整理。
随機
是指資料不是線性依次存儲,而是自由指定位址進行資料的讀寫。
SDRAM 結構長啥樣?
以IS42S86400B型号的SDRAM舉例說明。下圖為其内部結構
我們關注外圍的PIN以及Bank就可以了,下面逐一說明PIN定義及Bank。
PIN定義
PIN | 釋義 |
---|---|
A0 - A12 | 位址輸入,行位址A0-12,列位址A0-A9,A10用于控制Auto-precharge(自動預充電) |
BA0,BA1 | Bank位址輸入,選擇不同的BANK |
DQ0 - DQ15 | 資料I/O |
CLK | 系統時鐘輸入 |
CKE | 時鐘使能,高有效 |
CS_n | 晶片選通,低有效 |
RAS_n | 行選通,低有效 |
CAS_n | 列選通,低有效 |
WE_n | 寫使能,低有效 |
DQML | 低位元組資料輸入/輸出掩碼 |
DQMH | 高位元組資料輸入/輸出掩碼 |
Bank
一塊Bank可以了解為一片RAM,通過行列位址操作其内部的存儲單元。
SDRAM容量計算
SDRAM容量 = 資料位寬x存儲單元數量(BANK數x行位址x列位址)
即:16(DQ) x 4(Bank) x 8192(行位址範圍) x 1024(列位址範圍)= 512Mb
SDRAM如何用起來
了解基本的SDRAM的指令動作
依據IS42S86400B的器件手冊,得到基本的指令動作有:
上圖為指令的真值表,具體時序狀态,參考相應的晶片手冊。
指令動作 | 釋義 | 說明 |
---|---|---|
Device deselect (DESL) | 未指定裝置 | 未選中該裝置 |
No operation (NOP) | 空操作 | 在空閑或等待狀态下發出的指令 |
Burst stop (BST) | 突發停止 | 中斷目前執行的突發寫或者讀操作 |
Read | 讀指令 | |
Read with auto precharge | 讀指令 | 相對于上面的讀指令,A10被置為高,代表讀完自動進行預充電 |
Write | 寫指令 | |
Write with auto precharge | 寫指令 | 相對于上面的寫指令,A10被置為高,代表讀完自動進行預充電 |
Bank activate(ACT) | bank激活 | 執行激活Bank指令時,會激活該Bank和對應的行位址 |
Precharge select bank(PRE) | Bank預充電 | 選擇性的為Bank進行充電 |
Precharge all banks(PALL) | Bank預充電 | 為所有bank進行預充電 |
CBR Auto-Refresh(REF) | 自動重新整理 | 記憶體重新整理指令,固定周期,在此期間不能執行任何其他指令,該指令每64ms至少執行8192次 |
Self_Refresh(SELF) | 自重新整理 | 自重新整理在無有效時鐘輸入時使用,維持儲存設備在低功耗狀态,且可以保持資料 |
Mode register set(MBS) | 模式寄存器設定 | 該模式下配置記憶體參數,突發類型及長度等 |
通過指令動作組裝一個簡單SDRAM讀寫流程
凡是預則立,不預則廢,首先我們先規劃一個大緻的操作流程,在針對每個流程去看其中具體需要執行的指令動作,且在不同的指令動作間,有具體的操作周期間隔,這部分需依據手冊中辨別去編寫代碼。
初始化 init_s
這個狀态需要配置一切SDRAM工作所需要的參數,通過MBS指令動作完成,進行該指令動作時,要依據手冊中的動作時序進行。
- 上電後,執行NOP指令,維持至少200us。
- 然後進行所有bank的預充電動作-PALL。
- 執行至少8次的自動重新整理指令-REF,間隔期執行NOP。
- 執行MBS指令,配置參數。
- 配置完參數進入正常操作流程,空閑時執行NOP,需要進行讀寫操作時,激活相應bank和行位址。 MBS指令的配置,按照晶片手冊進行配置 這裡我們配置為
Register_Map <= "000" & '0' & "00" & "010" & '0' & "010";
--注意,這裡值包含了位址段;BA0,BA1應配置為0。
由此,這是一個可程式設計突發長度的标準操作模式下的SDRAM晶片,其中CAS的潛伏期為2,突發類型為有序突發,長度為4。
空閑态 Idle_s
該狀态下預設執行NOP操作,同時等待跳入自動重新整理和讀寫操作。
-------------------------------------------------------------------------------
-- NOP時的信号狀态
-------------------------------------------------------------------------------
sdram_RAS_n <= '1';
sdram_CAS_n <= '1';
sdram_WE_n <= '1';
sdram_Addr <= (others => '0');
sdram_BA <= "00";
sdram_DQM <= "1111";
自動重新整理 Auto_Ref_s
該狀态下執行自動重新整理指令,手冊中标注,該指令每64ms至少執行8192次,可以看到自動重新整理按照行位址掃描,64ms内要把所有的行位址重新整理完畢。這裡重新整理的間隔沒必要卡的這麼死,設定5us執行一次,64ms内可以執行12800次,足夠,也能為讀寫操作和自動重新整理的指令沖突提供緩沖。
-------------------------------------------------------------------------------
-- 自動重新整理時的信号狀态
-------------------------------------------------------------------------------
process(sys_clk)
begin
if(rising_edge(sys_clk))then
if(sys_rst = '1')then
auto_ref_RAS_n <= '1';
auto_ref_CAS_n <= '1';
auto_ref_WE_n <= '1';
elsif(ref_pstate = auto_ref_s)then
auto_ref_RAS_n <= '0';
auto_ref_CAS_n <= '0';
auto_ref_WE_n <= '1';
else
auto_ref_RAS_n <= '1';
auto_ref_CAS_n <= '1';
auto_ref_WE_n <= '1';
end if;
end if;
end process;
auto_ref_DQM <= "1111";
auto_ref_DQ <= (others => '0');
auto_ref_BA <= "00";
auto_ref_Addr <= (others => '0');
讀/寫操作(Active_s + Read_op_s / Write_op_s + Percharge_s)
關于讀寫操作,有很多組合,這裡舉了一種比較簡單的方式,更多複雜的組合可以在手冊的引導下完成。
本章節所說的讀或是寫操作過程如下:
- 擡手就是激活對應的bank和行位址。
- 然後通過WE_n信号的狀态,決定讀還是寫指令。
- 接一個預充電指令就可以回到空閑态。
- 持續輸出NOP指令,等待下一次讀寫操作,或者自動重新整理指令。
上述過程轉變時,存在一定的時間間隔的要求,具體檢視相應手冊。
簡單的讀指令操作
在T0前需要執行激活指令,圖中沒有展示該過程,而是着重展示了預充電後,需要等待Trp時間後才能繼續操作下一次激活指令。
-------------------------------------------------------------------------------
--激活+讀操作+預充電的信号狀态
-------------------------------------------------------------------------------
elsif(task_pstate = Active_s)then
r_sdram_RAS_n <= '0';
r_sdram_CAS_n <= '1';
r_sdram_WE_n <= '1';
r_sdram_Addr <= usr_sdram_row_addr;
r_sdram_BA <= usr_sdram_ba;
elsif(task_pstate = Read_s and sta_cnt = 0)then
r_sdram_RAS_n <= '1';
r_sdram_CAS_n <= '0';
r_sdram_WE_n <= '1';
r_sdram_Addr <= "0000" & usr_sdram_col_addr(8 downto 0);
r_sdram_BA <= usr_sdram_ba;
elsif(task_pstate = Precharge_s)then
r_sdram_RAS_n <= '0';
r_sdram_CAS_n <= '1';
r_sdram_WE_n <= '0';
r_sdram_Addr <= "00100" & x"00"; --A10-All Back
r_sdram_BA <= usr_sdram_ba;
簡單的寫指令操作
在T0前需要執行激活指令,圖中沒有展示該過程,而是着重展示了預充電後,需要等待Trp時間後才能繼續操作下一次激活指令。
-------------------------------------------------------------------------------
--激活+寫操作+預充電的信号狀态
-------------------------------------------------------------------------------
elsif(task_pstate = Active_s)then
w_sdram_RAS_n <= '0';
w_sdram_CAS_n <= '1';
w_sdram_WE_n <= '1';
w_sdram_Addr <= wr_sdram_row_addr;
w_sdram_BA <= wr_sdram_ba_addr;
elsif(task_pstate = Write_s and sta_cnt = 0)then
w_sdram_RAS_n <= '1';
w_sdram_CAS_n <= '0';
w_sdram_WE_n <= '0';
w_sdram_Addr <= wr_sdram_col_addr;
w_sdram_BA <= wr_sdram_ba_addr;
elsif(task_pstate = Precharge_s)then
w_sdram_RAS_n <= '0';
w_sdram_CAS_n <= '1';
w_sdram_WE_n <= '0';
w_sdram_Addr <= "00100" & x"00"; --A10-All Back
w_sdram_BA <= wr_sdram_ba_addr;
SDRAM代碼編寫的結建構議
建議分為五個子產品,即:
頂層子產品
仲裁子產品
寫子產品
讀子產品
自動重新整理子產品
使子產品間各司其職。
Sdram_Auto_Ref.vhd
Sdram_Inital.vhd
Sdram_Read.vhd
Sdram_Top.vhd
Sdram_Write.vhd
Sdram_arbit.vhd
仲裁輸出結構
sdram_CLK <= not sys_clk; --100Mhz
sdram_CS_n <= '0';
sdram_CKE <= '1';
process(sys_clk)
begin
if(rising_edge(sys_clk))then
if(sys_rst = '1')then
sdram_RAS_n <= '1';
sdram_CAS_n <= '1';
sdram_WE_n <= '1';
sdram_Addr <= (others => '0');
sdram_BA <= "00";
sdram_dout <= (others => '0');
sdram_DQM <= x"0";
elsif(task_pstate = idle_s and init_sdram_done = '0')then
sdram_RAS_n <= init_sdram_RAS_n;
sdram_CAS_n <= init_sdram_CAS_n;
sdram_WE_n <= init_sdram_WE_n;
sdram_Addr <= init_sdram_Addr;
sdram_BA <= init_sdram_BA;
sdram_dout <= init_sdram_DQ;
sdram_DQM <= init_sdram_DQM;
elsif(task_pstate = sdram_auto_ref_s)then
sdram_RAS_n <= auto_ref_RAS_n;
sdram_CAS_n <= auto_ref_CAS_n;
sdram_WE_n <= auto_ref_WE_n;
sdram_Addr <= auto_ref_Addr;
sdram_BA <= auto_ref_BA;
sdram_dout <= auto_ref_DQ;
sdram_DQM <= "1111";
elsif(task_pstate = sdram_read_s)then
sdram_RAS_n <= r_sdram_RAS_n;
sdram_CAS_n <= r_sdram_CAS_n;
sdram_WE_n <= r_sdram_WE_n;
sdram_Addr <= r_sdram_Addr;
sdram_BA <= r_sdram_BA;
sdram_DQM <= r_sdram_DQM;
elsif(task_pstate = sdram_write_s)then
sdram_RAS_n <= w_sdram_RAS_n;
sdram_CAS_n <= w_sdram_CAS_n;
sdram_WE_n <= w_sdram_WE_n;
sdram_Addr <= w_sdram_Addr;
sdram_BA <= w_sdram_BA;
sdram_dout <= w_sdram_DQ;
sdram_DQM <= w_sdram_DQM;
else
sdram_RAS_n <= '1';
sdram_CAS_n <= '1';
sdram_WE_n <= '1';
sdram_Addr <= (others => '0');
sdram_BA <= "00";
sdram_DQM <= "1111";
end if;
end if;
end process;
r_sdram_DQ <= sdram_DQ;
sdram_DQ <= sdram_dout when task_pstate = sdram_write_s or init_sdram_done = '0'
else "ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ";
資料分享
IS42S86400B器件手冊: https://download.csdn.net/download/sgxb2028/12747768
SDRAM控制器代碼-VHDL: https://download.csdn.net/download/sgxb2028/12747783