-
Job van Wee authoredJob van Wee authored
Code owners
Assign users and groups as approvers for specific file changes. Learn more.
ddrctrl_controller.vhd 13.82 KiB
-------------------------------------------------------------------------------
--
-- Copyright 2022
-- ASTRON (Netherlands Institute for Radio Astronomy) <http://www.astron.nl/>
-- P.O.Box 2, 7990 AA Dwingeloo, The Netherlands
--
-- Licensed under the Apache License, Version 2.0 (the "License");
-- you may not use this file except in compliance with the License.
-- You may obtain a copy of the License at
--
-- http://www.apache.org/licenses/LICENSE-2.0
--
-- Unless required by applicable law or agreed to in writing, software
-- distributed under the License is distributed on an "AS IS" BASIS,
-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-- See the License for the specific language governing permissions and
-- limitations under the License.
--
-------------------------------------------------------------------------------
-- Author: Job van Wee
-- Purpose: controller for ddrctrl, decides when to write when to read or when to stop writing or reading.
--
-- Description:
--
-- Remark:
-- Use VHDL coding template from:
-- https://support.astron.nl/confluence/display/SBe/VHDL+design+patterns+for+RTL+coding
--
LIBRARY IEEE, dp_lib, common_lib, tech_ddr_lib;
USE IEEE.std_logic_1164.ALL;
USE IEEE.numeric_std.ALL;
USE dp_lib.dp_stream_pkg.ALL;
USE common_lib.common_mem_pkg.ALL;
USE common_lib.common_pkg.ALL;
USE tech_ddr_lib.tech_ddr_pkg.ALL;
ENTITY ddrctrl_controller IS
GENERIC (
g_tech_ddr : t_c_tech_ddr;
g_stop_percentage : NATURAL := 50;
g_nof_streams : NATURAL;
g_out_data_w : NATURAL;
g_wr_data_w : NATURAL;
g_rd_fifo_depth : NATURAL;
g_rd_data_w : NATURAL;
g_block_size : NATURAL;
g_rd_fifo_uw_w : NATURAL;
g_max_adr : NATURAL;
g_burstsize : NATURAL;
g_last_burstsize : NATURAL
);
PORT (
clk : IN STD_LOGIC;
rst : IN STD_LOGIC;
-- ddrctrl_input
inp_of : IN NATURAL;
inp_sosi : IN t_dp_sosi;
inp_adr : IN NATURAL;
inp_ds : IN NATURAL;
inp_bsn_adr : IN NATURAL;
inp_data_stopped : IN STD_LOGIC;
rst_ddrctrl_input : OUT STD_LOGIC;
-- io_ddr
dvr_mosi : OUT t_mem_ctlr_mosi;
dvr_miso : IN t_mem_ctlr_miso;
wr_sosi : OUT t_dp_sosi;
rd_fifo_usedw : IN STD_LOGIC_VECTOR(g_rd_fifo_uw_w-1 DOWNTO 0);
-- ddrctrl_output
outp_ds : OUT NATURAL;
outp_bsn : OUT STD_LOGIC_VECTOR(c_dp_stream_bsn_w-1 DOWNTO 0) := (OTHERS => '0');
-- ddrctrl_controller
stop_in : IN STD_LOGIC;
stop_out : OUT STD_LOGIC
);
END ddrctrl_controller;
ARCHITECTURE rtl OF ddrctrl_controller IS
CONSTANT c_bitshift_adr : NATURAL := ceil_log2(g_burstsize); -- bitshift to make sure there is only a burst start at a interval of c_burstsize.
CONSTANT c_adr_w : NATURAL := func_tech_ddr_ctlr_address_w( g_tech_ddr ); -- the lengt of the address vector, for simulation this is smaller, otherwise the simulation would take to long, 27
CONSTANT c_pof_ma : NATURAL := (g_max_adr*(100-g_stop_percentage))/100;
CONSTANT c_zeros : STD_LOGIC_VECTOR(c_bitshift_adr-1 DOWNTO 0) := (OTHERS => '0');
-- constant for reading
CONSTANT c_rd_data_w : NATURAL := g_nof_streams*g_out_data_w; -- 168
CONSTANT c_rest : NATURAL := c_rd_data_w-(g_wr_data_w mod c_rd_data_w); -- 96
CONSTANT c_max_read_cnt : NATURAL := (g_max_adr+1)/g_burstsize; -- 256
-- type for statemachine
TYPE t_state IS (RESET, WRITING, SET_STOP, STOP_WRITING, START_READING, READING, STOP_READING, IDLE);
-- record for readability
TYPE t_reg IS RECORD
-- state of program
state : t_state;
started : STD_LOGIC;
-- stopping signals
stop_adr : STD_LOGIC_VECTOR(c_adr_w-1 DOWNTO 0);
stopped : STD_LOGIC;
rst_ddrctrl_input : STD_LOGIC;
-- writing signals
need_burst : STD_LOGIC;
-- reading signals
outp_ds : NATURAL;
outp_bsn : STD_LOGIC_VECTOR(c_dp_stream_bsn_w-1 DOWNTO 0);
read_cnt : NATURAL;
rd_burst_en : STD_LOGIC;
-- output
dvr_mosi : t_mem_ctlr_mosi;
wr_sosi : t_dp_sosi;
END RECORD;
CONSTANT c_t_reg_init : t_reg := (RESET, '0', TO_UVEC(g_max_adr, c_adr_w), '1', '1', '0', 0, (OTHERS => '0'), 0, '0', c_mem_ctlr_mosi_rst, c_dp_sosi_init);
-- signals for readability
SIGNAL d_reg : t_reg := c_t_reg_init;
SIGNAL q_reg : t_reg := c_t_reg_init;
BEGIN
q_reg <= d_reg WHEN rising_edge(clk);
-- put the input data into c_v and fill the output vector from c_v
p_state : PROCESS(q_reg, rst, inp_of, inp_sosi, inp_adr, inp_ds, inp_bsn_adr, inp_data_stopped, dvr_miso, rd_fifo_usedw, stop_in)
VARIABLE v : t_reg := c_t_reg_init;
BEGIN
v := q_reg;
CASE q_reg.state IS
WHEN RESET =>
v := c_t_reg_init;
v.dvr_mosi.burstbegin := '1';
v.dvr_mosi.burstsize(dvr_mosi.burstsize'length-1 DOWNTO 0) := (OTHERS => '0');
v.dvr_mosi.wr := '1';
WHEN WRITING =>
-- if adr mod g_burstsize = 0
-- this makes sure that only ones every 64 writes a writeburst is started.
IF TO_UVEC(inp_adr, c_adr_w)(c_bitshift_adr-1 DOWNTO 0) = c_zeros AND q_reg.dvr_mosi.burstbegin = '0'THEN
v.need_burst := '1';
END IF;
IF dvr_miso.done = '1' AND q_reg.need_burst = '1' THEN
v.dvr_mosi.burstbegin := '1';
v.need_burst := '0';
IF inp_adr < g_burstsize-1 THEN
v.dvr_mosi.address := TO_UVEC(g_max_adr-g_last_burstsize, dvr_mosi.address'length);
v.dvr_mosi.burstsize := TO_UVEC(g_last_burstsize, dvr_mosi.burstsize'length);
ELSE
v.dvr_mosi.address := TO_UVEC(inp_adr-g_burstsize, dvr_mosi.address'length);
v.dvr_mosi.address(c_bitshift_adr-1 DOWNTO 0) := c_zeros(c_bitshift_adr-1 DOWNTO 0); -- makes sure that a burst is only started on a multiple of g_burstsize
v.dvr_mosi.burstsize := TO_UVEC(g_burstsize, dvr_mosi.burstsize'length);
END IF;
ELSE
v.dvr_mosi.burstbegin := '0';
END IF;
v.dvr_mosi.wr := '1';
v.dvr_mosi.rd := '0';
v.wr_sosi := inp_sosi;
WHEN SET_STOP =>
--setting a stop address dependend on the g_stop_percentage
IF inp_adr+c_pof_ma >= g_max_adr THEN
v.stop_adr(c_adr_w-1 DOWNTO c_bitshift_adr) := TO_UVEC(inp_adr-c_pof_ma, c_adr_w)(c_adr_w-1 DOWNTO c_bitshift_adr);
ELSE
v.stop_adr(c_adr_w-1 DOWNTO c_bitshift_adr) := TO_UVEC(inp_adr+c_pof_ma, c_adr_w)(c_adr_w-1 DOWNTO c_bitshift_adr);
END IF;
v.stop_adr(c_bitshift_adr-1 DOWNTO 0) := c_zeros;
-- still a write cyle
-- if adr mod g_burstsize = 0
-- this makes sure that only ones every 64 writes a writeburst is started.
IF TO_UVEC(inp_adr, c_adr_w)(c_bitshift_adr-1 DOWNTO 0) = c_zeros AND q_reg.dvr_mosi.burstbegin = '0'THEN
v.need_burst := '1';
END IF;
IF dvr_miso.done = '1' AND q_reg.need_burst = '1' THEN
v.dvr_mosi.burstbegin := '1';
v.need_burst := '0';
IF inp_adr < g_burstsize-1 THEN
v.dvr_mosi.address := TO_UVEC(g_max_adr-g_last_burstsize, dvr_mosi.address'length);
v.dvr_mosi.burstsize := TO_UVEC(g_last_burstsize, dvr_mosi.burstsize'length);
ELSE
v.dvr_mosi.address := TO_UVEC(inp_adr-g_burstsize, dvr_mosi.address'length);
v.dvr_mosi.address(c_bitshift_adr-1 DOWNTO 0) := c_zeros(c_bitshift_adr-1 DOWNTO 0); -- makes sure that a burst is only started on a multiple of g_burstsize
v.dvr_mosi.burstsize := TO_UVEC(g_burstsize, dvr_mosi.burstsize'length);
END IF;
ELSE
v.dvr_mosi.burstbegin := '0';
END IF;
v.dvr_mosi.wr := '1';
v.dvr_mosi.rd := '0';
v.wr_sosi := inp_sosi;
WHEN STOP_WRITING =>
v.dvr_mosi.burstbegin := '0';
v.stopped := '1';
-- wait until the write burst is finished
IF inp_data_stopped = '0' THEN
v.state := STOP_WRITING;
ELSIF dvr_miso.done = '1' AND q_reg.dvr_mosi.burstbegin = '0' AND q_reg.need_burst = '0' THEN
v.wr_sosi.valid := '0';
v.state := START_READING;
ELSE
v.state := STOP_WRITING;
END IF;
-- still receiving write data.
-- if adr mod g_burstsize = 0
-- this makes sure that only ones every 64 writes a writeburst is started.
IF TO_UVEC(inp_adr, c_adr_w)(c_bitshift_adr-1 DOWNTO 0) = c_zeros AND q_reg.dvr_mosi.burstbegin = '0'THEN
v.need_burst := '1';
END IF;
IF dvr_miso.done = '1' AND q_reg.need_burst = '1' THEN
v.dvr_mosi.burstbegin := '1';
v.need_burst := '0';
IF inp_adr < g_burstsize-1 THEN
v.dvr_mosi.address := TO_UVEC(g_max_adr-g_last_burstsize, dvr_mosi.address'length);
v.dvr_mosi.burstsize := TO_UVEC(g_last_burstsize, dvr_mosi.burstsize'length);
ELSE
v.dvr_mosi.address := TO_UVEC(inp_adr-g_burstsize, dvr_mosi.address'length);
v.dvr_mosi.address(c_bitshift_adr-1 DOWNTO 0) := c_zeros(c_bitshift_adr-1 DOWNTO 0); -- makes sure that a burst is only started on a multiple of g_burstsize
v.dvr_mosi.burstsize := TO_UVEC(g_burstsize, dvr_mosi.burstsize'length);
END IF;
ELSE
v.dvr_mosi.burstbegin := '0';
END IF;
v.dvr_mosi.wr := '1';
v.dvr_mosi.rd := '0';
v.wr_sosi := inp_sosi;
WHEN START_READING =>
v.rd_burst_en := '1';
v.dvr_mosi.wr := '0';
v.dvr_mosi.rd := '1';
v.outp_ds := inp_ds;
FOR I IN 0 TO inp_bsn_adr+(g_max_adr-TO_UINT(q_reg.stop_adr)) LOOP -- takes a while WRONG, wil be fixed after L2SDP-705, 706 and 70
IF v.outp_ds-c_rest <= 0 THEN
v.outp_ds := v.outp_ds+c_rd_data_w-c_rest;
ELSE
v.outp_ds := v.outp_ds-c_rest;
END IF;
END LOOP;
v.outp_bsn := TO_UVEC(TO_UINT(inp_sosi.bsn), c_dp_stream_bsn_w); -- WRONG, wil be fixed after L2SDP-705, 706 and 707
v.state := READING;
WHEN READING =>
-- rd_fifo needs a refil after rd_fifo_usedw <= 10 because of delays, if you wait until rd_fifo_usedw = 0 then you get an empty fifo which results in your outputs sosi.valid not being constatly valid.
IF TO_UINT(rd_fifo_usedw) <= 10 AND dvr_miso.done = '1' AND q_reg.rd_burst_en = '1' AND dvr_miso.done = '1' THEN
IF TO_UINT(q_reg.stop_adr(c_adr_w-1 DOWNTO 0))+g_burstsize*q_reg.read_cnt >= g_max_adr THEN
v.dvr_mosi.address(c_adr_w-1 DOWNTO 0) := TO_UVEC((TO_UINT(q_reg.stop_adr(c_adr_w-1 DOWNTO 0))+g_burstsize*q_reg.read_cnt)-g_max_adr-1, c_adr_w);
ELSE
v.dvr_mosi.address(c_adr_w-1 DOWNTO 0) := TO_UVEC(TO_UINT(q_reg.stop_adr(c_adr_w-1 DOWNTO 0))+g_burstsize*q_reg.read_cnt, c_adr_w);
END IF;
v.dvr_mosi.burstbegin := '1';
v.read_cnt := v.read_cnt+1;
v.rd_burst_en := '0';
ELSE
v.dvr_mosi.burstbegin := '0';
END IF;
-- makes sure the fifo is filled before asking for another rd request. to prevent 4 rd burst to happend directly after one another.
IF TO_UINT(rd_fifo_usedw) = 11 THEN
v.rd_burst_en := '1';
END IF;
IF q_reg.read_cnt >= c_max_read_cnt THEN
v.state := STOP_READING;
ELSE
v.state := READING;
END IF;
WHEN STOP_READING =>
IF dvr_miso.done = '1' THEN
v.rst_ddrctrl_input := '0';
v.stopped := '0';
v.state := IDLE;
ELSE
v.state := STOP_READING;
END IF;
WHEN IDLE =>
v.wr_sosi.valid := '0';
-- the statemachine goes to Idle when its finished or when its waiting on other components.
WHEN OTHERS =>
v := c_t_reg_init;
END CASE;
IF q_reg.state = RESET OR q_reg.state = WRITING OR q_reg.state = SET_STOP OR q_reg.state = IDLE THEN
IF stop_in = '1' THEN
v.state := SET_STOP;
ELSIF v.stop_adr = TO_UVEC(inp_adr, c_adr_w) AND v.stop_adr(c_bitshift_adr-1 DOWNTO 0) = c_zeros(c_bitshift_adr-1 DOWNTO 0) AND q_reg.stopped = '0' THEN
v.state := STOP_WRITING;
ELSIF v.stopped = '0' AND inp_sosi.valid = '1' AND q_reg.started = '1' THEN
v.state := WRITING;
v.wr_sosi := inp_sosi;
ELSIF q_reg.stopped = '1' THEN
v.state := STOP_READING;
ELSE
v.state := IDLE;
END IF;
END IF;
IF rst = '1' THEN
v.state := RESET;
END IF;
IF inp_sosi.eop = '1' THEN
v.started := '1';
v.wr_sosi.valid := '1';
END IF;
d_reg <= v;
END PROCESS;
-- fill outputs
dvr_mosi <= q_reg.dvr_mosi;
wr_sosi <= q_reg.wr_sosi;
stop_out <= q_reg.stopped;
outp_bsn <= q_reg.outp_bsn;
outp_ds <= q_reg.outp_ds;
rst_ddrctrl_input <= q_reg.rst_ddrctrl_input OR rst;
END rtl;