Record Reflection Use Case - To Std_Logic_Vector
So one of the things I frequently have to do is transform record types to and from std_logic_vector. Vendor-provided IP tools (such as dual-clock FIFOs and IP system generators) tend to not work with anything smarter. So I've got a library of functions I use called pack and unpack for all manner of datatypes, the prototypes of which always look like:
procedure pack(target : inout std_logic_vector; idx : inout integer; nd : in std_logic_vector);
procedure pack(target : inout std_logic_vector; idx : inout integer; nd : in std_ulogic);
procedure unpack(source : in std_logic_vector; idx : inout integer; dat : out std_logic_vector);
procedure unpack(source : in std_logic_vector; idx : inout integer; dat : out std_ulogic);
And then the usage model is:
type t_wide_response is record
data : std_logic_vector(63 downto 0);
resp : unsigned(15 downto 0); -- Response time, 8 ns LSB
af : std_logic; -- VME cycle failed arbitration
dtack : std_logic; -- VME cycle completed with DTACK
berr : std_logic; -- VME cycle completed with BERR
retry : std_logic; -- VME cycle completed with RETRY
bto : std_logic; -- VME cycle completed with bus timeout
seq : t_sequence_id; -- Sequence ID from the request.
end record;
subtype t_wide_response_slv is std_logic_vector(89 downto 0);
function TO_SLV(rec : t_wide_response) return t_wide_response_slv is
variable slv : t_wide_response_slv := (others => 'U');
variable idx : integer := 0;
begin
pack(slv, idx, rec.seq);
pack(slv, idx, rec.data);
pack(slv, idx, rec.resp);
pack(slv, idx, rec.af);
pack(slv, idx, rec.dtack);
pack(slv, idx, rec.berr);
pack(slv, idx, rec.retry);
pack(slv, idx, rec.bto);
return slv;
end function TO_SLV;
function TO_RESPONSE(slv : t_wide_response_slv) return t_wide_response is
variable idx : integer := 0;
variable rec : t_wide_response;
begin
unpack(slv, idx, rec.seq);
unpack(slv, idx, rec.data);
unpack(slv, idx, rec.resp);
unpack(slv, idx, rec.af);
unpack(slv, idx, rec.dtack);
unpack(slv, idx, rec.berr);
unpack(slv, idx, rec.retry);
unpack(slv, idx, rec.bto);
return rec;
end function TO_RESPONSE;
As you can imagine, this doesn't become in the slightest bit repetitive in projects with lots of various types being passed around.
Ideally, it seems like all this horribleness could be replaced by a generic TO_SLV/TO_RECORD pair, each of which enumerates over the record type and calls the appropriate pack/unpack functions for each contained type. Practically, I'm not sure that's doable without the concept of dynamic types. We may be able to convince the simulation vendors to take up that one. But I can't see even the remotest chance of takeup for synthesis, since I found yesterday that the synthesis tool I'm working around has VHDL-2008 support for neither A) the ?? operator (implicit or explicit) or B) std_ulogic_vector.
To elaborate on why I think this may just not be feasible, let's keep the same example and have the compiler flesh it out at analysis time.
package gen_recslv is generic (type t_record; constant slv_len : positive) is
subtype t_slv is std_logic_vector(slv_len-1 downto 0);
...
package wide_responses is new work.gen_recslv generic map(t_record => t_wide_response; slv_len => 90);
gives us
function TO_RECORD(slv : t_slv) return t_record is [wide_responses.t_slv return wide_responses.t_record]
variable idx : integer := 0;
variable rec : t_record;
begin
standard_functions.unpack(slv, idx, rec.seq)[std_logic_vector, integer, unsigned];
standard_functions.unpack(slv, idx, rec.data)[std_logic_vector, integer, std_logic_vector];
standard_functions.unpack(slv, idx, rec.resp)[std_logic_vector, integer, unsigned];
standard_functions.unpack(slv, idx, rec.af)[std_logic_vector, integer, std_logic];
standard_functions.unpack(slv, idx, rec.dtack)[std_logic_vector, integer, std_logic];
standard_functions.unpack(slv, idx, rec.berr)[std_logic_vector, integer, std_logic];
standard_functions.unpack(slv, idx, rec.retry)[std_logic_vector, integer, std_logic];
standard_functions.unpack(slv, idx, rec.bto)[std_logic_vector, integer, std_logic];
return rec;
end function TO_RESPONSE;
All well and good, but what happens when we try to nest this record into another, a t_verywide_response, and instantiate a verywide_responses package? The specific verywide_responses.TO_RECORD can enumerate the fields, but then it hits a field which is a t_wide_response. The appropriate TO_RECORD is wide_responses.TO_RECORD, but verywide_responses has no way of knowing that.
--
Rob Gaddi - 2016-03-23
Comments