📄 sdramcntl.vhd
字号:
library IEEE,unisim;
use IEEE.std_logic_1164.all;
use IEEE.numeric_std.all;
use unisim.vcomponents.all;
entity sdramCntl is
generic(
FREQ: natural := 25_000; -- operating frequency in KHz
DATA_WIDTH: natural := 16; -- host & SDRAM data width
NROWS: natural := 4096; -- number of rows in SDRAM array
NCOLS: natural := 512; -- number of columns in SDRAM array
HADDR_WIDTH: natural := 23; -- host-side address width
SADDR_WIDTH: natural := 12 -- SDRAM-side address width
);
port(
clkin: in std_logic; -- master clock
-- host side
bufclk: out std_logic; -- buffered master clock
clk0: out std_logic; -- host clock sync'ed to master clock
clk2x: out std_logic; -- double-speed host clock
lock: out std_logic; -- indicate when clock circuitry is locked to master clock
rst: in std_logic; -- reset
rd: in std_logic; -- read data
wr: in std_logic; -- write data
done: out std_logic; -- read/write op done
hAddr: in unsigned(HADDR_WIDTH-1 downto 0); -- address from host
hDIn: in unsigned(DATA_WIDTH-1 downto 0); -- data from host
hDOut: out unsigned(DATA_WIDTH-1 downto 0); -- data to host
sdramCntl_state: out std_logic_vector(3 downto 0);
-- SDRAM side
sclkfb: in std_logic; -- clock from SDRAM after PCB delays
sclk: out std_logic; -- SDRAM clock sync'ed to master clock
cke: out std_logic; -- clock-enable to SDRAM
cs_n: out std_logic; -- chip-select to SDRAM
ras_n: out std_logic; -- command input to SDRAM
cas_n: out std_logic; -- command input to SDRAM
we_n: out std_logic; -- command input to SDRAM
ba: out unsigned(1 downto 0); -- SDRAM bank address bits
sAddr: out unsigned(SADDR_WIDTH-1 downto 0); -- SDRAM row/column address
sData: inout unsigned(DATA_WIDTH-1 downto 0); -- SDRAM in/out databus
dqmh: out std_logic; -- high databits I/O mask
dqml: out std_logic -- low databits I/O mask
);
end sdramCntl;
architecture arch of sdramCntl is
function log2(v: in natural) return natural is
variable n: natural;
variable logn: natural;
begin
n := 1;
for i in 0 to 128 loop
logn := i;
exit when (n>=v);
n := n * 2;
end loop;
return logn;
end function log2;
constant YES: std_logic := '1';
constant NO: std_logic := '0';
constant HI: std_logic := '1';
constant LO: std_logic := '0';
-- constants
constant ColCmdPos: natural := 10; -- position of command bit in SDRAM column address
constant Tinit: natural := 200; -- min initialization interval (us)
constant Tras: natural := 45; -- min interval between active to precharge commands (ns)
constant Trc: natural := 67; -- min interval between active to active commands (ns)
constant Trcd: natural := 20; -- min interval between active and R/W commands (ns)
constant Tref: natural := 64_000_000; -- maximum refresh interval (ns)
constant Trfc: natural := 66; -- duration of refresh operation (ns)
constant Trp: natural := 20; -- min precharge command duration (ns)
constant Twr: natural := 15; -- write recovery time (ns)
constant Ccas: natural := 3; -- CAS latency (cycles)
constant Cmrd: natural := 2; -- mode register setup time (cycles)
constant RfshCycles: natural := 8; -- number of refresh cycles needed to init RAM
constant ROW_LEN: natural := log2(NROWS); -- number of row address bits
constant COL_LEN: natural := log2(NCOLS); -- number of column address bits
constant NORM: natural := 1_000_000; -- normalize ns * KHz
constant INIT_CYCLES: natural := 1 + ((Tinit * FREQ) / 1000); -- SDRMA power-on initialization interval
constant RAS_CYCLES: natural := 1 + ((Tras * FREQ) / NORM); -- active-to-precharge interval
constant RC_CYCLES: natural := 1 + ((Trc * FREQ) / NORM); -- active-to-active interval
constant RCD_CYCLES: natural := 1 + ((Trcd * FREQ) / NORM); -- active-to-R/W interval
constant REF_CYCLES: natural := 1 + (((Tref/NROWS) * FREQ) / NORM); -- interval between row refreshes
constant RFC_CYCLES: natural := 1 + ((Trfc * FREQ) / NORM); -- refresh operation interval
constant RP_CYCLES: natural := 1 + ((Trp * FREQ) / NORM); -- precharge operation interval
constant WR_CYCLES: natural := 1 + ((Twr * FREQ) / NORM); -- write recovery time
-- states of the SDRAM controller state machine
type cntlState is (
INITWAIT, -- initialization - waiting for power-on initialization to complete
INITPCHG, -- initialization - doing precharge of banks
INITSETMODE, -- initialization - set SDRAM mode
INITRFSH, -- initialization - do refreshes
REFRESH, -- refresh a row of the SDRAM
RW, -- wait for read/write operations to SDRAM
RDDONE, -- indicate that the SDRAM read is done
WRDONE, -- indicate that the SDRAM write is done
ACTIVATE -- open a row of the SDRAM for reading/writing
);
signal state_r, state_next: cntlState; -- state register and next state
constant AUTO_PCHG_ON: std_logic := '1'; -- set sAddr(10) to this value to auto-precharge the bank
constant AUTO_PCHG_OFF: std_logic := '0'; -- set sAddr(10) to this value to disable auto-precharge
constant ALL_BANKS: std_logic := '1'; -- set sAddr(10) to this value to select all banks
constant ACTIVE_BANK: std_logic := '0'; -- set sAddr(10) to this value to select only the active bank
signal bank: unsigned(ba'range);
signal row: unsigned(ROW_LEN - 1 downto 0);
signal col: unsigned(COL_LEN - 1 downto 0);
signal col_tmp: unsigned(sAddr'high-1 downto sAddr'low);
signal changeRow: std_logic;
signal dirOut: std_logic; -- high when driving data to SDRAM
-- registers
signal activeBank_r, activeBank_next: unsigned(bank'range); -- currently active SDRAM bank
signal activeRow_r, activeRow_next: unsigned(row'range); -- currently active SDRAM row
signal inactiveFlag_r, inactiveFlag_next: std_logic; -- 1 when all SDRAM rows are inactive
signal doRfshFlag_r, doRfshFlag_next: std_logic; -- 1 when a row refresh operation is required
signal wrFlag_r, wrFlag_next: std_logic; -- 1 when writing data to SDRAM
signal rdFlag_r, rdFlag_next: std_logic; -- 1 when reading data from SDRAM
signal rfshCntr_r, rfshCntr_next: unsigned(log2(RfshCycles+1)-1 downto 0); -- counts initialization refreshes
-- timer registers that count down times for various SDRAM operations
signal timer_r, timer_next: unsigned(log2(INIT_CYCLES+1)-1 downto 0); -- current SDRAM op time
signal rasTimer_r, rasTimer_next: unsigned(log2(RAS_CYCLES+1)-1 downto 0); -- active-to-precharge time
signal wrTimer_r, wrTimer_next: unsigned(log2(WR_CYCLES+1)-1 downto 0); -- write-to-precharge time
signal refTimer_r, refTimer_next: unsigned(log2(REF_CYCLES+1)-1 downto 0); -- time between row refreshes
-- SDRAM commands
subtype sdramCmd is unsigned(5 downto 0);
-- cmd = (cs_n,ras_n,cas_n,we_n,dqmh,dqml)
constant NOP_CMD: sdramCmd := "011100";
constant ACTIVE_CMD: sdramCmd := "001100";
constant READ_CMD: sdramCmd := "010100";
constant WRITE_CMD: sdramCmd := "010000";
constant PCHG_CMD: sdramCmd := "001011";
constant MODE_CMD: sdramCmd := "000011";
constant RFSH_CMD: sdramCmd := "000111";
signal cmd: sdramCmd;
-- SDRAM mode register
subtype sdramMode is unsigned(11 downto 0);
constant MODE: sdramMode := "00" & "0" & "00" & "011" & "0" & "000";
-- clock DLL signals
signal logic0: std_logic;
-- signals for internal logic clock DLL
signal bufclkin, dllint_clk0, dllint_clk2x, bufdllint_clk0, bufdllint_clk2x, lockint: std_logic;
-- signals for external logic clock DLL
signal bufsclkfb, dllext_clk0, lockext: std_logic;
signal clk: std_logic; -- clock for SDRAM controller logic
begin
logic0 <= '0';
-- master clock must come from a dedicated clock pin
clkpad: IBUFG port map (I=>clkin, O=>bufclkin);
bufclk <= bufclkin;
-- generate an internal clock sync'ed to the master clock
dllint: CLKDLL port map(
CLKIN=>bufclkin, CLKFB=>bufdllint_clk0, CLK0=>dllint_clk0,
RST=>logic0, CLK90=>open, CLK180=>open, CLK270=>open,
CLK2X=>dllint_clk2x, CLKDV=>open, LOCKED=>lockint
);
-- sync'ed single and double-speed clocks for use by internal logic
clkg: BUFG port map (I=>dllint_clk0, O=>bufdllint_clk0);
clkg2x: BUFG port map(I=>dllint_clk2x, O=>bufdllint_clk2x);
clk <= bufdllint_clk0; -- SDRAM controller logic clock
clk0 <= bufdllint_clk0; -- clock to other FPGA logic
clk2x <= bufdllint_clk2x; -- doubled clock to other FPGA logic;
lock <= lockint and lockext; -- indicate lock status of the DLLs
-- generate an external SDRAM clock sync'ed to the master clock
clkfbpad : IBUFG port map (I=>sclkfb, O=>bufsclkfb); -- SDRAM clock with PCB delays
dllext: CLKDLL port map(
CLKIN=>bufclkin, CLKFB=>bufsclkfb, CLK0=>dllext_clk0,
RST=>logic0, CLK90=>open, CLK180=>open, CLK270=>open,
CLK2X=>open, CLKDV=>open, LOCKED=>lockext
);
-- output the sync'ed SDRAM clock to the SDRAM
clkextpad: OBUF port map (I=>dllext_clk0, O=>sclk);
hDOut <= sData(hDOut'range); -- connect SDRAM data bus to host data bus
sData <= hDIn(sData'range) when dirOut='1' else (others=>'Z'); -- connect host data bus to SDRAM data bus
combinatorial: process(rd,wr,hAddr,hDIn,state_r,bank,row,col,changeRow,
activeBank_r,activeRow_r,doRfshFlag_r,rdFlag_r,wrFlag_r,
rfshCntr_r,timer_r,rasTimer_r,wrTimer_r,refTimer_r,cmd,col_tmp,inactiveFlag_r)
begin
-- attach bits in command to SDRAM control signals
(cs_n,ras_n,cas_n,we_n,dqmh,dqml) <= cmd;
-- get bank, row, column from host address
bank <= hAddr(bank'length + ROW_LEN + COL_LEN - 1 downto ROW_LEN + COL_LEN);
row <= hAddr(ROW_LEN + COL_LEN - 1 downto COL_LEN);
col <= hAddr(COL_LEN - 1 downto 0);
-- extend column (if needed) until it is as large as the (SDRAM address bus - 1)
col_tmp <= (others=>'0'); -- set it to all zeroes
col_tmp(col'range) <= col; -- write column into the lower bits
-- default operations
cke <= YES; -- enable SDRAM clock input
cmd <= NOP_CMD; -- set SDRAM command to no-operation
done <= NO; -- pending SDRAM operation is not done
ba <= bank; -- set SDRAM bank address bits
-- set SDRAM address to column with interspersed command bit
sAddr(ColCmdPos-1 downto 0) <= col_tmp(ColCmdPos-1 downto 0);
sAddr(sAddr'high downto ColCmdPos+1) <= col_tmp(col_tmp'high downto ColCmdPos);
sAddr(ColCmdPos) <= AUTO_PCHG_OFF; -- set command bit to disable auto-precharge
dirOut <= NO;
-- default register updates
state_next <= state_r;
inactiveFlag_next <= inactiveFlag_r;
activeBank_next <= activeBank_r;
activeRow_next <= activeRow_r;
doRfshFlag_next <= doRfshFlag_r;
rdFlag_next <= rdFlag_r;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -