DRAM(Dynamic Random Access Memory)'ler SRAM'lere göre verileri mantık hücreleri yerine kondansatörlerde tutarlar. Bu nedenle üretimlerinde SRAM'lere göre çok daha az transistör kullanılır. Daha az transistör kullanılması ile die üzerine daha fazla hafıza hücresi eklenebilmektedir. DRAM'lerin üretim maliyeti hafıza kapasitesine göre SRAM'lerden çok daha düşük olmasından dolayı DRAM'ler yaygın olarak kullanılır.
DRAM'lerde veriler kondansatörlerde tutulduğunda dolayı verilerin kaybolmaması için belirli aralıklarla bütün kondansatörlerin yeniden şarj edilmesi gerekir. Bu işleme Yenileme denir. Yenileme işlemleri DRAM'in çalışma süresinin bir miktarını harcar. DRAM'ler ile SRAM'lerin arasındaki en büyük farklardan biride yenileme işlemidir.
DRAM'de veriler bank'larda tutulur. Her bank satırlardan, her satırda sütunlardan oluşur.
Satır ve bank adresi RAS(Row Address Strobe) sinyali ile, sütun ise CAS(Column Address Strobe) sinyali ile seçilir. Satır adresinin seçilmesi ile satırdaki bütün sütunlar sense amplifier'a yüklenir. Yükleme işlemi (Row Command Delay) sürede gerçekleşir. Bu işlem ACT (Activate) olarak adlandırılır.
Sense amplifier'dan verilere erişilmesi (Column Latency) sürede gerçekleşir.
Satır ile yapılacak işlemler bittiğinde sense amlifier'daki verilerin satıra geri yüklenmesi gerekir. Bu işlem PRE (Precharge) olarak adlandırılır ve (Row Precharge Delay) sürede gerçekleşir.
Kondansatörlerdeki yükün azalıp veri kaybına yol açmaması için belirli aralıklarla bütün satırlardaki kondansatörlerin yeniden doldurulması gerekir. Her bir satırın yenileme işlemi (Row Cycle Delay) sürede gerçekleşir.
FPGA Üzerinde SDR SDRAM Kontrolcüsü Tasarımı
VHDL Kodu:
------------------- SDRAM_Controller.vhd ----------------- -- Bertan Taşkın -- 1.7.2017 -- -- SDR SDRAM Kontrolcüsü. SDRAM'e ait parametreler değiştirilerek -- farklı RAM entegreleri kontrol edilebilir. -- -- Okuma işleminde okunaca veri sayısı PORT_BURST ile belirlenir. -- Toplam okunacak veri sayısı = 2^PORT_BURST. Okunan veriler sıralı -- biçimde okunur. -- -- Yazma işlemi tek tek yapılır. Yazılacak Row ve Bank açık ise -- yazma işlemi 1 cycle'da gerçekleşir. -- -- PORT_BUSY '1' olduğunda, PORT_ADDRESS ve PORT_DATAIN sinyallerinin -- değiştirilmemesi gerekir. Aksi halde okuma ve yazma işlemleri hatalı -- gerçekleşir. -- -- PORT_ADDRESS, (Bank & Row & Column) şeklinde parçalara bölünür. -- --Kütüphaneler library IEEE; use IEEE.STD_LOGIC_1164.ALL; USE ieee.numeric_std.ALL; use ieee.math_real.all; entity SDRAM_Controller is port(Clk, Enable : in std_logic; --Clock ve Clock Enable Girişi RAMCLK, CKE : out std_logic; --SDRAM'in Clock ve Clock Enable Çıkışları CS, RAS, CAS, WE : out std_logic; --SDRAM'in CS, RAS, CAS ve WE Çıkışları DQMH, DQML : out std_logic; --SDRAM'in DQM Çıkışları BA : out std_logic_vector(1 downto 0); --SDRAM'in Bank Çıkışları ADDRESS : out std_logic_vector(12 downto 0); --SDRAM'in Adres Çıkışı DATA : inout std_logic_vector(15 downto 0); --SDRAM'in Data Hattı PORT_ADDRESS : in std_logic_vector(23 downto 0); --İşlem Yapılacak Adres PORT_DATAIN : in std_logic_vector(15 downto 0); --Yazma İşlemi için Data Girişi PORT_DATAOUT : out std_logic_vector(15 downto 0); --Okuma İşlemi için Data Çıkışı PORT_RW, PORT_ENABLE : in std_logic; --Okuma yada Yazma Yapılacağını Belirten Girişler PORT_BURST : in std_logic_vector(2 downto 0); --Okuma Uzunluğu Girişi, Okunacak Veri Sayısı = 2^Burst PORT_BUSY, PORT_READY : out std_logic); --Meşgul Çıkışı ve Veri Hazır Çıkışı end SDRAM_Controller; architecture Behavioral of SDRAM_Controller is --IS42S16160G'ye göre ayarlanmış zamanlama parametreleri --(2-2-2-6) ------------------- SDRAM Parametreleri ------------------- constant Freq : integer := 100000000; --Çalışma Frekansı constant CL : integer := 2; --CAS Gecikmesi constant tRCD : integer := 2; --ACT -> RW Gecikmesi constant tRP : integer := 2; --PRE -> ACT Gecikmesi constant tRAS : integer := 6; --ACT -> PRE Gecikmesi constant tRC : integer := 8; --REF Süresi (tRP + tRAS) constant tREF : integer := 64; --Yenileme Periyodu (ms cinsinden) ------------------------------------------------------------ --Mode Registeri constant ModeRegisterCASLatency : std_logic_vector(2 downto 0) := std_logic_vector(to_unsigned(CL, 3)); constant ModeRegister : std_logic_vector(12 downto 0) := "000000" & ModeRegisterCASLatency & "0000"; --Kaç Cycle'da bir yenileme işleminin yapılacağı belirten sabit constant RefCycle : integer := tREF * (Freq / 1000) / 8192; --Başlangıç işlmeleri için gerekli olan 100us'nin kaç cycle'a denk --geldiğini belirten sabit constant InitializeDelay : integer := Freq / 10000; --Yenileme yapılmasının gerektiğini belirten sinyaller signal NeedRefresh : std_logic := '0'; signal ForceRefresh : std_logic := '0'; --Aktif olan row ve bank signal ActiveRow : std_logic_vector(12 downto 0) := (others=>'0'); signal ActiveBank : std_logic_vector(1 downto 0) := (others=>'0'); --Herhangi bir bankın aktif olup olmadığını belirten sinyal signal AllBankPrecharged : std_logic := '0'; --State Machine komutları type COMMAND_TYPE is (NOP, BST, RD, WR, ACT, PRE, REF, MRS); type STATE_TYPE is (IDLE, INITIALIZE, READ_DATA, WRITE_DATA, REFRESH); --DATA hattının kontrol eden tri-state buffer signal DataBuffer : std_logic := '1'; signal DataS : std_logic_vector(15 downto 0); --İlk enerji verildiğinde gerekli başlangıç işlemleri yapılmalıdır signal NeedInitialize : std_logic := '1'; signal BurstInt : integer range 0 to 127; begin --RAM, Clk ile aynı frekansda RAMCLK <= Clk; --RAM'in clock sinyali sürekli aktif CKE <= '1'; --RAM'in DATA bufferı sürekli aktif DQMH <= '0'; DQML <= '0'; --DATA hattını kontrol eden tri-state buffer with DataBuffer select DATA <= (others=>'Z') when '1', DataS when others; --İstenen burst sayısının integer hali BurstInt <= 2**to_integer(unsigned(PORT_BURST)); --Bütün işlmelerin yapıldığı process process(Clk, Enable) variable Command : COMMAND_TYPE := NOP; variable State : STATE_TYPE := IDLE; variable InitializeCnt : integer range 0 to 5 := 0; variable StateCnt : integer range 0 to 3 := 0; variable DelayCnt : integer range 0 to InitializeDelay - 1 := 0; variable RefreshCnt : integer range 0 to RefCycle - 1 := 0; --Sayaçlar variable tRCCnt : integer range 0 to tRC - 1 := 0; --tRC_Complete sinyali resetlendiğinde tRC sayacı aktif olur variable tRC_Complete : std_logic := '1'; variable tRPCnt : integer range 0 to tRP - 1 := 0; variable tRP_Complete : std_logic := '1'; variable tRCDCnt : integer range 0 to tRCD - 1 := 0; variable tRCD_Complete : std_logic := '1'; variable tRASCnt : integer range 0 to tRAS - 1 := 0; variable tRAS_Complete : std_logic := '1'; variable CLCnt : integer range 0 to CL := 0; variable CL_Complete : std_logic := '1'; variable BurstCnt : integer range 0 to 127 := 0; variable BurstCnt2 : integer range 0 to 127 := 0; variable ReadBuffer : std_logic := '0'; begin --Okuma işlemi Clk sinyalinin yükselen kenarı ile yapılır if rising_edge(Clk) then if ReadBuffer = '1' then PORT_DATAOUT <= DATA; PORT_READY <= '1'; else PORT_READY <= '0'; end if; end if; --Diğer bütün işlemler Clock sinyalinin düşen kenarı ile tetiklenir if falling_edge(Clk) and Enable = '1' then --------------- CAS Sayacı ------------------- if CL_Complete = '0' then --CAS gecikmesi bitene kadar bekle if CLCnt < CL - 1 then CLCnt := CLCnt + 1; else --DATA hattından BurstInt kadar veri oku if BurstCnt2 < BurstInt then --Okuma bufferı aktif ReadBuffer := '1'; --Eğer son veri okunuyor ise bitiş işlemlerini yap if BurstCnt2 = BurstInt - 1 then BurstCnt2 := 0; PORT_BUSY <= '0'; State := IDLE; StateCnt := 0; CLCnt := 0; CL_Complete := '1'; --Değil ise Burst sayacı arttırılır else BurstCnt2 := BurstCnt2 + 1; end if; end if; end if; else ReadBuffer := '0'; end if; ----------------------------------------------- --------------- Komutların Çözülmesi ----------- if State = IDLE then --Başlangıç işlemleri if NeedInitialize = '1' then State := INITIALIZE; PORT_BUSY <= '1'; --Yenilemeye zorla elsif ForceRefresh = '1' then State := REFRESH; PORT_BUSY <= '1'; --Yazma elsif PORT_ENABLE = '1' and PORT_RW = '0' then State := WRITE_DATA; PORT_BUSY <= '1'; --Okuma elsif PORT_ENABLE = '1' and PORT_RW = '1' then State := READ_DATA; PORT_BUSY <= '1'; --Yenileme elsif NeedRefresh = '1' then State := REFRESH; PORT_BUSY <= '1'; --Yapılması gereken bir komut yok ise IDLE'de kal else State := IDLE; end if; end if; ------------------------------------------------- --Boşta if State = IDLE then Command := NOP; DataBuffer <= '1'; --Başlangıç İşlemleri elsif State = INITIALIZE then DataBuffer <= '1'; --100us Bekleme if InitializeCnt = 0 then Command := NOP; if DelayCnt < InitializeDelay - 1 then DelayCnt := DelayCnt + 1; else InitializeCnt := InitializeCnt + 1; end if; --PRE --Bütün banklar kapatılır elsif InitializeCnt = 1 then Command := PRE; --Bütün banklar ADDRESS(10) <= '1'; --tRP sayacı aktif edilir tRP_Complete := '0'; InitializeCnt := InitializeCnt + 1; --REF --2 kere yenileme işlemi yapılır elsif InitializeCnt = 2 then --tRP sayacının tamamlanması beklenir if tRP_Complete = '0' then Command := NOP; else Command := REF; tRC_Complete := '0'; InitializeCnt := InitializeCnt + 1; end if; --REF elsif InitializeCnt = 3 then if tRC_Complete = '0' then Command := NOP; else Command := REF; tRC_Complete := '0'; InitializeCnt := InitializeCnt + 1; end if; --MOD --Mode registeri ayarlanır elsif InitializeCnt = 4 then if tRC_Complete = '0' then Command := NOP; else Command := MRS; ADDRESS <= ModeRegister; BA <= "00"; InitializeCnt := InitializeCnt + 1; end if; --NOP elsif InitializeCnt = 5 then Command := NOP; NeedInitialize <= '0'; State := IDLE; PORT_BUSY <= '0'; end if; --Yenileme İşlemleri elsif State = REFRESH then --PRE --Açık olan bütün banklar kapatılır if StateCnt = 0 then if tRAS_Complete = '0' then Command := NOP; else Command := PRE; ADDRESS(10) <= '1'; StateCnt := StateCnt + 1; AllBankPrecharged <= '1'; tRP_Complete := '0'; end if; --REF --Yenileme işlemi yapılır elsif StateCnt = 1 then if tRP_Complete = '0' then Command := NOP; else Command := REF; tRC_Complete := '0'; StateCnt := StateCnt + 1; end if; --NOP --Yenileme işlemi bitinceye kadar herhangi bir işlem --yapılmaz elsif StateCnt = 2 then Command := NOP; if tRC_Complete = '1' then --Yenilenmeye zorlanmış ise yenileme işlemi --bir kez daha yapılır if ForceRefresh = '1' then StateCnt := 1; ForceRefresh <= '0'; else StateCnt := 0; NeedRefresh <= '0'; State := IDLE; PORT_BUSY <= '0'; end if; end if; end if; --Okuma İşlemleri elsif State = READ_DATA then if StateCnt = 0 then if AllBankPrecharged = '0' then --PRE --Okunacak row yada bank aktif degil ise aktif olan --bütün banklar kapatılır if ActiveRow /= PORT_ADDRESS(21 downto 9) or ActiveBank /= PORT_ADDRESS(23 downto 22) then if tRAS_Complete = '0' then Command := NOP; else Command := PRE; ADDRESS(10) <= '1'; AllBankPrecharged <= '1'; tRP_Complete := '0'; StateCnt := 1; end if; --RD --Okunacak row ve bank aktif ise okuma komutu gönderilir else --BurstInt kadar okuma komutu gönderilir. --Her komutta adres değeri 1 arttırılır if BurstCnt < BurstInt then Command := RD; ADDRESS(8 downto 0) <= std_logic_vector(unsigned(PORT_ADDRESS(8 downto 0)) + to_unsigned(BurstCnt, 9)); --Gönderilen ilk komut ise CL_Complete sinyali resetlenir ve --CAS sayacı aktif edilir if BurstCnt = 0 then CL_Complete := '0'; end if; --Son komutta if BurstCnt = BurstInt - 1 then BurstCnt := 0; StateCnt := 3; else BurstCnt := BurstCnt + 1; end if; end if; end if; --ACT --Okunacak row ve bank aktif edilir else Command := ACT; ADDRESS <= PORT_ADDRESS(21 downto 9); BA <= PORT_ADDRESS(23 downto 22); --tRCD ve tRAS sayaçları aktif edilir tRCD_Complete := '0'; tRAS_Complete := '0'; ActiveRow <= PORT_ADDRESS(21 downto 9); ActiveBank <= PORT_ADDRESS(23 downto 22); AllBankPrecharged <= '0'; StateCnt := 2; end if; --ACT --Okunacak row ve bank aktif edilir elsif StateCnt = 1 then if tRP_Complete = '0' then Command := NOP; else Command := ACT; ADDRESS <= PORT_ADDRESS(21 downto 9); BA <= PORT_ADDRESS(23 downto 22); tRCD_Complete := '0'; tRAS_Complete := '0'; ActiveRow <= PORT_ADDRESS(21 downto 9); ActiveBank <= PORT_ADDRESS(23 downto 22); AllBankPrecharged <= '0'; StateCnt := 2; end if; --RD --Okuma komutu gönderilir elsif StateCnt = 2 then if tRCD_Complete = '0' then Command := NOP; else if BurstCnt < 2**to_integer(unsigned(PORT_BURST)) then Command := RD; ADDRESS(8 downto 0) <= std_logic_vector(unsigned(PORT_ADDRESS(8 downto 0)) + to_unsigned(BurstCnt, 9)); if BurstCnt = 0 then CL_Complete := '0'; end if; if BurstCnt = 2**to_integer(unsigned(PORT_BURST)) - 1 then BurstCnt := 0; StateCnt := 3; else BurstCnt := BurstCnt + 1; end if; end if; end if; --CAS --Okuma işlemlerinin bitmesi beklenir elsif StateCnt = 3 then --DATA hattı yüksek empedans konumunda DataBuffer <= '1'; Command := NOP; StateCnt := 3; end if; --Yazma İşlemleri elsif State = WRITE_DATA then if StateCnt = 0 then if AllBankPrecharged = '0' then --PRE --Yazılacak row yada bank aktif degil ise aktif olan --bütün banklar kapatılır if ActiveRow /= PORT_ADDRESS(21 downto 9) or ActiveBank /= PORT_ADDRESS(23 downto 22) then if tRAS_Complete = '0' then Command := NOP; else Command := PRE; ADDRESS(10) <= '1'; AllBankPrecharged <= '1'; tRP_Complete := '0'; StateCnt := 1; end if; --WR --Yazılacak row ve bank aktif ise yazma komutu gönderilir else Command := WR; DataBuffer <= '0'; ADDRESS(8 downto 0) <= PORT_ADDRESS(8 downto 0); DataS <= PORT_DATAIN; StateCnt := 0; State := IDLE; PORT_BUSY <= '0'; end if; --ACT --Yazılacak row ve bank aktif edilir else Command := ACT; ADDRESS <= PORT_ADDRESS(21 downto 9); BA <= PORT_ADDRESS(23 downto 22); tRCD_Complete := '0'; tRAS_Complete := '0'; ActiveRow <= PORT_ADDRESS(21 downto 9); ActiveBank <= PORT_ADDRESS(23 downto 22); AllBankPrecharged <= '0'; StateCnt := 2; end if; --ACT --Yazılacak row ve bank aktif edilir elsif StateCnt = 1 then if tRP_Complete = '0' then Command := NOP; else Command := ACT; ADDRESS <= PORT_ADDRESS(21 downto 9); BA <= PORT_ADDRESS(23 downto 22); tRCD_Complete := '0'; tRAS_Complete := '0'; ActiveRow <= PORT_ADDRESS(21 downto 9); ActiveBank <= PORT_ADDRESS(23 downto 22); AllBankPrecharged <= '0'; StateCnt := 2; end if; --WR --Yazma komutu gönderilir elsif StateCnt = 2 then if tRCD_Complete = '0' then Command := NOP; else Command := WR; DataBuffer <= '0'; ADDRESS(8 downto 0) <= PORT_ADDRESS(8 downto 0); DataS <= PORT_DATAIN; StateCnt := 0; State := IDLE; PORT_BUSY <= '0'; end if; end if; end if; ------------------- Komutlar ------------------------ --No Operation if Command = NOP then CS <= '0'; RAS <= '1'; CAS <= '1'; WE <= '1'; --Burst Stop elsif Command = BST then CS <= '0'; RAS <= '1'; CAS <= '1'; WE <= '0'; --Read elsif Command = RD then CS <= '0'; RAS <= '1'; CAS <= '0'; WE <= '1'; --Write elsif Command = WR then CS <= '0'; RAS <= '1'; CAS <= '0'; WE <= '0'; --Bank Active elsif Command = ACT then CS <= '0'; RAS <= '0'; CAS <= '1'; WE <= '1'; --Precharge elsif Command = PRE then CS <= '0'; RAS <= '0'; CAS <= '1'; WE <= '0'; --Refresh elsif Command = REF then CS <= '0'; RAS <= '0'; CAS <= '0'; WE <= '1'; --Mode Register Set elsif Command = MRS then CS <= '0'; RAS <= '0'; CAS <= '0'; WE <= '0'; end if; ----------------------------------------------------- ----------- Yenileme Sayacı ----------------- if RefreshCnt < RefCycle - 1 then RefreshCnt := RefreshCnt + 1; else RefreshCnt := 0; if NeedRefresh = '0' then NeedRefresh <= '1'; --Önceki yenileme işlemi henüz gerçekleşmedi ise --yenilemeye zorla else ForceRefresh <= '1'; end if; end if; ----------------------------------------------- --------------- tRC Sayacı ------------------- if tRC_Complete = '0' then if tRCCnt < tRC - 1 then tRCCnt := tRCCnt + 1; else tRCCnt := 0; tRC_Complete := '1'; end if; end if; ----------------------------------------------- --------------- tRP Sayacı ------------------- if tRP_Complete = '0' then if tRPCnt < tRP - 1 then tRPCnt := tRPCnt + 1; else tRPCnt := 0; tRP_Complete := '1'; end if; end if; ----------------------------------------------- --------------- tRCD Sayacı ------------------ if tRCD_Complete = '0' then if tRCDCnt < tRCD - 1 then tRCDCnt := tRCDCnt + 1; else tRCDCnt := 0; tRCD_Complete := '1'; end if; end if; ----------------------------------------------- --------------- tRAS Sayacı ------------------ if tRAS_Complete = '0' then if tRASCnt < tRAS - 1 then tRASCnt := tRASCnt + 1; else tRASCnt := 0; tRAS_Complete := '1'; end if; end if; ----------------------------------------------- end if; end process; end Behavioral;
Quartus II ile oluşturulmuş RTL şeması:
Kaynak kullanımı:
EP4CE22F17C6 (Cyclone IV, -6 Speed Grade) için Fmax:
Çalışma Esnasında Signal Tap II ile Alınan Veriler
Kurulum:
- IS42S16160G SDRAM
- 100Mhz Clock Frekansı
- 2-2-2-6 (---)
- Burst uzunluğu = 4
Okuma
Farklı Row Okuma
Seri Yazma
Okumadan Yazmaya Geçiş
Normal Yenileme
Yenilemeye Zorlama