3

Suppose I have defined a record with fields of std_ulogic_vector to represent a larger std_ulogic_vector. It's straightforward to convert this record to the large vector using a concatenation (without knowledge of the size for each field).

How do I do the reverse, e.g. convert the large std_ulogic_vector back to the record ?

Example :

architecture RTL of record_conversion is
  type data_t is record
    top    : std_ulogic_vector(4 downto 0);
    bottom : std_ulogic_vector(2 downto 0);
  end record data_t;

  signal record_s : data_t;
  signal vector_s : std_ulogic_vector(7 downto 0);
begin

  -- vector to record works
  --vector_s <= record_s.top & record_s.bottom;

  -- record to vector does not work
  (record_s.top, record_s.bottom) <= vector_s;

  -- tedious solution with knowledge of the field size
  record_s.top    <= vector_s(7 downto 3);
  record_s.bottom <= vector_s(2 downto 0);

end architecture;

2 Answers2

5

It is usually a good idea to wrap such conversion in functions, and with a subtype for resulting vector, like:

...
  constant LEN : integer := 8;  -- Number of bits in data_t

  type data_t is record
    top    : std_ulogic_vector(4 downto 0);
    bottom : std_ulogic_vector(2 downto 0);
  end record data_t;

  subtype vector_t is std_ulogic_vector(LEN - 1 downto 0);

  function data_to_vector(data : data_t) return std_ulogic_vector is
    variable res_v : vector_t;
  begin
    res_v := data.top & data.bottom;
    return res_v;
  end function;

  function vector_to_data(vector : vector_t) return data_t is
    variable res_v : data_t;
  begin
    res_v.top    := vector(LEN - 1 downto LEN - res_v.top'length);
    res_v.bottom := vector(res_v.bottom'length - 1 downto 0);
    return res_v;
  end function;

  signal record_s : data_t;
  signal vector_s : vector_t;

begin

  record_s <= vector_to_data(vector_s);
  vector_s <= data_to_vector(record_s);
...

Maybe another constant should be added to define the split between top and bottom.

Morten Zilmer
  • 15,586
  • 3
  • 30
  • 49
2

There are a couple of other methods besides using subprogram calls to assign elements of one type to elements of another type.

You can use a qualified expression:

  record_s <= data_t'(vector_s(7 downto 3), vector_s (2 downto 0));

Where the aggregate comprised of two slices of vector_s with an explicit type matching the record. See IEEE Std 1076-2008 9.3.6 Qualified expressions.

During simulation new values for signals are validated. See 14.7.3.4 Signal update:

b) If S is a composite signal (including a slice of an array), the effective value of S is implicitly converted to the subtype of S. The subtype conversion checks that for each element of S there is a matching element in the effective value and vice versa. An error occurs if this check fails. The result of this subtype conversion is then assigned to the variable representing the current value of S.

Besides having a matching element (subelement,...) subtype conversion changes the index ranges to match the target.

You can specify the slice index ranges with subtype index ranges:

library ieee;
use ieee.std_logic_1164.all;

entity record_conversion is
end entity;

architecture subtypes of record_conversion is
  type data_t is record
    top    : std_ulogic_vector(4 downto 0);
    bottom : std_ulogic_vector(2 downto 0);
  end record data_t;

  signal record_s : data_t;
  signal vector_s : std_ulogic_vector(7 downto 0);
  subtype t is std_logic_vector (
        vector_s'LEFT downto vector_s'LEFT - record_s.top'length + 1
  );
  subtype b is std_logic_vector (
        vector_s'LEFT - record_s.top'length downto 0
  );
begin

  record_s <= data_t'(vector_s(t'range), vector_s(b'range));

end architecture;

Here the subtypes index range slices of the right hand side expression elements.

You can describe the slices with aliases:

architecture aliases of record_conversion is
    type data_t is record
        top:     std_ulogic_vector(4 downto 0);
        bottom:  std_ulogic_vector(2 downto 0);
    end record data_t;

    signal record_s:  data_t;
    signal vector_s:  std_ulogic_vector(7 downto 0);
    alias vector_s_top: std_ulogic_vector(record_s.top'range) is
        vector_s(7 downto 3);
    alias vector_s_bottom: std_ulogic_vector(record_s.bottom'range) is
        vector_s (2 downto 0);

begin
    record_s <= data_t'(vector_s_top, vector_s_bottom);
end architecture;

Here the two aliases describe fields of vector_s. If you were guaranteed to always assign the record composite object you could actually do away with records and simply use aliases. The closest VHDL comes to unions.

The above examples analyze, elaborate and simulate without error, demonstrating there are no slice boundary issues.

Qualified expressions, subtype declarations and aliases incur no additional simulation overhead while subprogram calls do.

  • Would this technique work if there was for example an enumerated type, or other record type within the record being converted? – scary_jeff Jan 29 '19 at 16:50
  • There is no actual conversion here. A qualified expression states the type of an expression. Each declaration is unique, including elements of a record. An enumerated element would require type conversion. A record type being converted would require selected names and compatible elements or conversion of incompatible elements. –  Jan 29 '19 at 18:18