Search code examples
vhdl

Is there a way to define a range type based on the range of an unconstrained vector declared in the entity's port?


I'm am trying to implement a generic deserializer in VHDL-2008. (Specifically, I'm aiming for something that can be synthesized by Vivado in VHDL-2008 mode).

I have included my current code below. The entity port declaration specifies an unconstrained std_logic_vector DATA_OUT, for the deserialized output word.

The problem is that, in this implementation, I need to specify a CounterType as follows if I want to be able to process 32-bit words:

type CounterType is range 0 to 31;

I haven't been able to figure out a way to write a definition of CounterType from the size of the DATA_OUT port in a way that is valid VHDL, let alone a way that is acceptable to Vivado's compiler.

Is there a way to do this? (I.e., define a range type where the range corresponds to the range of an unconstrained actual parameter?)

If not, what are my options to keep this deserializer implementation as generic as possible, i.e., to be able to instantiate it for different output word sizes?

(Note: I'd prefer a way that does not add a generic to the entity interface, since this seems redundant with the specification of DATA_OUT's range at instantiation time. But if it can't be done otherwise, I'd be interested in those kinds of solutions as well.)

library ieee;
use ieee.std_logic_1164.all;

entity deserializer is

    -- The deserializer accepts its single input bit DATA_IN whenever (DATA_IN_VALID and DATA_IN_READY) = '1'.
    -- The deserializer drops its output word DATA_OUT and proceeds whenever (DATA_OUT_VALID and DATA_OUT_READY) = '1'.

    port (
        CLK            : in  std_logic;
        RESET          : in  std_logic;
        DATA_IN        : in  std_logic;
        DATA_IN_VALID  : in  std_logic;
        DATA_IN_READY  : out std_logic;
        DATA_OUT       : out std_logic_vector;
        DATA_OUT_VALID : out std_logic;
        DATA_OUT_READY : in  std_logic
    );

end entity deserializer;


architecture arch of deserializer is

-- This implementation is designed to have no wait states: if a continuous stream of input bits is offered,
-- and the deserializer can offload its output words unimpeded, DATA_IN_READY will always be true, i.e.,
-- we'll never throttle our input; we'll process 1 bit per clock cycle.

type CounterType is range 0 to 31; -- should correspond to the index range of DATA_OUT.

type StateType is record
    -- Internal state.
    counter     : CounterType;
    data_in_bit : std_logic; -- Used to store an input bit while waiting to offload the output word in state (counter == 0).
    -- Output registers.
    data_in_ready  : std_logic;
    data_out       : std_logic_vector(DATA_OUT'range);
    data_out_valid : std_logic;
end record StateType;

constant reset_state : StateType := (
    counter        => 0,
    data_in_bit    => '-',
    data_in_ready  => '1',
    data_out       => (others => '-'),
    data_out_valid => '0'
  );

signal current_state : StateType := reset_state;
signal next_state    : StateType;

begin

    combinatorial: process (all) is
    variable state: StateType;
    begin
        -- Calculate next state based on current state and inputs.
        if RESET = '1' then
            -- Handle synchronous reset.
            state := reset_state;
        else
            -- Start from current state.
            state := current_state;

            if state.counter = 0 then
                -- Note: we may have a pending output, waiting to be accepted.
                if state.data_out_valid = '1' and DATA_OUT_READY = '1' then
                    state.data_out := (others => '-');
                    state.data_out_valid := '0';
                end if;

                if state.data_in_ready = '1' and DATA_IN_VALID = '1' then
                    state.data_in_bit := DATA_IN;
                    state.data_in_ready := '0';
                end if;

                if state.data_out_valid = '0' and state.data_in_ready = '0' then
                    state.data_out(state.data_out'right) := state.data_in_bit;
                    state.data_in_bit := '-';
                    state.counter := state.counter + 1;
                    state.data_in_ready := '1';
                end if;
            else
                if state.data_in_ready = '1'and DATA_IN_VALID = '1' then
                    state.data_out := state.data_out sll 1;
                    state.data_out(state.data_out'right) := DATA_IN;
                    if state.counter = CounterType'high then
                        state.data_out_valid := '1';
                        state.counter := 0;
                    else
                        state.counter := state.counter + 1;
                    end if;
                end if; 
            end if;

        end if;

        -- Schedule next state for update at next rising clock edge.
        next_state <= state;

        -- Drive entity outputs from current state.
        DATA_IN_READY  <= current_state.data_in_ready;
        DATA_OUT       <= current_state.data_out;
        DATA_OUT_VALID <= current_state.data_out_valid;

    end process combinatorial;

    sequential: process (CLK) is
    begin
        if rising_edge(CLK) then
            current_state <= next_state;
        end if;
    end process sequential;

end architecture arch;

Solution

  • CounterType can be sized by DATA_OUT'low to DATA_OUT'high. Instead of declaring a complete new integer type, that is incompatible to the predefined integer type called ìnteger`, you should declare a subtype as follows:

    subtype CounterType is integer range DATA_OUT'low to DATA_OUT'high;
    

    If the tools is fully VHDL-2008 compliant, it should also accept this:

    subtype CounterType is integer range DATA_OUT'range;