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









