Skip to content
Snippets Groups Projects
Commit f894602f authored by Eric Kooistra's avatar Eric Kooistra
Browse files

Ported $UNB ddr3.vhd to $RADIOHDL io_ddr.vhd. Initial version, still needs to...

Ported $UNB ddr3.vhd to $RADIOHDL io_ddr.vhd. Initial version, still needs to be compiled and simulated with tb_io_ddr.vhd.
parent 3c8fc0e5
No related branches found
No related tags found
No related merge requests found
hdl_lib_name = io_ddr
hdl_library_clause_name = io_ddr_lib
hdl_lib_uses = common technology tech_ddr tech_ddr3 dp diag
hdl_lib_technology =
build_dir_sim = $HDL_BUILD_DIR
build_dir_synth = $HDL_BUILD_DIR
synth_files =
src/vhdl/io_ddr_driver.vhd
src/vhdl/io_ddr.vhd
test_bench_files =
src/vhdl/tb_io_ddr.vhd
quartus_qip_files =
--------------------------------------------------------------------------------
--
-- Copyright (C) 2014
-- ASTRON (Netherlands Institute for Radio Astronomy) <http://www.astron.nl/>
-- JIVE (Joint Institute for VLBI in Europe) <http://www.jive.nl/>
-- P.O.Box 2, 7990 AA Dwingeloo, The Netherlands
--
-- This program is free software: you can redistribute it and/or modify
-- it under the terms of the GNU General Public License as published by
-- the Free Software Foundation, either version 3 of the License, or
-- (at your option) any later version.
--
-- This program is distributed in the hope that it will be useful,
-- but WITHOUT ANY WARRANTY; without even the implied warranty of
-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-- GNU General Public License for more details.
--
-- You should have received a copy of the GNU General Public License
-- along with this program. If not, see <http://www.gnu.org/licenses/>.
--
--------------------------------------------------------------------------------
LIBRARY IEEE, technology_lib, tech_ddr_lib, common_lib, dp_lib;
USE IEEE.STD_LOGIC_1164.ALL;
USE common_lib.common_pkg.ALL;
USE technology_lib.technology_select_pkg.ALL;
USE technology_lib.technology_pkg.ALL;
USE tech_ddr_lib.tech_ddr_pkg.ALL;
USE dp_lib.dp_stream_pkg.ALL;
ENTITY io_ddr IS
GENERIC(
g_technology : NATURAL := c_tech_select_default;
g_tech_ddr : t_c_tech_ddr;
g_wr_data_w : NATURAL := c_tech_ddr_ctlr.data_w;
g_wr_use_ctrl : BOOLEAN := FALSE; -- TRUE to allow filling the WR FIFO (by disabling flush) after an EOP
g_wr_fifo_depth : NATURAL := 128; -- >=16 AND >c_tech_ddr_ctlr.maxburstsize , defined at read side of write FIFO.
g_rd_fifo_depth : NATURAL := 256; -- >=16 AND >c_tech_ddr_ctlr.maxburstsize > c_ddr_ctrl_nof_latent_reads, defined at write side of read FIFO.
g_rd_data_w : NATURAL := c_tech_ddr_ctlr.data_w;
g_flush_wr_fifo : BOOLEAN := FALSE; -- TRUE instantiates a dp_flush + controller to flush the write fifo when the driver is not ready to write
g_flush_sop : BOOLEAN := FALSE;
g_flush_sop_channel : BOOLEAN := FALSE;
g_flush_sop_start_channel : NATURAL := 0;
g_flush_nof_channels : NATURAL := 0
);
PORT (
ctlr_ref_clk : IN STD_LOGIC;
ctlr_ref_rst : IN STD_LOGIC;
ctlr_gen_clk : OUT STD_LOGIC; -- Controller generated clock
ctlr_gen_rst : OUT STD_LOGIC;
ctlr_gen_clk_2x : OUT STD_LOGIC; -- Controller generated double frequency clock
ctlr_gen_rst_2x : OUT STD_LOGIC; -- ctlr_gen_rst synchronized to ctlr_gen_clk_2x
ctlr_init_done : OUT STD_LOGIC;
ctlr_rdy : OUT STD_LOGIC;
dvr_start_addr : IN t_tech_ddr_addr;
dvr_end_addr : IN t_tech_ddr_addr;
dvr_en : IN STD_LOGIC;
dvr_wr_not_rd : IN STD_LOGIC;
dvr_done : OUT STD_LOGIC;
wr_clk : IN STD_LOGIC;
wr_rst : IN STD_LOGIC;
wr_sosi : IN t_dp_sosi;
wr_siso : OUT t_dp_siso;
rd_sosi : OUT t_dp_sosi;
rd_siso : IN t_dp_siso;
rd_clk : IN STD_LOGIC;
rd_rst : IN STD_LOGIC;
rd_fifo_usedw : OUT STD_LOGIC_VECTOR(ceil_log2(g_rd_fifo_depth * (g_tech_ddr.data_w/g_rd_data_w) )-1 DOWNTO 0);
phy_in : IN t_tech_ddr_phy_in;
phy_io : INOUT t_tech_ddr_phy_io;
phy_ou : OUT t_tech_ddr_phy_ou
);
END io_ddr;
ARCHITECTURE str OF io_ddr IS
CONSTANT c_wr_fifo_depth : NATURAL := g_wr_fifo_depth * (g_tech_ddr.data_w/g_wr_data_w); -- Multiply fifo depth by the fifo's rd/wr width ratio to get write side depth
CONSTANT c_latency : NATURAL := 1;
SIGNAL ctlr_burst : STD_LOGIC;
SIGNAL ctlr_burst_size : STD_LOGIC_VECTOR(g_tech_ddr.maxburstsize_w-1 DOWNTO 0);
SIGNAL ctlr_address : STD_LOGIC_VECTOR(ceil_log2(g_tech_ddr.cs_w-1) + g_tech_ddr.ba_w + g_tech_ddr.a_w + g_tech_ddr.a_col_w - g_tech_ddr.rsl_w-1 DOWNTO 0); -- ceil_log2(..-1) because the chip select lines are converted to a logical address
SIGNAL ctlr_rd_req : STD_LOGIC;
SIGNAL ctlr_wr_req : STD_LOGIC;
SIGNAL ctlr_ref_rst_n : STD_LOGIC;
SIGNAL ctlr_gen_rst_n : STD_LOGIC;
SIGNAL i_ctlr_gen_clk : STD_LOGIC;
SIGNAL i_ctlr_gen_rst : STD_LOGIC;
SIGNAL i_ctlr_gen_clk_2x : STD_LOGIC;
SIGNAL i_ctlr_init_done : STD_LOGIC;
SIGNAL i_ctlr_rdy : STD_LOGIC;
SIGNAL i_dvr_done : STD_LOGIC;
SIGNAL dvr_cur_addr : t_tech_ddr_addr;
SIGNAL dvr_flush : STD_LOGIC := '0';
SIGNAL ctlr_wr_siso : t_dp_siso := c_dp_siso_rdy; -- default xon='1'
SIGNAL ctlr_wr_sosi : t_dp_sosi;
SIGNAL flush_wr_siso : t_dp_siso;
SIGNAL flush_wr_sosi : t_dp_sosi;
SIGNAL ctlr_rd_siso : t_dp_siso;
SIGNAL ctlr_rd_sosi : t_dp_sosi;
SIGNAL wr_fifo_usedw : STD_LOGIC_VECTOR(ceil_log2(g_wr_fifo_depth)-1 DOWNTO 0); -- read side depth of the write FIFO
BEGIN
dvr_done <= i_dvr_done;
ctlr_ref_rst_n <= NOT(ctlr_ref_rst);
i_ctlr_gen_rst <= NOT(ctlr_gen_rst_n);
ctlr_gen_clk <= i_ctlr_gen_clk;
ctlr_gen_rst <= i_ctlr_gen_rst;
ctlr_gen_clk_2x <= i_ctlr_gen_clk_2x;
ctlr_rdy <= i_ctlr_rdy;
ctlr_init_done <= i_ctlr_init_done;
u_wr_fifo : ENTITY dp_lib.dp_fifo_dc_mixed_widths
GENERIC MAP (
g_wr_data_w => g_wr_data_w,
g_rd_data_w => g_tech_ddr.data_w,
g_use_ctrl => g_wr_use_ctrl,
g_wr_fifo_size => c_wr_fifo_depth,
g_wr_fifo_af_margin => 4 + c_latency, --default (4) + c_latency to compensate for latency introduced by registering wr_siso.ready
g_rd_fifo_rl => 0
)
PORT MAP (
wr_rst => wr_rst,
wr_clk => wr_clk,
rd_rst => i_ctlr_gen_rst,
rd_clk => i_ctlr_gen_clk,
snk_out => wr_siso,
snk_in => wr_sosi,
wr_usedw => OPEN,
rd_usedw => wr_fifo_usedw,
rd_emp => OPEN,
src_in => flush_wr_siso,
src_out => flush_wr_sosi
);
u_dp_flush : ENTITY dp_lib.dp_flush -- Always instantiate the flusher as it also contains a RL adapter
GENERIC MAP (
g_ready_latency => 0,
g_framed_xon => g_wr_use_ctrl, -- stop flushing when dvr_flush is low and a sop has arrived
g_framed_xoff => FALSE -- immediately start flushing when dvr_flush goes high
)
PORT MAP (
rst => i_ctlr_gen_rst,
clk => i_ctlr_gen_clk,
snk_in => flush_wr_sosi,
snk_out => flush_wr_siso,
src_out => ctlr_wr_sosi,
src_in => ctlr_wr_siso, -- fixed streaming xon='1'
flush_en => dvr_flush -- memory mapped xon/xoff control
);
gen_flush : IF g_flush_wr_fifo = TRUE GENERATE
u_flush_ctrl : ENTITY work.ddr_flush_ctrl
GENERIC MAP (
g_sop => g_flush_sop,
g_sop_channel => g_flush_sop_channel,
g_sop_start_channel => g_flush_sop_start_channel,
g_nof_channels => g_flush_nof_channels
)
PORT MAP (
rst => wr_rst,
clk => wr_clk,
dvr_en => dvr_en,
dvr_wr_not_rd => dvr_wr_not_rd,
dvr_done => i_dvr_done,
wr_sosi => wr_sosi,
dvr_flush => dvr_flush
);
END GENERATE;
u_rd_fifo : ENTITY dp_lib.dp_fifo_dc_mixed_widths
GENERIC MAP (
g_wr_data_w => g_tech_ddr.data_w,
g_rd_data_w => g_rd_data_w,
g_use_ctrl => FALSE,
g_wr_fifo_size => g_rd_fifo_depth,
g_wr_fifo_af_margin => c_ddr_ctrl_nof_latent_reads, -- >=4 (required by dp_fifo)
g_rd_fifo_rl => 1
)
PORT MAP (
wr_rst => i_ctlr_gen_rst,
wr_clk => i_ctlr_gen_clk,
rd_rst => rd_rst,
rd_clk => rd_clk,
snk_out => ctlr_rd_siso,
snk_in => ctlr_rd_sosi,
wr_usedw => OPEN,
rd_usedw => rd_fifo_usedw,
rd_emp => OPEN,
src_in => rd_siso,
src_out => rd_sosi
);
u_io_ddr_driver : ENTITY work.io_ddr_driver
GENERIC MAP (
g_tech_ddr => g_tech_ddr,
g_wr_fifo_depth => g_wr_fifo_depth
)
PORT MAP (
rst => i_ctlr_gen_rst,
clk => i_ctlr_gen_clk,
ctlr_rdy => i_ctlr_rdy,
ctlr_init_done => i_ctlr_init_done,
ctlr_wr_req => ctlr_wr_req,
ctlr_rd_req => ctlr_rd_req,
ctlr_burst => ctlr_burst,
ctlr_burst_size => ctlr_burst_size,
wr_val => ctlr_wr_sosi.valid,
wr_rdy => ctlr_wr_siso.ready,
rd_rdy => ctlr_rd_siso.ready,
cur_addr => dvr_cur_addr,
start_addr => dvr_start_addr,
end_addr => dvr_end_addr,
dvr_en => dvr_en,
dvr_wr_not_rd => dvr_wr_not_rd,
dvr_done => i_dvr_done,
wr_fifo_usedw => wr_fifo_usedw
);
ctlr_address <= dvr_cur_addr.chip &
dvr_cur_addr.bank &
dvr_cur_addr.row(g_tech_ddr.a_w-1 DOWNTO 0) &
dvr_cur_addr.column(g_tech_ddr.a_col_w-1 DOWNTO g_tech_ddr.rsl_w);
gen_uphy_4g_800_master : IF func_tech_ddr_module_size(c_tech_ddr)=4 AND g_tech_ddr.mts=800 GENERATE
u_uphy_4g_800_master : COMPONENT uphy_4g_800_master
PORT MAP (
pll_ref_clk => ctlr_ref_clk,
global_reset_n => ctlr_ref_rst_n,
soft_reset_n => '1',
afi_clk => i_ctlr_gen_clk,
afi_half_clk => OPEN,
afi_reset_n => ctlr_gen_rst_n,
mem_a => phy_ou.a(g_tech_ddr.a_w-1 DOWNTO 0),
mem_ba => phy_ou.ba(g_tech_ddr.ba_w-1 DOWNTO 0),
mem_ck => phy_io.clk(g_tech_ddr.clk_w-1 DOWNTO 0),
mem_ck_n => phy_io.clk_n(g_tech_ddr.clk_w-1 DOWNTO 0),
mem_cke => phy_ou.cke(g_tech_ddr.clk_w-1 DOWNTO 0),
mem_cs_n => phy_ou.cs_n(g_tech_ddr.cs_w-1 DOWNTO 0),
mem_dm => phy_ou.dm(g_tech_ddr.dm_w-1 DOWNTO 0),
mem_ras_n => phy_ou.ras_n,
mem_cas_n => phy_ou.cas_n,
mem_we_n => phy_ou.we_n,
mem_reset_n => phy_ou.reset_n,
mem_dq => phy_io.dq(g_tech_ddr.dq_w-1 DOWNTO 0),
mem_dqs => phy_io.dqs(g_tech_ddr.dqs_w-1 DOWNTO 0),
mem_dqs_n => phy_io.dqs_n(g_tech_ddr.dqs_w-1 DOWNTO 0),
mem_odt => phy_ou.odt(g_tech_ddr.cs_w-1 DOWNTO 0),
avl_ready => i_ctlr_rdy,
avl_burstbegin => ctlr_burst,
avl_addr => ctlr_address,
avl_rdata_valid => ctlr_rd_sosi.valid,
avl_rdata => ctlr_rd_sosi.data(g_tech_ddr.data_w-1 DOWNTO 0),
avl_wdata => ctlr_wr_sosi.data(g_tech_ddr.data_w-1 DOWNTO 0),
avl_be => (OTHERS => '1'),
avl_read_req => ctlr_rd_req,
avl_write_req => ctlr_wr_req,
avl_size => ctlr_burst_size,
local_init_done => i_ctlr_init_done,
local_cal_success => OPEN,
local_cal_fail => OPEN,
oct_rdn => phy_in.oct_rdn,
oct_rup => phy_in.oct_rup,
seriesterminationcontrol => OPEN,
parallelterminationcontrol => OPEN,
pll_mem_clk => i_ctlr_gen_clk_2x,
pll_write_clk => OPEN,
pll_write_clk_pre_phy_clk => OPEN,
pll_addr_cmd_clk => OPEN,
pll_locked => OPEN,
pll_avl_clk => OPEN,
pll_config_clk => OPEN,
dll_delayctrl => OPEN
);
END GENERATE;
END str;
--------------------------------------------------------------------------------
--
-- Copyright (C) 2014
-- ASTRON (Netherlands Institute for Radio Astronomy) <http://www.astron.nl/>
-- JIVE (Joint Institute for VLBI in Europe) <http://www.jive.nl/>
-- P.O.Box 2, 7990 AA Dwingeloo, The Netherlands
--
-- This program is free software: you can redistribute it and/or modify
-- it under the terms of the GNU General Public License as published by
-- the Free Software Foundation, either version 3 of the License, or
-- (at your option) any later version.
--
-- This program is distributed in the hope that it will be useful,
-- but WITHOUT ANY WARRANTY; without even the implied warranty of
-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-- GNU General Public License for more details.
--
-- You should have received a copy of the GNU General Public License
-- along with this program. If not, see <http://www.gnu.org/licenses/>.
--
--------------------------------------------------------------------------------
LIBRARY IEEE, tech_ddr_lib, common_lib;
USE IEEE.STD_LOGIC_1164.ALL;
USE common_lib.common_pkg.ALL;
USE tech_ddr_lib.tech_ddr_pkg.ALL;
ENTITY io_ddr_driver IS
GENERIC (
g_tech_ddr : t_c_tech_ddr;
g_wr_fifo_depth : NATURAL := 128
);
PORT (
clk : IN STD_LOGIC;
rst : IN STD_LOGIC;
ctlr_rdy : IN STD_LOGIC;
ctlr_init_done : IN STD_LOGIC;
ctlr_burst : OUT STD_LOGIC;
ctlr_burst_size : OUT STD_LOGIC_VECTOR(g_tech_ddr.maxburstsize_w-1 DOWNTO 0);
ctlr_wr_req : OUT STD_LOGIC;
ctlr_rd_req : OUT STD_LOGIC;
dvr_en : IN STD_LOGIC := '1';
dvr_wr_not_rd : IN STD_LOGIC;
dvr_done : OUT STD_LOGIC; -- Requested wr or rd sequence is done.
wr_val : IN STD_LOGIC;
wr_rdy : OUT STD_LOGIC;
rd_rdy : IN STD_LOGIC;
cur_addr : OUT t_tech_ddr_addr;
start_addr : IN t_tech_ddr_addr;
end_addr : IN t_tech_ddr_addr;
wr_fifo_usedw : IN STD_LOGIC_VECTOR
);
END io_ddr_driver;
ARCHITECTURE str OF io_ddr_driver IS
CONSTANT c_chip_addr_w : NATURAL := ceil_log2(g_tech_ddr.cs_w); --Chip sel lines converted to logical address
CONSTANT c_address_w : NATURAL := c_chip_addr_w + g_tech_ddr.ba_w + g_tech_ddr.a_w + g_tech_ddr.a_col_w +1; -- 1 bit added to detect overflow
CONSTANT c_margin : NATURAL := 2; -- wr_burst_size is updated one cycle after reading actual nof available words.
-- Subtract two (wr_fifo_usedw and wr_burst_size are both registered) so we cannot
-- post a request for a too large burst size, which could cause the wr_burst state
-- to be two valid words short.
SIGNAL req_burst_cycles : STD_LOGIC_VECTOR(g_tech_ddr.maxburstsize_w-1 DOWNTO 0);
SIGNAL nxt_req_burst_cycles : STD_LOGIC_VECTOR(g_tech_ddr.maxburstsize_w-1 DOWNTO 0);
TYPE t_state_enum IS (s_init, s_idle, s_wait1, s_wait2, s_wait3, s_rd_request, s_wr_request, s_wr_burst);
SIGNAL state : t_state_enum;
SIGNAL nxt_state : t_state_enum;
SIGNAL prev_state : t_state_enum;
SIGNAL wr_burst_size : NATURAL;
SIGNAL rd_burst_size : NATURAL;
SIGNAL nxt_wr_burst_size : NATURAL;
SIGNAL nxt_rd_burst_size : NATURAL;
SIGNAL i_ctlr_burst_size : STD_LOGIC_VECTOR(g_tech_ddr.maxburstsize_w-1 DOWNTO 0);
SIGNAL i_dvr_done : STD_LOGIC;
SIGNAL nxt_dvr_done : STD_LOGIC;
SIGNAL start_address : STD_LOGIC_VECTOR(c_address_w-1 DOWNTO 0);
SIGNAL end_address : STD_LOGIC_VECTOR(c_address_w-1 DOWNTO 0);
SIGNAL cur_address : STD_LOGIC_VECTOR(c_address_w-1 DOWNTO 0);
SIGNAL nxt_cur_address : STD_LOGIC_VECTOR(c_address_w-1 DOWNTO 0);
SIGNAL diff_address : STD_LOGIC_VECTOR(c_address_w-1 DOWNTO 0);
SIGNAL addresses_rem : STD_LOGIC_VECTOR(31 DOWNTO 0); -- nof words (on the user side interface) to rd/wr until end addr is reached
SIGNAL reg_addresses_rem : STD_LOGIC_VECTOR(31 DOWNTO 0); -- nof words (on the user side interface) to rd/wr until end addr is reached
SIGNAL reg_wr_fifo_usedw : STD_LOGIC_VECTOR(ceil_log2(g_wr_fifo_depth)-1 DOWNTO 0); -- read side depth of the write FIFO
BEGIN
ctlr_burst_size <= i_ctlr_burst_size;
dvr_done <= i_dvr_done;
p_clk : PROCESS(rst, clk)
BEGIN
IF rst='1' THEN
state <= s_init;
req_burst_cycles <= (OTHERS => '0');
i_dvr_done <= '0';
cur_address <= (OTHERS=>'0');
wr_burst_size <= 0;
rd_burst_size <= 0;
reg_addresses_rem <= (OTHERS=>'0');
reg_wr_fifo_usedw <= (OTHERS=>'0');
prev_state <= s_idle;
ELSIF rising_edge(clk) THEN
state <= nxt_state;
req_burst_cycles <= nxt_req_burst_cycles;
i_dvr_done <= nxt_dvr_done;
cur_address <= nxt_cur_address;
wr_burst_size <= nxt_wr_burst_size;
rd_burst_size <= nxt_rd_burst_size;
reg_addresses_rem <= addresses_rem;
reg_wr_fifo_usedw <= wr_fifo_usedw;
prev_state <= state;
END IF;
END PROCESS;
-- Add 1 address (accounting for address resulotion) to diff_address: we also want to write the last address. Shift the result right to provide the correct resolution.
addresses_rem <= RESIZE_UVEC( SHIFT_UVEC( INCR_UVEC(diff_address, g_tech_ddr.rsl), g_tech_ddr.rsl_w), addresses_rem'LENGTH);
-- End address - current address
diff_address <= SUB_UVEC(end_address, cur_address, c_address_w);
p_burst_size : PROCESS (reg_addresses_rem, reg_wr_fifo_usedw)
VARIABLE v_burst_size : NATURAL;
BEGIN
-- Write burst size is smallest of g_tech_ddr.maxburstsize, addresses_rem and wr_fifo_usedw
v_burst_size := g_tech_ddr.maxburstsize+c_margin;
IF UNSIGNED(reg_wr_fifo_usedw)>=c_margin AND UNSIGNED(reg_addresses_rem)>=c_margin THEN
IF v_burst_size > SIGNED('0' & reg_addresses_rem) THEN v_burst_size := TO_UINT(reg_addresses_rem); END IF;
IF v_burst_size > SIGNED('0' & reg_wr_fifo_usedw) THEN v_burst_size := TO_UINT(reg_wr_fifo_usedw); END IF;
v_burst_size := v_burst_size - c_margin;
ELSE
v_burst_size := 0;
END IF;
nxt_wr_burst_size <= v_burst_size;
-- Read burst size is smallest of g_tech_ddr.maxburstsize and addresses_rem
v_burst_size := g_tech_ddr.maxburstsize;
IF UNSIGNED(reg_addresses_rem)>=1 THEN -- prevent assigning <0 value to natural
IF v_burst_size > SIGNED('0' & reg_addresses_rem) THEN v_burst_size := TO_UINT(reg_addresses_rem); END IF;
ELSE
v_burst_size := 0;
END IF;
nxt_rd_burst_size <= v_burst_size;
END PROCESS;
p_state : PROCESS(prev_state, state, i_dvr_done, ctlr_rdy, req_burst_cycles, dvr_wr_not_rd, wr_val, wr_fifo_usedw, wr_burst_size, rd_burst_size, dvr_en, ctlr_init_done, reg_addresses_rem, rd_rdy, i_ctlr_burst_size, start_address, cur_address)
BEGIN
nxt_state <= state;
ctlr_wr_req <= '0';
ctlr_rd_req <= '0';
ctlr_burst <= '0';
i_ctlr_burst_size <= (OTHERS => '0');
nxt_req_burst_cycles <= req_burst_cycles;
wr_rdy <= '0';
nxt_dvr_done <= i_dvr_done;
nxt_cur_address <= cur_address;
CASE state IS
WHEN s_wr_burst => -- Performs the burst portion (word 2+)
ctlr_wr_req <= '1';
IF ctlr_rdy = '1' THEN -- when local_ready goes low, that cycle does not count as a burst cycle
nxt_req_burst_cycles <= INCR_UVEC(req_burst_cycles, -1);
wr_rdy <= '1'; -- wr side uses latency of 0, so wr_rdy<='1' acknowledges a successful write request.
IF UNSIGNED(req_burst_cycles) = 1 THEN -- Then we're in the last cycle of this burst sequence
nxt_state <= s_wr_request; -- We can only initiate a burst through the wr_request state
END IF;
END IF;
WHEN s_wr_request => -- Performs 1 write or read and goes into s_wr_burst when requested write words >1
nxt_state <= s_wait3;
IF UNSIGNED(reg_addresses_rem) = 0 THEN -- end address reached
nxt_dvr_done <= '1';
nxt_state <= s_idle;
ELSIF ctlr_rdy = '1' THEN
IF wr_val = '1' THEN
-- Always perform 1st write here
ctlr_burst <= '1'; -- assert burst begin: strictly this is a burst of 1.
ctlr_wr_req <= '1';
wr_rdy <= '1';
i_ctlr_burst_size <= TO_UVEC(1, g_tech_ddr.maxburstsize_w); -- Set ctlr_burst_size to 1 by default
IF wr_burst_size > 1 THEN
-- Perform any remaining writes in a burst
nxt_state <= s_wr_burst;
nxt_req_burst_cycles <= TO_UVEC(wr_burst_size-1, g_tech_ddr.maxburstsize_w); -- Forward the required nof burst cycles (-1 as we've done the 1st in this state already) to burst state
i_ctlr_burst_size <= TO_UVEC(wr_burst_size , g_tech_ddr.maxburstsize_w);
END IF; -- ELSE: there is only 1 word, so no need for remaining burst
nxt_cur_address <= INCR_UVEC(cur_address, UNSIGNED(i_ctlr_burst_size)*g_tech_ddr.rsl);
-- IF UNSIGNED(i_ctlr_burst_size) = 1 THEN -- Prevents FSM from going into this state again too soon (reg_addresses_rem invalid)
-- nxt_state <= s_wait3;
-- END IF;
END IF;
END IF;
WHEN s_rd_request => -- Posts a read request for a burst (0...g_tech_ddr.maxburstsize)
nxt_state <= s_wait3;
IF UNSIGNED(reg_addresses_rem) = 0 THEN -- end address reached
nxt_dvr_done <= '1';
nxt_state <= s_idle;
ELSE
IF rd_rdy = '1' THEN -- Fifo uses its internal almost_full signal to toggle its snk_out.rdy
IF ctlr_rdy = '1' THEN
ctlr_rd_req <= '1';
ctlr_burst <= '1'; -- assert burst begin: strictly this is a burst of 1.
i_ctlr_burst_size <= TO_UVEC(rd_burst_size, g_tech_ddr.maxburstsize_w);
IF rd_burst_size = 0 THEN i_ctlr_burst_size <= TO_UVEC(1, g_tech_ddr.maxburstsize_w); END IF;
nxt_cur_address <= INCR_UVEC(cur_address, UNSIGNED(i_ctlr_burst_size)*g_tech_ddr.rsl);
-- IF UNSIGNED(i_ctlr_burst_size) = 1 THEN -- Prevents FSM from going into this state again too soon (reg_addresses_rem invalid)
-- nxt_state <= s_wait3;
-- END IF;
END IF;
END IF;
END IF;
-- This wait state is inserted between two requests when necessary, e.g. when FSM enters wr_request
-- from the state wr_request, an extra cycle is needed for reg_addresses_rem to be valid.
WHEN s_wait3 =>
IF prev_state = s_wr_request THEN nxt_state <= s_wr_request; END IF;
IF prev_state = s_rd_request THEN nxt_state <= s_rd_request; END IF;
-- In this cycle reg_addresses_rem is valid. This cycle is added so wr_burst_size and rd_burst_size
-- (derived from reg_addresses_rem) are valid the next cycle.
WHEN s_wait2 =>
IF dvr_wr_not_rd = '1' THEN
nxt_state <= s_wr_request;
ELSE
nxt_state <= s_rd_request;
END IF;
-- Wait a cycle so reg_addresses_rem is valid the next cyle.
WHEN s_wait1 =>
nxt_state <= s_wait2;
WHEN s_idle =>
IF dvr_en = '1' THEN
nxt_cur_address <= start_address;
nxt_dvr_done <= '0';
nxt_state <= s_wait1;
END IF;
WHEN OTHERS => -- s_init
IF ctlr_init_done = '1' THEN
nxt_state <= s_idle;
END IF;
END CASE;
END PROCESS;
end_address <= RESIZE_UVEC( end_addr.chip & end_addr.bank & end_addr.row(g_tech_ddr.a_w-1 DOWNTO 0) & end_addr.column(g_tech_ddr.a_col_w-1 DOWNTO 0), c_address_w);
start_address <= RESIZE_UVEC(start_addr.chip & start_addr.bank & start_addr.row(g_tech_ddr.a_w-1 DOWNTO 0) & start_addr.column(g_tech_ddr.a_col_w-1 DOWNTO 0), c_address_w);
cur_addr.chip( c_chip_addr_w -1 DOWNTO 0) <= cur_address(c_chip_addr_w+g_tech_ddr.ba_w+g_tech_ddr.a_w+g_tech_ddr.a_col_w-1 DOWNTO g_tech_ddr.ba_w+g_tech_ddr.a_w+g_tech_ddr.a_col_w);
cur_addr.bank( g_tech_ddr.ba_w -1 DOWNTO 0) <= cur_address( g_tech_ddr.ba_w+g_tech_ddr.a_w+g_tech_ddr.a_col_w-1 DOWNTO g_tech_ddr.a_w+g_tech_ddr.a_col_w);
cur_addr.row( g_tech_ddr.a_w -1 DOWNTO 0) <= cur_address( g_tech_ddr.a_w+g_tech_ddr.a_col_w-1 DOWNTO g_tech_ddr.a_col_w);
cur_addr.column(g_tech_ddr.a_col_w-1 DOWNTO 0) <= cur_address( g_tech_ddr.a_col_w-1 DOWNTO 0);
END str;
--------------------------------------------------------------------------------
--
-- Copyright (C) 2014
-- ASTRON (Netherlands Institute for Radio Astronomy) <http://www.astron.nl/>
-- JIVE (Joint Institute for VLBI in Europe) <http://www.jive.nl/>
-- P.O.Box 2, 7990 AA Dwingeloo, The Netherlands
--
-- This program is free software: you can redistribute it and/or modify
-- it under the terms of the GNU General Public License as published by
-- the Free Software Foundation, either version 3 of the License, or
-- (at your option) any later version.
--
-- This program is distributed in the hope that it will be useful,
-- but WITHOUT ANY WARRANTY; without even the implied warranty of
-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-- GNU General Public License for more details.
--
-- You should have received a copy of the GNU General Public License
-- along with this program. If not, see <http://www.gnu.org/licenses/>.
--
--------------------------------------------------------------------------------
-- This testbench tests the different type of DDR controllers:
--
-- - uphy_4g_800_master
-- - uphy_4g_800_slave
--
-- The DUT can be selected, using the c_tech_ddr constants.
--
-- Testbench is selftesting:
--
-- > run -all
--
LIBRARY IEEE, technology_lib, tech_ddr_lib, common_lib, dp_lib, diagnostics_lib;
USE IEEE.STD_LOGIC_1164.ALL;
USE IEEE.numeric_std.ALL;
USE common_lib.common_pkg.ALL;
USE common_lib.common_mem_pkg.ALL;
USE common_lib.tb_common_mem_pkg.ALL;
USE dp_lib.dp_stream_pkg.ALL;
USE technology_lib.technology_pkg.ALL;
USE tech_ddr_lib.tech_ddr_pkg.ALL;
ENTITY tb_io_ddr IS
END ENTITY tb_io_ddr;
ARCHITECTURE str of tb_io_ddr IS
CONSTANT c_ctlr_ref_clk_period : TIME := 5 ns; -- 200 MHz
CONSTANT c_tech_ddr : t_c_tech_ddr := c_tech_ddr_4g_800m;
CONSTANT c_data_w : NATURAL := 256; --32;
SIGNAL ctlr_ref_clk : STD_LOGIC := '0';
SIGNAL ctlr_ref_rst : STD_LOGIC := '1';
SIGNAL tb_end : STD_LOGIC := '0';
SIGNAL ctlr_gen_clk : STD_LOGIC;
SIGNAL ctlr_gen_rst : STD_LOGIC;
SIGNAL ctlr_rdy : STD_LOGIC;
SIGNAL ctlr_init_done : STD_LOGIC;
SIGNAL dvr_start_addr : t_tech_ddr_addr;
SIGNAL dvr_end_addr : t_tech_ddr_addr;
SIGNAL dvr_en : STD_LOGIC;
SIGNAL dvr_wr_not_rd : STD_LOGIC;
SIGNAL dvr_done : STD_LOGIC;
SIGNAL wr_siso : t_dp_siso;
SIGNAL wr_sosi : t_dp_sosi;
SIGNAL wr_siso_pre_fifo : t_dp_siso;
SIGNAL wr_sosi_pre_fifo : t_dp_sosi;
SIGNAL rd_siso : t_dp_siso;
SIGNAL rd_sosi : t_dp_sosi;
SIGNAL src_diag_en : STD_LOGIC;
SIGNAL src_val_cnt : STD_LOGIC_VECTOR(31 DOWNTO 0);
SIGNAL snk_diag_en : STD_LOGIC;
SIGNAL snk_diag_res : STD_LOGIC;
SIGNAL snk_diag_res_val : STD_LOGIC;
SIGNAL snk_val_cnt : STD_LOGIC_VECTOR(31 DOWNTO 0);
SIGNAL phy_in : t_tech_ddr_phy_in;
SIGNAL phy_io : t_tech_ddr_phy_io;
SIGNAL phy_ou : t_tech_ddr_phy_ou;
SIGNAL ras_n : STD_LOGIC_VECTOR(0 DOWNTO 0);
SIGNAL cas_n : STD_LOGIC_VECTOR(0 DOWNTO 0);
SIGNAL we_n : STD_LOGIC_VECTOR(0 DOWNTO 0);
BEGIN
ctlr_ref_clk <= NOT(ctlr_ref_clk) OR tb_end AFTER c_ctlr_ref_clk_period/2;
ctlr_ref_rst <= '0' AFTER 100 ns;
dvr_start_addr <= c_tech_ddr_addr_lo;
dvr_end_addr <= c_tech_ddr_addr_hi_sim;
p_stimuli : PROCESS
BEGIN
tb_end <= '0';
dvr_en <= '0';
src_diag_en <= '0';
dvr_wr_not_rd <= '0';
snk_diag_en <= '0';
WAIT UNTIL ctlr_init_done = '1';
FOR i IN 0 TO 1 LOOP
WAIT UNTIL rising_edge(ctlr_gen_clk); -- Give the driver FSM a cycle to go into idle mode
END LOOP;
-- START WRITE
src_diag_en <= '1';
dvr_wr_not_rd <= '1';
dvr_en <= '1';
WAIT UNTIL rising_edge(ctlr_gen_clk);
dvr_en <= '0';
-- WRITE DONE
WAIT UNTIL dvr_done = '1';
src_diag_en <= '0';
-- START READ
snk_diag_en <= '1';
dvr_wr_not_rd <= '0';
dvr_en <= '1';
WAIT UNTIL rising_edge(ctlr_gen_clk);
dvr_en <= '0';
-- READ DONE
WAIT UNTIL dvr_done = '1';
WAIT FOR 2 us; -- 'Done' means all requests are posted. Wait for the last read data to arrive.
ASSERT snk_diag_res_val = '1' REPORT "[ERROR] DIAG_RES INVALID!" SEVERITY FAILURE;
ASSERT snk_diag_res = '0' REPORT "[ERROR] NON-ZERO DIAG_RES!" SEVERITY FAILURE;
ASSERT FALSE REPORT "[OK] Test passed." SEVERITY NOTE;
tb_end <= '1';
WAIT;
END PROCESS;
u_diagnostics: ENTITY diagnostics_lib.diagnostics
GENERIC MAP (
g_dat_w => c_data_w,
g_nof_streams => 1
)
PORT MAP (
rst => ctlr_gen_rst,
clk => ctlr_gen_clk,
snk_out_arr(0) => rd_siso,
snk_in_arr(0) => rd_sosi,
snk_diag_en(0) => snk_diag_en,
snk_diag_md(0) => '1',
snk_diag_res(0) => snk_diag_res,
snk_diag_res_val(0) => snk_diag_res_val,
snk_val_cnt(0) => snk_val_cnt,
src_out_arr(0) => wr_sosi,
src_in_arr(0) => wr_siso,
src_diag_en(0) => src_diag_en,
src_diag_md(0) => '1',
src_val_cnt(0) => src_val_cnt
);
gen_ddr_4g_memory_model : IF func_tech_ddr_module_size(c_tech_ddr) = 4 GENERATE
u_4gb_ddr3_memory_model : COMPONENT alt_mem_if_ddr3_mem_model_top_ddr3_mem_if_dm_pins_en_mem_if_dqsn_en
GENERIC MAP (
MEM_IF_ADDR_WIDTH => 15,
MEM_IF_ROW_ADDR_WIDTH => 15,
MEM_IF_COL_ADDR_WIDTH => 10,
MEM_IF_CS_PER_RANK => 1,
MEM_IF_CONTROL_WIDTH => 1,
MEM_IF_DQS_WIDTH => 8,
MEM_IF_CS_WIDTH => 2,
MEM_IF_BANKADDR_WIDTH => 3,
MEM_IF_DQ_WIDTH => 64,
MEM_IF_CK_WIDTH => 2,
MEM_IF_CLK_EN_WIDTH => 2,
DEVICE_WIDTH => 1,
MEM_TRCD => 6,
MEM_TRTP => 3,
MEM_DQS_TO_CLK_CAPTURE_DELAY => 100,
MEM_CLK_TO_DQS_CAPTURE_DELAY => 100000,
MEM_IF_ODT_WIDTH => 2,
MEM_MIRROR_ADDRESSING_DEC => 0,
MEM_REGDIMM_ENABLED => false,
DEVICE_DEPTH => 1,
MEM_GUARANTEED_WRITE_INIT => false,
MEM_VERBOSE => true,
MEM_INIT_EN => false,
MEM_INIT_FILE => "",
DAT_DATA_WIDTH => 32
)
PORT MAP (
mem_a => phy_ou.a(c_tech_ddr.a_w-1 DOWNTO 0), -- memory.mem_a
mem_ba => phy_ou.ba, -- .mem_ba
mem_ck => phy_io.clk, -- .mem_ck
mem_ck_n => phy_io.clk_n, -- .mem_ck_n
mem_cke => phy_ou.cke(c_tech_ddr.cs_w-1 DOWNTO 0), -- .mem_cke
mem_cs_n => phy_ou.cs_n(c_tech_ddr.cs_w-1 DOWNTO 0), -- .mem_cs_n
mem_dm => phy_ou.dm, -- .mem_dm
mem_ras_n => ras_n, -- .mem_ras_n
mem_cas_n => cas_n, -- .mem_cas_n
mem_we_n => we_n, -- .mem_we_n
mem_reset_n => phy_ou.reset_n, -- .mem_reset_n
mem_dq => phy_io.dq, -- .mem_dq
mem_dqs => phy_io.dqs, -- .mem_dqs
mem_dqs_n => phy_io.dqs_n, -- .mem_dqs_n
mem_odt => phy_ou.odt -- .mem_odt
);
ras_n(0) <= phy_ou.ras_n;
cas_n(0) <= phy_ou.cas_n;
we_n(0) <= phy_ou.we_n;
END GENERATE;
u_io_ddr: ENTITY work.io_ddr
GENERIC MAP(
g_technology => c_tech_select_default,
g_ddr => c_tech_ddr,
g_wr_data_w => c_data_w,
g_rd_data_w => c_data_w
)
PORT MAP (
ctlr_ref_clk => ctlr_ref_clk,
ctlr_ref_rst => ctlr_ref_rst,
ctlr_gen_clk => ctlr_gen_clk,
ctlr_gen_rst => ctlr_gen_rst,
ctlr_init_done => ctlr_init_done,
ctlr_rdy => ctlr_rdy,
dvr_start_addr => dvr_start_addr,
dvr_end_addr => dvr_end_addr,
dvr_en => dvr_en,
dvr_wr_not_rd => dvr_wr_not_rd,
dvr_done => dvr_done,
wr_clk => ctlr_gen_clk,
wr_rst => ctlr_gen_rst,
wr_sosi => wr_sosi,
wr_siso => wr_siso,
rd_sosi => rd_sosi,
rd_siso => rd_siso,
rd_clk => ctlr_gen_clk,
rd_rst => ctlr_gen_rst,
phy_ou => phy_ou,
phy_io => phy_io,
phy_in => phy_in
);
END ARCHITECTURE str;
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment