1
0
mirror of git://projects.qi-hardware.com/nn-usb-fpga.git synced 2024-12-13 10:48:06 +02:00
nn-usb-fpga/plasma/logic/ddr_ctrl.vhd

355 lines
13 KiB
VHDL
Raw Normal View History

2010-04-22 04:01:38 +03:00
---------------------------------------------------------------------
-- 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