-
Job van Wee authoredJob van Wee authored
Code owners
Assign users and groups as approvers for specific file changes. Learn more.
ddrctrl_output_unpack.vhd 17.39 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: resize the data into a single sosi with a width of g_out_data_w.
--
-- Description:
-- The data gets collected into a vector(c_v) and from this vector the output
-- data gets read. When the reading passes the halfway point of c_v new data
-- is requested and the whole vector shifts g_in_data_w amount of bits to fit
-- the new data at the end.
--
-- Remark:
-- Use VHDL coding template from:
-- https://support.astron.nl/confluence/display/SBe/VHDL+design+patterns+for+RTL+coding
-- The output vector must be larger than the input vector.
LIBRARY IEEE, dp_lib, tech_ddr_lib, common_lib;
USE IEEE.std_logic_1164.ALL;
USE dp_lib.dp_stream_pkg.ALL;
USE tech_ddr_lib.tech_ddr_pkg.ALL;
USE common_lib.common_pkg.ALL;
ENTITY ddrctrl_output_unpack IS
GENERIC (
g_tech_ddr : t_c_tech_ddr;
g_in_data_w : NATURAL;
g_out_data_w : NATURAL;
g_block_size : NATURAL;
g_bim : NATURAL
);
PORT (
clk : IN STD_LOGIC;
rst : IN STD_LOGIC;
in_sosi : IN t_dp_sosi := c_dp_sosi_init;
in_bsn : IN STD_LOGIC_VECTOR(c_dp_stream_bsn_w-1 DOWNTO 0);
out_siso : IN t_dp_siso;
out_sosi : OUT t_dp_sosi := c_dp_sosi_init;
out_ready : OUT STD_LOGIC := '0';
state_off : OUT STD_LOGIC := '0'
);
END ddrctrl_output_unpack;
ARCHITECTURE rtl OF ddrctrl_output_unpack IS
CONSTANT c_v_w : NATURAL := g_in_data_w*2;
-- type for statemachine
TYPE t_state IS ( READING, FIRST_READ, OVER_HALF, BSN, RESET, IDLE, OFF);
-- record for readability
TYPE t_reg IS RECORD
state : t_state;
a_of : NATURAL;
op_data_cnt : NATURAL;
delay_data : STD_LOGIC_VECTOR(g_in_data_w-1 DOWNTO 0);
dd_fresh : STD_LOGIC;
valid_data : STD_LOGIC;
c_v : STD_LOGIC_VECTOR(c_v_w-1 DOWNTO 0);
bsn_cnt : NATURAL;
out_sosi : t_dp_sosi;
out_ready : STD_LOGIC;
state_off : STD_LOGIC;
END RECORD;
CONSTANT c_t_reg_init : t_reg := (RESET, 0, 0, (OTHERS => '0'), '0', '0', (OTHERS => '0'), 0, c_dp_sosi_init, '0', '0');
-- 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, in_bsn, in_sosi, out_siso)
VARIABLE v : t_reg;
BEGIN
v := q_reg;
IF out_siso.ready = '1' OR q_reg.state = OFF OR q_reg.state = IDLE OR q_reg.state = RESET OR rst = '1' THEN
CASE q_reg.state IS
WHEN READING =>
-- generating output from the data already present in c_v
v.out_ready := '0';
v.out_sosi.data(g_out_data_w-1 DOWNTO 0) := q_reg.c_v((g_out_data_w*(q_reg.op_data_cnt+1))+q_reg.a_of-1 DOWNTO (g_out_data_w*q_reg.op_data_cnt)+q_reg.a_of);
v.out_sosi.valid := '1';
v.bsn_cnt := q_reg.bsn_cnt+1;
v.op_data_cnt := q_reg.op_data_cnt+1;
IF q_reg.dd_fresh = '1' AND q_reg.valid_data = '0' THEN
-- put the delay data into the second half of c_v beceause these are now zeros
v.c_v(c_v_w-1 DOWNTO g_in_data_w) := q_reg.delay_data(g_in_data_w-1 DOWNTO 0);
v.dd_fresh := '0';
v.valid_data := '1';
END IF;
IF in_sosi.valid = '1' THEN
v.delay_data(g_in_data_w-1 DOWNTO 0) := in_sosi.data(g_in_data_w-1 DOWNTO 0);
v.dd_fresh := '1';
END IF;
IF (g_out_data_w*(v.op_data_cnt+1))+q_reg.a_of >= g_in_data_w AND (v.dd_fresh = '1' OR v.valid_data = '1') AND out_siso.ready = '1' THEN
v.state := OVER_HALF;
ELSIF (g_out_data_w*(v.op_data_cnt+1))+q_reg.a_of >= g_in_data_w AND v.dd_fresh = '0' AND v.valid_data = '0' THEN
v.state := IDLE;
ELSIF out_siso.ready = '1' THEN
v.state := READING;
ELSE
v.state := IDLE;
END IF;
IF q_reg.out_sosi.eop = '1' THEN
v.out_sosi.bsn(c_dp_stream_bsn_w-1 DOWNTO 0) := INCR_UVEC(q_reg.out_sosi.bsn, 1);
v.out_sosi.eop := '0';
v.out_sosi.sop := '1';
v.bsn_cnt := 0;
ELSIF q_reg.out_sosi.sop = '1' THEN
v.out_sosi.sop := '0';
END IF;
IF q_reg.bsn_cnt = g_block_size-3 AND out_siso.ready = '1' THEN
v.state := BSN;
v.out_ready := '1';
ELSIF q_reg.bsn_cnt = g_block_size-3 THEN
v.state := IDLE;
END IF;
WHEN OVER_HALF =>
-- generating output data from c_v but past the halfway point of c_v so there needs to be new data added
IF q_reg.dd_fresh = '1' AND q_reg.valid_data = '1' THEN
v.out_ready := '1';
-- generate output from the middle of c_v
v.out_sosi.data(g_out_data_w-1 DOWNTO 0) := q_reg.c_v((g_out_data_w*(q_reg.op_data_cnt+1))+q_reg.a_of-1 DOWNTO (g_out_data_w*q_reg.op_data_cnt)+q_reg.a_of);
v.out_sosi.valid := '1';
v.bsn_cnt := q_reg.bsn_cnt+1;
-- put the second half of c_v into the first half of c_v
v.c_v(g_in_data_w-1 DOWNTO 0) := q_reg.c_v(c_v_w-1 DOWNTO g_in_data_w);
-- put the delay data into the first half of c_v
v.c_v(c_v_w-1 DOWNTO g_in_data_w) := q_reg.delay_data(g_in_data_w-1 DOWNTO 0);
v.dd_fresh := '0';
v.a_of := ((g_out_data_w*(q_reg.op_data_cnt+1))+q_reg.a_of)-g_in_data_w;
v.op_data_cnt := 0;
ELSIF q_reg.dd_fresh = '0' AND q_reg.valid_data = '1' THEN
v.out_ready := '1';
-- generate output from the middle of c_v
v.out_sosi.data(g_out_data_w-1 DOWNTO 0) := q_reg.c_v((g_out_data_w*(q_reg.op_data_cnt+1))+q_reg.a_of-1 DOWNTO (g_out_data_w*q_reg.op_data_cnt)+q_reg.a_of);
v.out_sosi.valid := '1';
v.bsn_cnt := q_reg.bsn_cnt+1;
-- put the second half of c_v into the first half of c_v
v.c_v(g_in_data_w-1 DOWNTO 0) := q_reg.c_v(c_v_w-1 DOWNTO g_in_data_w);
-- put zeros into the second half of c_v beceause dd_fresh is '0'
v.c_v(c_v_w-1 DOWNTO g_in_data_w) := (OTHERS => '0');
v.a_of := ((g_out_data_w*(q_reg.op_data_cnt+1))+q_reg.a_of)-g_in_data_w;
v.op_data_cnt := 0;
v.valid_data := '0';
ELSIF q_reg.dd_fresh = '1' AND q_reg.valid_data = '0' THEN
v.out_ready := '1';
-- put the delay data into the second half of c_v beceause these are now zeros
v.c_v(c_v_w-1 DOWNTO g_in_data_w) := q_reg.delay_data(g_in_data_w-1 DOWNTO 0);
-- generate output from the middle of c_v
v.out_sosi.data(g_out_data_w-1 DOWNTO 0) := v.c_v((g_out_data_w*(q_reg.op_data_cnt+1))+q_reg.a_of-1 DOWNTO (g_out_data_w*q_reg.op_data_cnt)+q_reg.a_of);
-- put the second half of c_v into the first half of c_v
v.c_v(g_in_data_w-1 DOWNTO 0) := q_reg.c_v(c_v_w-1 DOWNTO g_in_data_w);
v.out_sosi.valid := '1';
v.bsn_cnt := q_reg.bsn_cnt+1;
v.dd_fresh := '0';
v.a_of := ((g_out_data_w*(q_reg.op_data_cnt+1))+q_reg.a_of)-g_in_data_w;
v.op_data_cnt := 0;
END IF;
IF in_sosi.valid = '1' THEN
v.delay_data(g_in_data_w-1 DOWNTO 0) := in_sosi.data(g_in_data_w-1 DOWNTO 0);
v.dd_fresh := '1';
END IF;
IF (g_out_data_w*(v.op_data_cnt+1))+v.a_of >= g_in_data_w AND (v.dd_fresh = '1' OR v.valid_data = '1') AND out_siso.ready = '1' THEN
v.state := OVER_HALF;
ELSIF q_reg.dd_fresh = '0' AND q_reg.valid_data = '0' THEN
v.state := IDLE;
ELSIF out_siso.ready = '1' THEN
v.state := READING;
ELSE
v.state := IDLE;
END IF;
IF q_reg.out_sosi.sop = '1' THEN
v.out_sosi.sop := '0';
END IF;
IF q_reg.bsn_cnt = g_block_size-3 AND out_siso.ready = '1' THEN
v.state := BSN;
v.dd_fresh := '1';
ELSIF q_reg.bsn_cnt = g_block_size-3 THEN
v.state := IDLE;
END IF;
WHEN FIRST_READ =>
-- fills the first half of c_v and generates output from it.
v.out_ready := '1';
v.c_v(c_v_w-1 DOWNTO 0) := (OTHERS => '0');
v.c_v(g_in_data_w-1 DOWNTO 0) := q_reg.delay_data(g_in_data_w-1 DOWNTO 0);
v.dd_fresh := '0';
v.out_sosi.data(g_out_data_w-1 DOWNTO 0) := v.c_v(g_out_data_w+v.a_of-1 DOWNTO v.a_of);
v.out_sosi.valid := '1';
v.out_sosi.bsn(c_dp_stream_bsn_w-1 DOWNTO 0) := in_bsn(c_dp_stream_bsn_w-1 DOWNTO 0);
v.out_sosi.sop := '1';
v.out_sosi.eop := '0';
v.bsn_cnt := 0;
v.op_data_cnt := q_reg.op_data_cnt+1;
IF in_sosi.valid = '1' THEN
v.delay_data(g_in_data_w-1 DOWNTO 0) := in_sosi.data(g_in_data_w-1 DOWNTO 0);
v.dd_fresh := '1';
END IF;
IF out_siso.ready = '1' THEN
v.state := READING;
ELSE
v.state := IDLE;
END IF;
WHEN BSN =>
-- generating output data from c_v but past the halfway point of c_v so there needs to be new data added also increases the bsn output
v.out_sosi.valid := '0';
IF q_reg.dd_fresh = '1' AND q_reg.valid_data = '1' THEN
-- generate output from the middle of c_v
v.out_sosi.data(g_out_data_w-1 DOWNTO 0) := q_reg.c_v((g_out_data_w*(q_reg.op_data_cnt+1))+q_reg.a_of-1 DOWNTO (g_out_data_w*q_reg.op_data_cnt)+q_reg.a_of);
v.out_sosi.valid := '1';
v.bsn_cnt := q_reg.bsn_cnt+1;
-- put the second half of c_v into the first half of c_v
v.c_v(g_in_data_w-1 DOWNTO 0) := q_reg.c_v(c_v_w-1 DOWNTO g_in_data_w);
-- put the delay data into the first half of c_v
v.c_v(c_v_w-1 DOWNTO g_in_data_w) := q_reg.delay_data(g_in_data_w-1 DOWNTO 0);
v.dd_fresh := '0';
v.op_data_cnt := 0;
ELSIF q_reg.dd_fresh = '0' AND q_reg.valid_data = '1' THEN
-- generate output from the middle of c_v
v.out_sosi.data(g_out_data_w-1 DOWNTO 0) := q_reg.c_v((g_out_data_w*(q_reg.op_data_cnt+1))+q_reg.a_of-1 DOWNTO (g_out_data_w*q_reg.op_data_cnt)+q_reg.a_of);
v.out_sosi.valid := '1';
v.bsn_cnt := q_reg.bsn_cnt+1;
-- put the second half of c_v into the first half of c_v
v.c_v(g_in_data_w-1 DOWNTO 0) := q_reg.c_v(c_v_w-1 DOWNTO g_in_data_w);
-- put zeros into the second half of c_v beceause dd_fresh is '0'
v.c_v(c_v_w-1 DOWNTO g_in_data_w) := (OTHERS => '0');
v.op_data_cnt := 0;
v.valid_data := '0';
ELSIF q_reg.dd_fresh = '1' AND q_reg.valid_data = '0' THEN
-- put the delay data into the second half of c_v beceause these are now zeros
v.c_v(c_v_w-1 DOWNTO g_in_data_w) := q_reg.delay_data(g_in_data_w-1 DOWNTO 0);
-- generate output from the middle of c_v
v.out_sosi.data(g_out_data_w-1 DOWNTO 0) := v.c_v((g_out_data_w*(q_reg.op_data_cnt+1))+q_reg.a_of-1 DOWNTO (g_out_data_w*q_reg.op_data_cnt)+q_reg.a_of);
-- put the second half of c_v into the first half of c_v
v.c_v(g_in_data_w-1 DOWNTO 0) := q_reg.c_v(c_v_w-1 DOWNTO g_in_data_w);
v.out_sosi.valid := '1';
v.bsn_cnt := q_reg.bsn_cnt+1;
v.dd_fresh := '0';
v.op_data_cnt := 0;
ELSIF q_reg.dd_fresh = '0' AND q_reg.valid_data = '0' AND (g_out_data_w*(v.op_data_cnt+1))+q_reg.a_of < g_in_data_w THEN
-- generate output from the middle of c_v
v.out_sosi.data(g_out_data_w-1 DOWNTO 0) := q_reg.c_v((g_out_data_w*(q_reg.op_data_cnt+1))+q_reg.a_of-1 DOWNTO (g_out_data_w*q_reg.op_data_cnt)+q_reg.a_of);
v.out_sosi.valid := '1';
v.bsn_cnt := q_reg.bsn_cnt+1;
END IF;
v.out_ready := '0';
v.out_sosi.eop := '1';
v.a_of := 0;
v.bsn_cnt := q_reg.bsn_cnt+1;
IF v.dd_fresh = '1' AND v.valid_data = '0' THEN
-- put the delay data into the second half of c_v beceause these are now zeros
v.c_v(c_v_w-1 DOWNTO g_in_data_w) := q_reg.delay_data(g_in_data_w-1 DOWNTO 0);
v.dd_fresh := '0';
v.valid_data := '1';
END IF;
IF in_sosi.valid = '1' THEN
v.delay_data(g_in_data_w-1 DOWNTO 0) := in_sosi.data(g_in_data_w-1 DOWNTO 0);
v.dd_fresh := '1';
END IF;
IF g_bim+TO_UINT(in_bsn)-1 = TO_UINT(v.out_sosi.bsn) THEN
v.state := RESET;
ELSIF out_siso.ready = '1' THEN
v.state := READING;
ELSE
v.state := IDLE;
END IF;
WHEN RESET =>
v := c_t_reg_init;
IF in_sosi.valid = '1' THEN
v.delay_data(g_in_data_w-1 DOWNTO 0) := in_sosi.data(g_in_data_w-1 DOWNTO 0);
v.dd_fresh := '1';
END IF;
v.state := OFF;
WHEN IDLE =>
-- the statemachine goes to Idle when its finished or when its waiting on other components.
IF q_reg.dd_fresh = '0' THEN
v.out_ready := '1';
ELSE
v.out_ready := '0';
END IF;
v.out_sosi.valid := '0';
IF q_reg.dd_fresh = '1' AND q_reg.valid_data = '0' THEN
-- put the delay data into the second half of c_v beceause these are now zeros
v.c_v(c_v_w-1 DOWNTO g_in_data_w) := q_reg.delay_data(g_in_data_w-1 DOWNTO 0);
v.dd_fresh := '0';
v.valid_data := '1';
END IF;
IF in_sosi.valid = '1' THEN
v.delay_data(g_in_data_w-1 DOWNTO 0) := in_sosi.data(g_in_data_w-1 DOWNTO 0);
v.dd_fresh := '1';
END IF;
IF q_reg.bsn_cnt = g_block_size-3 AND out_siso.ready = '0' THEN
v.state := IDLE;
ELSIF q_reg.bsn_cnt = g_block_size-3 THEN
v.state := BSN;
ELSIF (g_out_data_w*(v.op_data_cnt+1))+q_reg.a_of >= g_in_data_w AND v.dd_fresh = '1' AND out_siso.ready = '1' AND out_siso.ready = '1' THEN
v.state := OVER_HALF;
ELSIF (g_out_data_w*(v.op_data_cnt+1))+q_reg.a_of < g_in_data_w AND out_siso.ready = '1' THEN
v.state := READING;
ELSE
v.state := IDLE;
END IF;
WHEN OFF =>
-- the stamachine has a state off so it knows when to go to first read, it can't go to first read from IDLE
v.out_sosi := c_dp_sosi_init;
v.bsn_cnt := 0;
v.state_off := '1';
IF in_sosi.valid = '1' AND q_reg.dd_fresh = '0' THEN
v.delay_data(g_in_data_w-1 DOWNTO 0) := in_sosi.data(g_in_data_w-1 DOWNTO 0);
v.dd_fresh := '1';
v.out_ready := '0';
v.state_off := '0';
v.state := FIRST_READ;
ELSIF q_reg.dd_fresh = '1' THEN
v.out_ready := '0';
v.state := OFF;
ELSIF out_siso.ready = '1' THEN
v.out_ready := '1';
v.state := OFF;
ELSE
v.state := OFF;
END IF;
END CASE;
ELSE
IF q_reg.dd_fresh = '0' THEN
v.out_ready := '1';
ELSE
v.out_ready := '0';
END IF;
v.out_sosi.valid := '0';
IF q_reg.dd_fresh = '1' AND q_reg.valid_data = '0' THEN
-- put the delay data into the second half of c_v beceause these are now zeros
v.c_v(c_v_w-1 DOWNTO g_in_data_w) := q_reg.delay_data(g_in_data_w-1 DOWNTO 0);
v.dd_fresh := '0';
v.valid_data := '1';
END IF;
IF in_sosi.valid = '1' THEN
v.delay_data(g_in_data_w-1 DOWNTO 0) := in_sosi.data(g_in_data_w-1 DOWNTO 0);
v.dd_fresh := '1';
END IF;
IF q_reg.bsn_cnt = g_block_size-3 AND out_siso.ready = '0' THEN
v.state := IDLE;
ELSIF q_reg.bsn_cnt = g_block_size-3 THEN
v.state := BSN;
ELSIF (g_out_data_w*(v.op_data_cnt+1))+q_reg.a_of >= g_in_data_w AND v.dd_fresh = '1' AND out_siso.ready = '1' AND out_siso.ready = '1' THEN
v.state := OVER_HALF;
ELSIF (g_out_data_w*(v.op_data_cnt+1))+q_reg.a_of < g_in_data_w AND out_siso.ready = '1' THEN
v.state := READING;
ELSE
v.state := IDLE;
END IF;
END IF;
IF rst = '1' THEN
v.state := RESET;
END IF;
d_reg <= v;
END PROCESS;
-- fill outputs
out_sosi <= q_reg.out_sosi;
out_ready <= q_reg.out_ready;
state_off <= q_reg.state_off;
END rtl;