天天看點

FPGA視角--SDRAMSDRAM是個啥?SDRAM 結構長啥樣?SDRAM如何用起來

SDRAM是個啥?

SDRAM(Synchronous Dynamic Random Access Memory),同步動态随機存儲器。

同步

是指 Memory工作需要同步時鐘,内部的指令的發送與資料的傳輸都以它為基準。

動态

是指存儲陣列需要不斷的重新整理來保證存儲的資料不丢失,因為SDRAM是通過電容來中存儲資料,而電容自然放置狀态是會有放電的,為了保證SDRAM中的資料不丢失,是以SDRAM需要在電容的電量放完之前進行重新整理。

随機

是指資料不是線性依次存儲,而是自由指定位址進行資料的讀寫。

SDRAM 結構長啥樣?

以IS42S86400B型号的SDRAM舉例說明。下圖為其内部結構

FPGA視角--SDRAMSDRAM是個啥?SDRAM 結構長啥樣?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,通過行列位址操作其内部的存儲單元。

FPGA視角--SDRAMSDRAM是個啥?SDRAM 結構長啥樣?SDRAM如何用起來

SDRAM容量計算

SDRAM容量 = 資料位寬x存儲單元數量(BANK數x行位址x列位址)

即:16(DQ) x 4(Bank) x 8192(行位址範圍) x 1024(列位址範圍)= 512Mb

SDRAM如何用起來

了解基本的SDRAM的指令動作

依據IS42S86400B的器件手冊,得到基本的指令動作有:

FPGA視角--SDRAMSDRAM是個啥?SDRAM 結構長啥樣?SDRAM如何用起來

上圖為指令的真值表,具體時序狀态,參考相應的晶片手冊。

指令動作 釋義 說明
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讀寫流程

凡是預則立,不預則廢,首先我們先規劃一個大緻的操作流程,在針對每個流程去看其中具體需要執行的指令動作,且在不同的指令動作間,有具體的操作周期間隔,這部分需依據手冊中辨別去編寫代碼。

FPGA視角--SDRAMSDRAM是個啥?SDRAM 結構長啥樣?SDRAM如何用起來

初始化 init_s

這個狀态需要配置一切SDRAM工作所需要的參數,通過MBS指令動作完成,進行該指令動作時,要依據手冊中的動作時序進行。

  1. 上電後,執行NOP指令,維持至少200us。
  2. 然後進行所有bank的預充電動作-PALL。
  3. 執行至少8次的自動重新整理指令-REF,間隔期執行NOP。
  4. 執行MBS指令,配置參數。
  5. 配置完參數進入正常操作流程,空閑時執行NOP,需要進行讀寫操作時,激活相應bank和行位址。
    FPGA視角--SDRAMSDRAM是個啥?SDRAM 結構長啥樣?SDRAM如何用起來
    MBS指令的配置,按照晶片手冊進行配置
    FPGA視角--SDRAMSDRAM是個啥?SDRAM 結構長啥樣?SDRAM如何用起來
    這裡我們配置為
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)

關于讀寫操作,有很多組合,這裡舉了一種比較簡單的方式,更多複雜的組合可以在手冊的引導下完成。

本章節所說的讀或是寫操作過程如下:

  1. 擡手就是激活對應的bank和行位址。
  2. 然後通過WE_n信号的狀态,決定讀還是寫指令。
  3. 接一個預充電指令就可以回到空閑态。
  4. 持續輸出NOP指令,等待下一次讀寫操作,或者自動重新整理指令。

上述過程轉變時,存在一定的時間間隔的要求,具體檢視相應手冊。

簡單的讀指令操作

在T0前需要執行激活指令,圖中沒有展示該過程,而是着重展示了預充電後,需要等待Trp時間後才能繼續操作下一次激活指令。

FPGA視角--SDRAMSDRAM是個啥?SDRAM 結構長啥樣?SDRAM如何用起來
-------------------------------------------------------------------------------
--激活+讀操作+預充電的信号狀态
-------------------------------------------------------------------------------
      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時間後才能繼續操作下一次激活指令。

FPGA視角--SDRAMSDRAM是個啥?SDRAM 結構長啥樣?SDRAM如何用起來
-------------------------------------------------------------------------------
--激活+寫操作+預充電的信号狀态
-------------------------------------------------------------------------------
      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