------------------------------------------------------------------------------- -- -- 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;