views:

36

answers:

2

Suppose I have the following type definition which relies on constants to indicate vector length of the record members:

type point_t is record
    x: std_logic_vector(X_WIDTH-1 downto 0);
    y: std_logic_vector(Y_WIDTH-1 downto 0); 
end record;

I would like to convert these kind of records into std_logic_vectors to put them into, say, a FIFO. Currently I am using the following code:

PROCEDURE encodepoint(signal pnt: in point_t;
                      signal d: out std_logic_vector(POINT_ENC_WIDTH-1 downto 0)) is
    variable top: integer := 0;
begin
    top := X_WIDTH-1;
    d(top downto 0) <= pnt.x;

    top := top + Y_WIDTH;
    d(top downto top-X_WIDTH+1) <= sl.y;

    d(d'left downto top+1) <= (others => '0');
end;

This code is suboptimal in many ways. For example it requires me to always correctly set POINT_ENC_WIDTH to a value that is big enough to allow d to hold the whole serialized record. It relies on the programmer to do very mechanical work. For example for every member of the record, say x, X_WIDTH appears twice in the code, once in direct connection with x and once in connection with the next member, y. This get tedious quickly. If I change the definition of the record by adding additional fields, I have to update both the serializing and the (very similar) deserializing code, and I may just forget this. When I remove fields, at least the compiler complains.

Thus this leads me to my question: Is there a simple, automated or at least quasi-automated way to convert VHDL records into std_logic_vectors without having to resort to manually written serializing/unserializing code? It is not important for me to know the specific encoding, as I am using the records internally and the final output format is clearly specified and will be implemented manually.

+3  A: 

Can't you just write this:

d <= pnt.x & pnt.y;
Philippe
Oh, good point, thanks! Sometimes I wonder how much I am able to overlook! Is the inverse operation also writable in a similarly concise way?
distributed
A: 

I typically define conversion functions in a package along with the record.

In your case, something like:

function point2slv (pnt : point_t) return std_logic_vector is
    variable slv : std_logic_vector(X_WIDTH + Y_WIDTH - 1 downto 0);
begin
    slv :=  pnt.x & pnt.y;
    return slv;
end;

function slv2point (slv : std_logic_vector) return point_t is
    variable pnt : point_t;
begin
    pnt.x     := slv(X_WIDTH + Y_WIDTH - 1 downto Y_WIDTH);
    pnt.y     := slv(Y_WIDTH - 1 downto  0);
    return pnt;
end;

NOTE: Depending on what you're trying to do, you may wish to use pre-defined sizes on one side or the other, and conversion functions to pad/clip to natural lengths (ie: perhaps fit the X and Y values into 16 or 32 bit values). The unsigned type and resize function work well for this:

slv(31 downto 16):=  std_logic_vector(resize(unsigned(pnt.x,16)));
slv(15 downto  0):=  std_logic_vector(resize(unsigned(pnt.7,16)));
Charles Steinkuehler
Hum, this is exactly the situation I have now. I try to keep the encoding/decoding functions very close to the type definitions. Your encoding/decoding code is written a little differently, with explicit indices, though the implications are the same. The more members you have in your record, the longer your lines get. For records with, say, 10 members, this becomes unwieldy. If you add or delete members, you have to update your code and be very wary that you do so correctly.
distributed
For large records with longer members, I typically explicitly define the bit regions instead of concatenating the SLVs, ie:slv(31 downto 16) := pnt.x;slv(15 downto 0) := pnt.y;This is where using the resize functions can come in very handy, if the length of X and Y are not fixed:slv(31 downto 16) := std_logic_vector(resize(unsigned(pnt.x,16));
Charles Steinkuehler