开发者

"Serialize" VHDL record

开发者 https://www.devze.com 2023-01-21 12:08 出处:网络
Suppose I have the following type definition which relies on constants to indicate vector length of the record members:

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.


Can't you just write this:

d <= pnt.x & pnt.y;


While there is currently no official automated way of converting records to vectors (and vice-versa), it is a very common requirement. You have 2 options:

  • Define your own to_vector and from_vector functions for every record type (tedious)
  • Autogenerate packages containing above type conversions (difficult / error prone)

Tedious and repetitive tasks that motivate you to write a script to generate code are an indication of a deficiency in the language itself. You can change this by taking an active role in the IEEE working-group and influence the next version of the VHDL standard.


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)));


Taken from discussion here

One reasonable way to do this, with large records is to define the ranges ahead of time like this:

type t_SPI_DATA_PORT is record
  Data_Size_Minus1: std_ulogic_vector(31 downto 28);
  Done: std_ulogic_vector(27 downto 27);
  Rx_Wait_Timeout: std_ulogic_vector(26 downto 26);
  Rx_Wait_On_Miso: std_ulogic_vector(25 downto 25);
  Sclk_Select: std_ulogic_vector(24 downto 24);
  Reserved: std_ulogic_vector(23 downto 20);
  Hold_Cs: std_ulogic_vector(19 downto 19);
  Cpha: std_ulogic_vector(18 downto 17);
  Cpol: std_ulogic_vector(16 downto 16);
  Data: std_ulogic_vector(15 downto 0);
end record;

Then the conversion functions look like this:

function To_Std_ULogic_Vector(L : t_SPI_DATA_PORT) return
  std_ulogic_vector is
    variable RetVal: std_ulogic_vector(31 downto 0);
  begin

    RetVal := (others => '0');
    RetVal(L.Data_Size_Minus1'range) := L.Data_Size_Minus1;
    RetVal(L.Done'range) := L.Done;
    RetVal(L.Rx_Wait_Timeout'range) := L.Rx_Wait_Timeout;
    RetVal(L.Sclk_Select'range) := L.Sclk_Select;
    RetVal(L.Reserved'range) := L.Reserved;
    RetVal(L.Rx_Wait_On_Miso'range) := L.Rx_Wait_On_Miso;
    RetVal(L.Hold_Cs'range) := L.Hold_Cs;
    RetVal(L.Cpha'range) := L.Cpha;
    RetVal(L.Cpol'range) := L.Cpol;
    RetVal(L.Data'range) := L.Data;

    return(RetVal);
end To_Std_ULogic_Vector;

function From_Std_ULogic_Vector(L : std_ulogic_vector) return
  t_SPI_DATA_PORT is
    variable RetVal: t_SPI_DATA_PORT;
    variable Lx: std_ulogic_vector(L'length - 1 downto 0);
  begin
    Lx := L;    
    RetVal.Data_Size_Minus1 := Lx(RetVal.Data_Size_Minus1'range);
    RetVal.Done := Lx(RetVal.Done'range);
    RetVal.Rx_Wait_Timeout := Lx(RetVal.Rx_Wait_Timeout'range);
    RetVal.Sclk_Select := Lx(RetVal.Sclk_Select'range);
    RetVal.Reserved := Lx(RetVal.Reserved'range);
    RetVal.Rx_Wait_On_Miso := Lx(RetVal.Rx_Wait_On_Miso'range);
    RetVal.Hold_Cs := Lx(RetVal.Hold_Cs'range);
    RetVal.Cpha := Lx(RetVal.Cpha'range);
    RetVal.Cpol := Lx(RetVal.Cpol'range);
    RetVal.Data := Lx(RetVal.Data'range);

    return(RetVal);
end From_Std_ULogic_Vector;


I have prepared a script which automatically generates the VHDL package for conversions between the user defined record type and the std_logic_vector type.

The sources of this script are published as PUBLIC DOMAIN in the alt.sources group. You can see http://groups.google.com/group/alt.sources/browse_frm/thread/53ea61208013e9d1 or look for topic "Script to generate VHDL package for conversion between the record type and std_logic_vector" If you want to unpack the archive from the Google archive, remember to select "show original" option. Otherwise the indendation of the Python source will be damaged.


I did another try at record serialization. I believe my method is a bit more robust. You still need to create function for every record type but you do not need to mention ranges.

Lets say you need to serialize type_a using my package:

type type_a is record
  a : std_logic_vector(15 downto 0);
  b : std_logic_vector(31 downto 0);
  c : std_logic_vector(7 downto 0);
  d : std_logic_vector(7 downto 0);
end record type_a;

constant type_a_width : integer := 64;

Define two functions like this:

use work.SERIALIZE_PKG.all;

function serialize (
     input : type_a)
  return std_logic_vector is
  variable ser : serializer_t(type_a_width-1 downto 0);
  variable r   : std_logic_vector(type_a_width-1 downto 0);
begin  -- function serialize_detectorCacheLine_t
  serialize_init(ser);
  serialize(ser, input.a);
  serialize(ser, input.b);
  serialize(ser, input.c);
  serialize(ser, input.d);
  r := serialize_get(ser);
  return r;
end function serialize;


function deserialize (
     input : std_logic_vector)
  return type_a is
  variable ser : serializer_t(type_a_width-1 downto 0);
  variable r   : type_a;
begin  -- function serialize_detectorCacheLine_t
  ser := serialize_set(input);
  deserialize(ser, r.a);
  deserialize(ser, r.b);
  deserialize(ser, r.c);
  deserialize(ser, r.d);
  return r;
end function deserialize;

And then you can use it in code like this:

signal type_a_ser       : std_logic_vector(type_a_width-1 downto 0)       := (others => '0');
signal type_a_in        : type_a;
signal type_a_out       : type_a;
....
type_a_ser       <= serialize(type_a_in);
type_a_out       <= deserialize(type_a_ser);

I posted my code and example of more complex types (nested records etc) at: https://github.com/gitmodimo/vhdl-serialize

0

精彩评论

暂无评论...
验证码 换一张
取 消

关注公众号