mirror of
git://projects.qi-hardware.com/nn-usb-fpga.git
synced 2024-12-13 02:55:21 +02:00
355 lines
13 KiB
VHDL
355 lines
13 KiB
VHDL
---------------------------------------------------------------------
|
|
-- TITLE: DDR SDRAM Interface
|
|
-- AUTHORS: Steve Rhoads (rhoadss@yahoo.com)
|
|
-- DATE CREATED: 7/26/07
|
|
-- FILENAME: ddr_ctrl.vhd
|
|
-- PROJECT: Plasma CPU core
|
|
-- COPYRIGHT: Software placed into the public domain by the author.
|
|
-- Software 'as is' without warranty. Author liable for nothing.
|
|
-- DESCRIPTION:
|
|
-- Double Data Rate Sychronous Dynamic Random Access Memory Interface
|
|
--
|
|
-- For: 64 MB = MT46V32M16, 512Mb, 32Mb x 16 (default)
|
|
-- ROW = address(25 downto 13)
|
|
-- BANK = address(12 downto 11)
|
|
-- COL = address(10 downto 2)
|
|
--
|
|
-- Changes are needed for 32 MB = MT46V16M16, 256Mb, 16Mb x 16
|
|
-- ROW = address(24 downto 12) -- 25 ignored
|
|
-- BANK = address(11 downto 10)
|
|
-- COL = address(9 downto 2) --also change ddr_init.c
|
|
--
|
|
-- Changes are needed for 128 MB = MT46V64M16, 1Gb, 64Mb x 16
|
|
-- ROW = address(26 downto 14)
|
|
-- BANK = address(13 downto 12)
|
|
-- COL = address(11 downto 2) --also change ddr_init.c
|
|
--
|
|
-- Requires CAS latency=2; burst size=2.
|
|
-- Requires clk changes on rising_edge(clk_2x).
|
|
-- Requires active, address, byte_we, data_w stable throughout transfer.
|
|
-- DLL mode requires 77MHz. Non-DLL mode runs at 25 MHz.
|
|
--
|
|
-- cycle_cnt 777777770000111122223333444455556666777777777777
|
|
-- clk_2x --__--__--__--__--__--__--__--__--__--__--__--__
|
|
-- clk ____----____----____----____----____----____----
|
|
-- SD_CLK ----____----____----____----____----____----____
|
|
-- cmd ____write+++WRITE+++____________________________
|
|
-- SD_DQ ~~~~~~~~~~~~~~uuuullllUUUULLLL~~~~~~~~~~~~~~~~~~
|
|
--
|
|
-- cycle_cnt 777777770000111122223333444455556666777777777777
|
|
-- clk_2x --__--__--__--__--__--__--__--__--__--__--__--__
|
|
-- clk ____----____----____----____----____----____----
|
|
-- SD_CLK ----____----____----____----____----____----____
|
|
-- cmd ____read++++________________________read++++____
|
|
-- SD_DQ ~~~~~~~~~~~~~~~~~~~~~~~~uuuullll~~~~~~~~~~~~~~~~
|
|
-- SD_DQnDLL ~~~~~~~~~~~~~~~~~~~~~~~~~~uuuullll~~~~~~~~~~~~~~
|
|
-- pause ____------------------------________------------
|
|
--
|
|
-- Must run DdrInit() to initialize DDR chip.
|
|
-- Read Micron DDR SDRAM MT46V32M16 data sheet for more details.
|
|
---------------------------------------------------------------------
|
|
library ieee;
|
|
use ieee.std_logic_1164.all;
|
|
use ieee.std_logic_unsigned.all;
|
|
use ieee.std_logic_arith.all;
|
|
use work.mlite_pack.all;
|
|
|
|
entity ddr_ctrl is
|
|
port(
|
|
clk : in std_logic;
|
|
clk_2x : in std_logic;
|
|
reset_in : in std_logic;
|
|
|
|
address : in std_logic_vector(25 downto 2);
|
|
byte_we : in std_logic_vector(3 downto 0);
|
|
data_w : in std_logic_vector(31 downto 0);
|
|
data_r : out std_logic_vector(31 downto 0);
|
|
active : in std_logic;
|
|
no_start : in std_logic;
|
|
no_stop : in std_logic;
|
|
pause : out std_logic;
|
|
|
|
SD_CK_P : out std_logic; --clock_positive
|
|
SD_CK_N : out std_logic; --clock_negative
|
|
SD_CKE : out std_logic; --clock_enable
|
|
|
|
SD_BA : out std_logic_vector(1 downto 0); --bank_address
|
|
SD_A : out std_logic_vector(12 downto 0); --address(row or col)
|
|
SD_CS : out std_logic; --chip_select
|
|
SD_RAS : out std_logic; --row_address_strobe
|
|
SD_CAS : out std_logic; --column_address_strobe
|
|
SD_WE : out std_logic; --write_enable
|
|
|
|
SD_DQ : inout std_logic_vector(15 downto 0); --data
|
|
SD_UDM : out std_logic; --upper_byte_enable
|
|
SD_UDQS : inout std_logic; --upper_data_strobe
|
|
SD_LDM : out std_logic; --low_byte_enable
|
|
SD_LDQS : inout std_logic); --low_data_strobe
|
|
end; --entity ddr
|
|
|
|
architecture logic of ddr_ctrl is
|
|
|
|
--Commands for bits RAS & CAS & WE
|
|
subtype command_type is std_logic_vector(2 downto 0);
|
|
constant COMMAND_LMR : command_type := "000";
|
|
constant COMMAND_AUTO_REFRESH : command_type := "001";
|
|
constant COMMAND_PRECHARGE : command_type := "010";
|
|
constant COMMAND_ACTIVE : command_type := "011";
|
|
constant COMMAND_WRITE : command_type := "100";
|
|
constant COMMAND_READ : command_type := "101";
|
|
constant COMMAND_TERMINATE : command_type := "110";
|
|
constant COMMAND_NOP : command_type := "111";
|
|
|
|
subtype ddr_state_type is std_logic_vector(3 downto 0);
|
|
constant STATE_POWER_ON : ddr_state_type := "0000";
|
|
constant STATE_IDLE : ddr_state_type := "0001";
|
|
constant STATE_ROW_ACTIVATE : ddr_state_type := "0010";
|
|
constant STATE_ROW_ACTIVE : ddr_state_type := "0011";
|
|
constant STATE_READ : ddr_state_type := "0100";
|
|
constant STATE_READ2 : ddr_state_type := "0101";
|
|
constant STATE_READ3 : ddr_state_type := "0110";
|
|
constant STATE_PRECHARGE : ddr_state_type := "0111";
|
|
constant STATE_PRECHARGE2 : ddr_state_type := "1000";
|
|
|
|
signal state_prev : ddr_state_type;
|
|
signal refresh_cnt : std_logic_vector(7 downto 0);
|
|
signal data_write2 : std_logic_vector(47 downto 0); --write pipeline
|
|
signal byte_we_reg2 : std_logic_vector(5 downto 0); --write pipeline
|
|
signal write_active : std_logic;
|
|
signal write_prev : std_logic;
|
|
signal cycle_count : std_logic_vector(2 downto 0); --half clocks since op
|
|
signal cycle_count2 : std_logic_vector(2 downto 0); --delayed by quarter clock
|
|
signal cke_reg : std_logic;
|
|
signal clk_p : std_logic;
|
|
signal bank_open : std_logic_vector(3 downto 0);
|
|
signal data_read : std_logic_vector(31 downto 0);
|
|
|
|
begin
|
|
ddr_proc: process(clk, clk_p, clk_2x, reset_in,
|
|
address, byte_we, data_w, active, no_start, no_stop,
|
|
SD_DQ, SD_UDQS, SD_LDQS,
|
|
state_prev, refresh_cnt,
|
|
byte_we_reg2, data_write2,
|
|
cycle_count, cycle_count2, write_prev,
|
|
write_active, cke_reg, bank_open,
|
|
data_read)
|
|
type address_array_type is array(3 downto 0) of std_logic_vector(12 downto 0);
|
|
variable address_row : address_array_type;
|
|
variable command : std_logic_vector(2 downto 0); --RAS & CAS & WE
|
|
variable bank_index : integer;
|
|
variable state_current : ddr_state_type;
|
|
|
|
begin
|
|
|
|
command := COMMAND_NOP;
|
|
bank_index := conv_integer(address(12 downto 11));
|
|
state_current := state_prev;
|
|
|
|
--DDR state machine to determine state_current and command
|
|
case state_prev is
|
|
when STATE_POWER_ON =>
|
|
if active = '1' then
|
|
if byte_we /= "0000" then
|
|
command := address(6 downto 4); --LMR="000"
|
|
else
|
|
state_current := STATE_IDLE; --read transistions to STATE_IDLE
|
|
end if;
|
|
end if;
|
|
|
|
when STATE_IDLE =>
|
|
if refresh_cnt(7) = '1' then
|
|
state_current := STATE_PRECHARGE;
|
|
command := COMMAND_AUTO_REFRESH;
|
|
elsif active = '1' and no_start = '0' then
|
|
state_current := STATE_ROW_ACTIVATE;
|
|
command := COMMAND_ACTIVE;
|
|
end if;
|
|
|
|
when STATE_ROW_ACTIVATE =>
|
|
state_current := STATE_ROW_ACTIVE;
|
|
|
|
when STATE_ROW_ACTIVE =>
|
|
if refresh_cnt(7) = '1' then
|
|
if write_prev = '0' then
|
|
state_current := STATE_PRECHARGE;
|
|
command := COMMAND_PRECHARGE;
|
|
end if;
|
|
elsif active = '1' and no_start = '0' then
|
|
if bank_open(bank_index) = '0' then
|
|
state_current := STATE_ROW_ACTIVATE;
|
|
command := COMMAND_ACTIVE;
|
|
elsif address(25 downto 13) /= address_row(bank_index) then
|
|
if write_prev = '0' then
|
|
state_current := STATE_PRECHARGE;
|
|
command := COMMAND_PRECHARGE;
|
|
end if;
|
|
else
|
|
if byte_we /= "0000" then
|
|
command := COMMAND_WRITE;
|
|
elsif write_prev = '0' then
|
|
state_current := STATE_READ;
|
|
command := COMMAND_READ;
|
|
end if;
|
|
end if;
|
|
end if;
|
|
|
|
when STATE_READ =>
|
|
state_current := STATE_READ2;
|
|
|
|
when STATE_READ2 =>
|
|
state_current := STATE_READ3;
|
|
|
|
when STATE_READ3 =>
|
|
if no_stop = '0' then
|
|
state_current := STATE_ROW_ACTIVE;
|
|
end if;
|
|
|
|
when STATE_PRECHARGE =>
|
|
state_current := STATE_PRECHARGE2;
|
|
|
|
when STATE_PRECHARGE2 =>
|
|
state_current := STATE_IDLE;
|
|
|
|
when others =>
|
|
state_current := STATE_IDLE;
|
|
end case; --state_prev
|
|
|
|
--rising_edge(clk) domain registers
|
|
if reset_in = '1' then
|
|
state_prev <= STATE_POWER_ON;
|
|
cke_reg <= '0';
|
|
refresh_cnt <= ZERO(7 downto 0);
|
|
write_prev <= '0';
|
|
write_active <= '0';
|
|
bank_open <= "0000";
|
|
elsif rising_edge(clk) then
|
|
|
|
if active = '1' then
|
|
cke_reg <= '1';
|
|
end if;
|
|
|
|
if command = COMMAND_WRITE then
|
|
write_prev <= '1';
|
|
elsif cycle_count2(2 downto 1) = "11" then
|
|
write_prev <= '0';
|
|
end if;
|
|
|
|
if command = COMMAND_WRITE then
|
|
write_active <= '1';
|
|
elsif cycle_count2 = "100" then
|
|
write_active <= '0';
|
|
end if;
|
|
|
|
if command = COMMAND_ACTIVE then
|
|
bank_open(bank_index) <= '1';
|
|
address_row(bank_index) := address(25 downto 13);
|
|
end if;
|
|
|
|
if command = COMMAND_PRECHARGE then
|
|
bank_open <= "0000";
|
|
end if;
|
|
|
|
if command = COMMAND_AUTO_REFRESH then
|
|
refresh_cnt <= ZERO(7 downto 0);
|
|
else
|
|
refresh_cnt <= refresh_cnt + 1;
|
|
end if;
|
|
|
|
state_prev <= state_current;
|
|
|
|
end if; --rising_edge(clk)
|
|
|
|
--rising_edge(clk_2x) domain registers
|
|
if reset_in = '1' then
|
|
cycle_count <= "000";
|
|
elsif rising_edge(clk_2x) then
|
|
--Cycle_count
|
|
if (command = COMMAND_READ or command = COMMAND_WRITE) and clk = '1' then
|
|
cycle_count <= "000";
|
|
elsif cycle_count /= "111" then
|
|
cycle_count <= cycle_count + 1;
|
|
end if;
|
|
|
|
clk_p <= clk; --earlier version of not clk
|
|
|
|
--Read data (DLL disabled)
|
|
if cycle_count = "100" then
|
|
data_read(31 downto 16) <= SD_DQ; --data
|
|
elsif cycle_count = "101" then
|
|
data_read(15 downto 0) <= SD_DQ;
|
|
end if;
|
|
end if;
|
|
|
|
--falling_edge(clk_2x) domain registers
|
|
if reset_in = '1' then
|
|
cycle_count2 <= "000";
|
|
data_write2 <= ZERO(15 downto 0) & ZERO;
|
|
byte_we_reg2 <= "000000";
|
|
elsif falling_edge(clk_2x) then
|
|
cycle_count2 <= cycle_count;
|
|
|
|
--Write pipeline
|
|
if clk = '0' then
|
|
data_write2 <= data_write2(31 downto 16) & data_w;
|
|
byte_we_reg2 <= byte_we_reg2(3 downto 2) & byte_we;
|
|
else
|
|
data_write2(47 downto 16) <= data_write2(31 downto 0);
|
|
byte_we_reg2(5 downto 2) <= byte_we_reg2(3 downto 0);
|
|
end if;
|
|
|
|
--Read data (DLL enabled)
|
|
--if cycle_count = "100" then
|
|
-- data_read(31 downto 16) <= SD_DQ; --data
|
|
--elsif cycle_count = "101" then
|
|
-- data_read(15 downto 0) <= SD_DQ;
|
|
--end if;
|
|
end if;
|
|
|
|
data_r <= data_read;
|
|
|
|
--Write data
|
|
if write_active = '1' then
|
|
SD_UDQS <= clk_p; --upper_data_strobe
|
|
SD_LDQS <= clk_p; --low_data_strobe
|
|
SD_DQ <= data_write2(47 downto 32); --data
|
|
SD_UDM <= not byte_we_reg2(5); --upper_byte_enable
|
|
SD_LDM <= not byte_we_reg2(4); --low_byte_enable
|
|
else
|
|
SD_UDQS <= 'Z'; --upper_data_strobe
|
|
SD_LDQS <= 'Z'; --low_data_strobe
|
|
SD_DQ <= "ZZZZZZZZZZZZZZZZ"; --data
|
|
SD_UDM <= 'Z';
|
|
SD_LDM <= 'Z';
|
|
end if;
|
|
|
|
--DDR control signals
|
|
SD_CK_P <= clk_p; --clock_positive
|
|
SD_CK_N <= not clk_p; --clock_negative
|
|
SD_CKE <= cke_reg; --clock_enable
|
|
|
|
SD_BA <= address(12 downto 11); --bank_address
|
|
if command = COMMAND_ACTIVE or state_current = STATE_POWER_ON then
|
|
SD_A <= address(25 downto 13); --address row
|
|
elsif command = COMMAND_READ or command = COMMAND_WRITE then
|
|
SD_A <= "000" & address(10 downto 2) & "0"; --address col
|
|
else
|
|
SD_A <= "0010000000000"; --PERCHARGE all banks
|
|
end if;
|
|
|
|
SD_CS <= not cke_reg; --chip_select
|
|
SD_RAS <= command(2); --row_address_strobe
|
|
SD_CAS <= command(1); --column_address_strobe
|
|
SD_WE <= command(0); --write_enable
|
|
|
|
if active = '1' and state_current /= STATE_POWER_ON and
|
|
command /= COMMAND_WRITE and state_prev /= STATE_READ3 then
|
|
pause <= '1';
|
|
else
|
|
pause <= '0';
|
|
end if;
|
|
|
|
end process; --ddr_proc
|
|
|
|
end; --architecture logic
|
|
|