Skip to content
Snippets Groups Projects
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;