"Serialize" VHDL record
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_vector
s 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_vector
s 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
精彩评论