Последний вариант AHDL кода этого же контроллера.
Главное отличие в том, что сигнал синхронизации для SD-RAM формируется снаружи относительно этой схемы,
и в организации раздельных каналов адреса для записи и для чтения (так было нужно для проекта)
Код:
-----------------------------------
-- Simple SDRAM controller
-- page mode burst read/write
-- latensy = 3, frq = 50..150MHz
-- 2002-2010 (c) Ivan Mak
-- E-mail: ivan_mak@mail.ru aka admin@winglion.ru
-- http://winglion.ru
-----------------------------------
TITLE "SDRAM_controller";
PARAMETERS
(
WIDTH = 16, -- SDRAM data WIDTH
OUT_SH = "YES", -- "YES" -- 1/2 clk data shift
PRIORITY = "WRITE" -- "READ" или "WRITE"
);
SUBDESIGN sdram
(
CLK : INPUT;
CLKs : input;
RESET : INPUT;
-- dir signals
DI[WIDTH-1..0] : INPUT = GND; -- input data
DO[WIDTH-1..0] : OUTPUT; -- output data
RA[23..0] : INPUT = GND; -- adress RAM
WA[23..0] : INPUT = GND; -- adress RAM
CS : INPUT = GND; -- select
WR : INPUT = VCC; -- write
RD : INPUT = VCC; -- read
STOP : INPUT = VCC; -- for sinhronize cycle
-- sdram signals
MD[WIDTH-1..0] : BIDIR; -- RAM data bus
MWE : OUTPUT; -- RAM signal WE
MRAS : OUTPUT; -- RAM signal RAS
MCAS : OUTPUT; -- RAM signal CAS
MCS : OUTPUT; -- RAM signal CS
MBS[1..0] : OUTPUT; -- RAM signals BS1,BS0
MCKE : OUTPUT; -- RAM signal CKE
MCLK : BIDIR; -- RAM signal CLK
MA[11..0] : OUTPUT; -- RAM adress bus
MDQM : OUTPUT;
-- return signals
MEM_C : OUTPUT; -- memory cycle
WRITE : OUTPUT; -- write data
READ : OUTPUT; -- data read
VALID : OUTPUT;
)
VARIABLE
ARGX[23..0] : DFFE;
ARG[23..0] : NODE;
CTT[5..0] : DFFE;
TAB[5..0] : DFF;
CTT_2 : NODE;
CTT_3 : NODE;
CTT_4 : NODE;
CT[3..0] : DFFE; -- counter
MX[11..0] : DFF; -- adress mux
MY[5..3] : DFF; -- dir mux
MDR[WIDTH-1..0] : DFF; -- data reg
MDRX[WIDTH-1..0]: DFF; -- data reg
MDRY[WIDTH-1..0]: DFF; -- data reg
PA : NODE;
RWC_FIX[2..0] : DFFE;
R/W : NODE;
WRITE_2 : NODE;
-- MCLKX : NODE;
-- MCLKY : NODE;
-- MCLKZ : NODE;
-- RX_MA[13..0]: DFF;
-- RX_MCKE : DFF;
-- RX_MBS[1..0]: DFF;
--
-- RX_MCS : DFF;
--
RX_MRAS : DFF;
RX_MCAS : DFF;
RX_MWE : DFF;
BEGIN
-- input address register
ARGX[].clk = CLK;
-- ARGX[].ena = VCC; -- fixed adress when bank active
ARGX[].ena = MEM_C; -- fixed adress when bank active
-- IF (PRIORITY != "READ") and (PRIORITY != "WRITE") THEN
-- ASSERT "PRIORITY parametr muast be READ or WRITE!";
-- END IF;
IF PRIORITY == "WRITE" GENERATE
CASE (RD,WR) IS
WHEN 3 => ARGX[] = GND;
WHEN 1 => ARGX[] = RA[];
WHEN 2,0 => ARGX[] = WA[];
END CASE;
R/W = DFFE((!(RWC_FIX[] == B"X00") or !(MY[5..3] == B"011")),CLK,,,CTT_2);
END GENERATE;
IF PRIORITY == "READ" GENERATE
CASE (RD,WR) IS
WHEN 3 => ARGX[] = GND;
WHEN 1,0 => ARGX[] = RA[];
WHEN 2 => ARGX[] = WA[];
END CASE;
R/W = DFFE(!(!(RWC_FIX[] == B"0X0") or !(MY[5..3] == B"011")),CLK,,,CTT_2);
END GENERATE;
ARG[] = ARGX[];
-- ARGX[].d = A[];
RWC_FIX[].clk = CLK;
RWC_FIX[].ena = MEM_C;
RWC_FIX[] = (RD,WR,CS);
-- micro count
CTT[].clk = CLK;
CTT[].clrn = RESET;
CTT[].ena = DFF((!(CT[] == 8) or STOP),CLK,,);
CTT_2 = DFF(CTT[] == 2,CLK,,);
CTT_3 = DFF(CTT[] == 3,CLK,,);
CTT_4 = DFF(CTT[] == 4,CLK,,);
IF CTT_2 THEN
CTT[] = TAB[]; -- one command period
ELSE
CTT[] = CTT[] - 1;
END IF;
-- SDRAM data
-- mode sdram bit2..0 - 000 - 1byte
-- 001 - 2byte
-- 010 - 4byte
-- 011 - 8byte
-- 111 - page
-- other - reserved
-- bit3 - 0 - seqential
-- 1 - interleave
-- bit6..4 - 010 - cas latency = 2
-- 011 - cas latency = 3
-- other - reserved
-- bit7 - 0 (?test?)
-- bit8 - 0 reserved
-- bit9 - 0 burst read & burst write
-- 1 burst read & single write
-- bit11..10 - 00 reserved
-- BS[1..0] - 00 reserved
-- sdram commands RAS,CAS,WE
-- 000 - mode register write
-- 001 - auto refresh -- cken=0 -> self refresh
-- -- CS=H -> SR exit
-- RCW=110 -> SR exit
-- 010 - bank precharge -- A10=0 precharge bank
-- -- A10=1 precharge all
-- 011 - bank active
-- 100 - write -- A10=1 - auto precharge
-- 101 - read -- A10=1 - auto precharge
-- 110 - burst stop
-- 111 - no operation
-- R/W = DFFE((!(RWC_FIX[] == B"X00") or !(MY[5..3] == B"011")),CLK,,,CTT_2);
-- comand counter
CT[].clk = CLK;
CT[].clrn = RESET;
CT[].ena = CTT_2;
IF DFF((CT[3..0] == 12),CLK,,) THEN
CT[] = 8;
ELSE
CT[] = CT[]+1;
END IF;
-- SDRAM command & times table
TAB[].clk = CLK;
MY[].clk = CLK;
CASE CT[3..0] IS -- MY[] -- RAS,CAS,WE
-- init commands
WHEN 0 => TAB[] = 4; MY[] = B"010"; -- precharge ALL
WHEN 1 => TAB[] = 4; MY[] = B"000"; -- set mode register
WHEN 2 => TAB[] = 8; MY[] = B"001"; -- autorefresh
WHEN 3 => TAB[] = 8; MY[] = B"001"; -- autorefresh
WHEN 4 => TAB[] = 8; MY[] = B"001"; -- autorefresh
WHEN 5 => TAB[] = 8; MY[] = B"001"; -- autorefresh
WHEN 6 => TAB[] = 8; MY[] = B"001"; -- autorefresh
WHEN 7 => TAB[] = 8; MY[] = B"001"; -- autorefresh
-- cycle commands (realy 5 items)
WHEN 8 => TAB[] = 4; MY[] = B"011"; -- bank active
-- ! words count (0->64)
WHEN 9 => TAB[] = 0; MY[] = (B"10",R/W); -- bank read/write
WHEN 10 => TAB[] = 4; MY[] = B"110"; -- burst stop!
WHEN 11 => TAB[] = 4; MY[] = B"010"; -- bank precharge
WHEN 12 => TAB[] = 8; MY[] = B"001"; -- autorefresh
WHEN 13 => TAB[] = 4; MY[] = B"111"; -- NOP no executed
WHEN 14 => TAB[] = 4; MY[] = B"111"; -- NOP no executed
WHEN 15 => TAB[] = 4; MY[] = B"111"; -- NOP no executed
END CASE;
MCLK = TRI(LCELL(!CLKs),VCC);
-- address MUX
PA = DFF(!(CT[2..0] == 0),CLK,,);
MX[].clk = CLK;
CASE (CT3,PA) IS
WHEN 0 => MX[] = (B"010000000000"); -- precharge all
WHEN 1 => MX[] = (B"000000110111"); -- SDRAM mode
WHEN 2 => MX[] = (ARG[21..10]); -- adress RAS -- 4k
WHEN 3 => MX[] = (B"00",ARG[9..0]); -- adress CAS -- 1k
END CASE;
-- input register
MDR[].clk = CLK;
MDR[].d = DI[];
-- WRITE_2 = (DFF(DFF(DFF(WRITE,CLK,,),CLK,,),CLK,,));
WRITE_2 = DFF(DFF(WRITE or DFF(WRITE,CLK,,),CLK,,),CLK,,);
-- WRITE_2 = DFF(WRITE,CLK,,);
FOR i IN 0 TO WIDTH-1 GENERATE
MD[i] = TRI(MDR[i],WRITE_2);
END GENERATE;
-- output register' -- for 1/2 shift
-- MDRX[].clk = !CLK;
-- MDRX[].clk = !LCELL(CLK);
MDRX[].clk = MCLK;
MDRX[].d = MD[];
-- output register
MDRY[].clk = CLK;
IF (OUT_SH == "YES") GENERATE
MDRY[].d = MDRX[]; -- 1/2 clk data shift
ELSE GENERATE
MDRY[].d = MD[]; -- no 1/2 clk data shift
END GENERATE;
DO[] = MDRY[];
-- IF (M_REG == "YES") GENERATE
-- MA[] = RX_MA[13..0];
---- MCKE = RX_MCKE;
-- MCKE = VCC;
-- MBS[1..0] = RX_MBS[1..0];
-- MCS = RX_MCS;
--
---- MRAS = RX_MRAS;
---- MCAS = RX_MCAS;
---- MWE = RX_MWE;
--
-- MRAS = DFF(DFF(RX_MRAS,CLK,,),CLK,,);
-- MCAS = DFF(DFF(RX_MCAS,CLK,,),CLK,,);
-- MWE = DFF(DFF(RX_MWE,CLK,,),CLK,,);
--
-- MDQM = GND;
-- ELSE GENERATE
MA[] = MX[];
-- (MDQM,MCKE,MRAS,MCAS,MWE,MBS1,MBS0) = (GND,VCC,MY[5..3],ARG[23..22]);
(MDQM,MCKE,MRAS,MCAS,MWE,MBS1,MBS0) = (GND,VCC,RX_MRAS,RX_MCAS,RX_MWE,ARG[23..22]);
-- RX_MRAS,RX_MCAS,RX_MWE
MCS = DFF((!(CTT[] == 1)),CLK,,);
-- END GENERATE;
-- (MDQM,MCKE,MRAS,MCAS,MWE,MBS1,MBS0) = (GND,VCC,MY[5..3],ARG[23..22]);
-- RX_MA[13..0]= MX[];
-- RX_MCKE = VCC;
-- RX_MBS[1..0]= ARG[23..22];
--
-- RX_MA[13..0].clk = CLK;
-- RX_MCKE.clk = CLK;
-- RX_MBS[1..0].clk = CLK;
--
-- RX_MCS = DFF((!(CTT[] == 1)),CLK,,); RX_MCS.clk = CLK;
--
RX_MRAS = MY5; RX_MRAS.clk = CLK;
RX_MCAS = MY4; RX_MCAS.clk = CLK;
RX_MWE = MY3; RX_MWE.clk = CLK;
-- return signals
-- write cycle
-- WRITE = DFFE((MY[5..3] == B"100"),CLK,,,CTT_3);
WRITE = DFFE((MY[5..3] == B"100"),CLK,,,CTT_4);
-- mem cycle
MEM_C = DFFE((MY[5..3] == B"001"),CLK,,,VCC);
-- MEM_C = PA;
-- read sysle
READ = DFFE(((RWC_FIX[] == B"010") & (MY[5..3] == B"101")),CLK,,,CTT_3);
-- data valid
VALID = DFF(DFF(DFF(DFF(DFF(READ,CLK,,),CLK,,),CLK,,),CLK,,),CLK,,);
END;
Проверялся в работе на реальном проекте на Cyclone-II с рабочей частотой до 150MHz