VHDL - init std_logic_vector array from HEX file - file-io

I have simple "RAM" implemented as:
type memory_array is array(31 downto 0) of std_logic_vector(7 downto 0);
signal ram : memory_array;
I would like to init it's content from HEX file. I wonder about reading the file like:
ram_init: process
file file_ptr : text;
variable line_text : string(1 to 14);
variable line_num : line;
variable lines_read : integer := 0;
variable char : character;
variable tmp_hexnum : string(1 to 2);
begin
file_open(file_ptr,"../RAM.HEX",READ_MODE);
while (not endfile(file_ptr)) loop
readline (file_ptr,line_num);
READ (line_num,line_text);
if (lines_read < 32) then
tmp_hexnum := line_text(10 to 11);
-- ram(lines_read) <= tmp_hexnum;
lines_read := lines_read + 1;
wait for 10 ns;
end if;
end loop;
file_close(file_ptr);
wait;
end process;
The problem is (if this code above would works, which I don't even know), how to convert the tmp_hexnum string to std_logic_vector.
Please have patience with me, VHDL beginner.

The first mistake is to use a process : if you attempt to synthesise the design, the process won't do anything until the design is built and running; which is far too late to read a file!
Instead, wrap the init code in a function, and use that to initialise the memory
signal ram : memory_array := my_ram_init(filename => "../RAM.HEX");
This will work in simulation, and many synthesis tools will infer a RAM and initialise it correctly. If you declared a constant instead of a signal, this would create a ROM instead of a RAM.
Anyway the function looks a bit like
function my_ram_init(filename : string) return memory_array is
variable temp : memory_array;
-- other variables
begin
file_open(...);
-- you have a good handle on the function body
file_close(...);
return temp;
end function;
leaving you with the original problem :
temp(lines_read) <= to_slv(tmp_hexnum);
writing the to_slv function. There ought to be a standard library of these, but for some reason there isn't a universally accepted one. So, here's a start...
function to_slv (tmp_hexnum : string) return std_logic_vector is
variable temp : std_logic_vector(7 downto 0);
variable digit : natural;
begin
for i in tmp_hexnum'range loop
case tmp_hexnum(i) is
when '0' to '9' =>
digit := Character'pos(tmp_hexnum(i)) - Character'pos('0');
when 'A' to 'F' =>
digit := Character'pos(tmp_hexnum(i)) - Character'pos('A') + 10;
when 'a' to 'f' =>
digit := Character'pos(tmp_hexnum(i)) - Character'pos('a') + 10;
when others => digit := 0;
end case;
temp(i*4+3 downto i*4) := std_logic_vector(to_unsigned(digit));
end loop;
return temp;
end function;

Converting a string of variable length to std_logic_vector with length as 4 *
length of string, can be done with the function below:
library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
...
-- Convert string to std_logic_vector, assuming characters in '0' to '9',
-- 'A' to 'F', or 'a' to 'f'.
function str_to_slv(str : string) return std_logic_vector is
alias str_norm : string(1 to str'length) is str;
variable char_v : character;
variable val_of_char_v : natural;
variable res_v : std_logic_vector(4 * str'length - 1 downto 0);
begin
for str_norm_idx in str_norm'range loop
char_v := str_norm(str_norm_idx);
case char_v is
when '0' to '9' => val_of_char_v := character'pos(char_v) - character'pos('0');
when 'A' to 'F' => val_of_char_v := character'pos(char_v) - character'pos('A') + 10;
when 'a' to 'f' => val_of_char_v := character'pos(char_v) - character'pos('a') + 10;
when others => report "str_to_slv: Invalid characters for convert" severity ERROR;
end case;
res_v(res_v'left - 4 * str_norm_idx + 4 downto res_v'left - 4 * str_norm_idx + 1) :=
std_logic_vector(to_unsigned(val_of_char_v, 4));
end loop;
return res_v;
end function;

Your (both) answers helped me a lot. But it seems not working.
function ram_init(filename : string) return memory_array is
variable temp : memory_array;
file file_ptr : text;
variable line_line : line;
variable line_text : string(1 to 14);
variable tmp_hexnum : string(1 to 2);
variable lines_read : integer := 0;
begin
file_open(file_ptr,filename,READ_MODE);
while (lines_read < 32 and not endfile(file_ptr)) loop
readline (file_ptr,line_line);
read (line_line,line_text);
tmp_hexnum := line_text(10 to 11);
temp(lines_read) := hex_to_bin(tmp_hexnum);
lines_read := lines_read + 1;
end loop;
file_close(file_ptr);
return temp;
end function;
signal ram : memory_array := ram_init(filename=>"../RAM.HEX");
If I set tmp_hexnum to e.g. "0A", it's OK, but reading from file do not fill the RAM.
Can you please check the file part for me, too?

Related

csv file is not updating while saving data from TwinCat

I have created a plc program in TwinCat and its saving data into csv file. But variable values are not updating. Only one value is repeating in csv file again and and again. Where I am doing wrong !!! Here is my code:
PROGRAM MAIN
VAR
// Open, close and write function block
fbFileOpen: FB_FileOpen;
fbFileClose: FB_FileClose;
fbFileWrite: FB_FileWrite;
fbFormatString2: FB_FormatString2;
fbGetTime: NT_GetTime;
//file variables
nState : INT := 0;
nCounter : LREAL :=0;
hFile: UINT;
sPathName : T_MaxString;
sWriteBuffer : STRING(5000);
sBufferTemp : STRING(1000);
bBufferTemp : BOOL;
sFormat : STRING(255);
//General Variables
bFill: BOOL;
bWrite: BOOL;
rTimestamp : LREAL;
rCurrent: LREAL;
rActPos: LREAL;
nTimeMilli: INT;
i: ULINT;
END_VAR
// Input values
//------------------------------------------------------------------------------------------------------------------------
rTimestamp:= ULINT_TO_LREAL(F_GetSystemTime());
nCounter:= nCounter+1;
rCurrent:= (nCounter+1)/100;
rActPos:= (nCounter+1)/200;
IF bFill THEN
FOR i :=0 TO 10000000 BY 1 DO
GVL.arrLog[i].rTimestamp := rTimestamp;
GVL.arrLog[i].rCurrent := rCurrent;
GVL.arrLog[i].rActPos := rActPos;
END_FOR
END_IF
// Function Block for Current Date and Time
//------------------------------------------------------------------------------------------------------------------------
IF fbGetTime.START AND NOT fbGetTime.BUSY THEN // simple flip flop for quick update of time
fbGetTime.START := FALSE;
ELSE
fbGetTime.START := TRUE;
END_IF
fbGetTime(
NETID:= ,
START:= ,
TMOUT:= ,
BUSY=> ,
ERR=> ,
ERRID=> ,
TIMESTR=> ); // The TIMESTR is used to get times and dates
//Case Statements that will handle sequence of writing
//------------------------------------------------------------------------------------------------------------------------
CASE nState OF
0: //Wait for write trigger
IF bWrite THEN
nState := 10;
bWrite := FALSE;
END_IF
10: //Create file path and file using date
sPathName := CONCAT('D:\Data\', WORD_TO_STRING(fbGetTime.TIMESTR.wYear));
sPathName := CONCAT(sPathName,'_');
sPathName := CONCAT(sPathName,WORD_TO_STRING(fbGetTime.TIMESTR.wMonth));
sPathName := CONCAT(sPathName,'_');
sPathName := CONCAT(sPathName,WORD_TO_STRING(fbGetTime.TIMESTR.wDay));
sPathName := CONCAT(sPathName,'_');
sPathName := CONCAT(sPathName,WORD_TO_STRING(fbGetTime.TIMESTR.wHour));
sPathName := CONCAT(sPathName,'_');
sPathName := CONCAT(sPathName,WORD_TO_STRING(fbGetTime.TIMESTR.wMinute));
sPathName := CONCAT(sPathName,'_Datalog.csv');
nState:= 20;
fbFileOpen.bExecute := TRUE;
20: //Open and wait for file to open
fbFileOpen.bExecute := TRUE;
IF NOT fbFileOpen.bBusy AND NOT fbFileOpen.bError THEN
fbFileOpen.bExecute := FALSE;
nState := 30;
END_IF
30: // Write contents in file
sWriteBuffer := 'Name, fCurrentScaled, fActPos $n';
sFormat := '%F, %F, %F $n';
nTimeMilli := WORD_TO_INT(fbGetTime.TIMESTR.wMilliseconds);
IF WORD_TO_STRING(fbGetTime.TIMESTR.wMinute) <> INT_TO_STRING(40) THEN
FOR nTimeMilli:= 0 TO 999 BY 1 DO
fbFormatString2(
pFormatString:= ADR(sFormat),
arg1:= F_LREAL(GVL.arrLog[i].rTimestamp),
arg2:= F_LREAL(GVL.arrLog[i].rCurrent),
arg3:= F_LREAL(GVL.arrLog[i].rActPos),
pDstString:= ADR(sWriteBuffer),
nDstSize:= SIZEOF(sWriteBuffer),
bError=> ,
nErrId=> );
bBufferTemp := CONCAT2(pSrcString1 := ADR(sWriteBuffer),
pSrcString2 := ADR(sBufferTemp),
pDstString:= ADR(sWriteBuffer),
nDstSize := SIZEOF(sWriteBuffer));
END_FOR
ELSE
nState := 40;
fbFileWrite.bExecute := TRUE;
END_IF
40: // Write data in file and Wait for writing in the file
fbFileWrite.bExecute := TRUE;
IF NOT fbFileWrite.bBusy AND NOT fbFileWrite.bError THEN
fbFileWrite.bExecute := FALSE;
nState := 50;
fbFileClose.bExecute := TRUE;
END_IF
50: // close file and wait for it to close
fbFileClose.bExecute := TRUE;
IF NOT fbFileClose.bBusy AND NOT fbFileClose.bError THEN
fbFileClose.bExecute := FALSE;
nState:= 0;
END_IF
END_CASE
// FunctionBlocks for OPEN, WRITE and CLOSE
//------------------------------------------------------------------------------------------------------------------------
fbFileOpen(
sNetId:= '', //The netID does not need to be specified for local system
sPathName:= sPathName,
nMode:= FOPEN_MODEAPPEND OR FOPEN_MODEPLUS, // Open empty file for both read and write. If file exists then its content are destroyed
ePath:= PATH_GENERIC,
bExecute:= ,
tTimeout:= ,
bBusy=> ,
bError=> ,
nErrId=> ,
hFile=> hFile); // This file handle will be same for all function blocks.
fbFileClose(
sNetId:= '',
hFile:= hFile,
bExecute:= ,
tTimeout:= ,
bBusy=> ,
bError=> ,
nErrId=> );
fbFileWrite(
sNetId:= '',
hFile:= hFile,
pWriteBuff:= ADR(sWriteBuffer), // A pointer is used to get address
cbWriteLen:= SIZEOF(sWriteBuffer), // Needs to know to size of string going to be written
bExecute:= ,
tTimeout:= ,
bBusy=> ,
bError=> ,
nErrId=> ,
cbWrite=> );
I have created a counter and divided into smaller portion so that I get several values for one second. Is there update syntax I am missing?
The problem would appear to be in your step 30. It looks like you're trying to execute the file write during the 40th minute of the hour, and the rest of the time you're assembling the string to be written. But you have the line sWriteBuffer := 'Name, fCurrentScaled, fActPos $n'; at the beginning of step 30, outside of the conditional block, so it will execute every time.
When the 40th minute occurs and you move to the next step to do the file write, you're still executing that line which will overwrite whatever was put into sWriteBuffer by fbFormatString2. I think the assignment statements for sWriteBuffer and sFormat should be inside the IF statement.
Also, I don't know why you have the line nTimeMilli := WORD_TO_INT(fbGetTime.TIMESTR.wMilliseconds); when you're using nTimeMilli as the index variable in your FOR loop. Is that supposed to be i?
You don't show how bWrite is activated, which starts the state machine, but what if it happened to be triggered right on the 40th minute? Your step 30 would go immediately into the file write without assembling the values to be written.

How to sum 1+2^2+3^3+...n^n in pascal?

I want to sum that thing but only use the 'for loop' (no power functions). I've already created a loop that generates powers:
Program powers;
Var
i, n, result : integer;
writeln('enter N'), read(n);
BEGIN
Result := 1;
for i := 1 to n do
begin
Result := Result * n;
end;
writeln('result=',result);
END.
But I neither have any idea on how to make that code generate multiple powers ( this code only generates n^n) nor how to make a loop that sums them together.
It's best to break problems down into smaller problems. In this case, you may wish to have a pow function to handle the exponentiation for you.
Hopefully the pow function is acceptable if it's not a library function.
function pow(n, exp : integer) : integer;
var
i, result : integer;
begin
result := n;
for i := 2 to exp do
result := result * n;
pow := result;
end;
Then the main portion of your program is simpler.
program powers;
var
n, i, sum : integer;
function pow(n, exp : integer) : integer;
var
i, result : integer;
begin
result := n;
for i := 2 to exp do
result := result * n;
pow := result;
end;
begin
sum := 0;
write('enter N:');
readln(n);
for i := 0 to n do
sum := sum + pow(i, i);
writeln('result=', sum);
end.
Now that I finally understand what you're asking, please try this:
Program powers;
Var
i, j, n, p, result : integer;
BEGIN
write('enter N:');
readln(n);
Result := 0;
for i := 1 to n do
begin
p := 1;
for j := 1 to i do
p := p * i;
Result := Result + p;
end;
writeln('result=', result);
END.

I'm getting an syntax error in my VHDL code near counter

I'm trying to simulate a pulse width modulate (PMW) waveform generator and getting a syntax error in ISE. Checked fuse.xmsgs and found out it's near counter. Can someone point out the syntax error, please?
library IEEE;
use IEEE.STD_LOGIC_1164.all;
use IEEE.numeric_std.all;
entity pwm_gen is
port
(
clock, reset : in std_logic;
width : in std_logic_vector(7 downto 0);
pwm : out std_logic);
end pwm_gen;
architecture bhv of pwm_gen is
type counter is range 0 to 255;
counter count := 0;
begin
process (clock)
begin
if (reset = '1') then
count <= 0;
elsif (clock'event and clock = '1') then
if (count <= width) then
count <= count + 1;
pwm <= '1';
else
count <= count + 1;
pwm <= '0';
end if;
end if;
end process;
end bhv;
counter count := 0;
This is illegal syntax, as you didnt declare the object class (signal, constant, variable). You need to use the format:
signal count : counter := 0
This is also illegal, as you are comparing an integer to a std_logic_vector that you havent included a package for. You need to convert the slv to an unsigned
if (count <= unsigned(width)) then
And finally, reset is missing from the sensitivity list

Does HelpNDoc Pascal Script support structures?

I am trying to create a structure:
MyTopic
TopicID : String;
HelpID : Integer;
I wanted to create an array of these structures so I could sort them.
I have tried using this type / record syntax but it is failing.
Update
I defined this type and procedure:
type
TMyTopicRecord = record
idTopic : String;
idContextHelp : integer;
End;
procedure GetSortedTopicIDs(aTopics : array of String; size : Integer);
var
aMyTopicRecords : array of TMyTopicRecord;
temp : TMyTopicRecord;
iTopic, i, j : Integer;
begin
// Init the array
SetLength(aMyTopicRecords, size);
// Fill the array with the existing topid ids.
// Get the context ids at the same time.
for iTopic := 0 to size - 1 do
aMyTopicRecords[iTopic].idTopic := aTopics[iTopic];
aMyTopicRecords[iTopic].idContextHelp := HndTopics.GetTopicHelpContext(aTopics[iTopic]);
// Sort the array on context id
for i := size-1 DownTo 1 do
for j := 2 to i do
if (aMyTopicRecords[j-1].idContextHelp > aMyTopicRecords[j].idContextHelp) Then
begin
temp := aMyTopicRecords[j-1];
aMyTopicRecords[j-1] := aMyTopicRecords[j];
aMyTopicRecords[j] := temp;
end;
// Rebuild the original array of topic ids
for iTopic := 0 to size - 1 do
aTopics[iTopic] := aMyTopicRecords[iTopic].idTopic;
end;
The procedure gets called in a loop of the parent function (code snipped):
function GetKeywordsAsHtml(): string;
var
aKeywordList: THndKeywordsInfoArray;
aAssociatedTopics: array of string;
nBlocLevel, nDif, nClose, nCurKeywordLevel, nCurKeywordChildrenCnt: Integer;
nCurKeyword, nCurKeywordTopic: Integer;
nCountAssociatedTopics: Integer;
sCurrentKeyword, sKeywordLink, sKeywordRelated: string;
sKeywordJsCaption: string;
begin
Result := '<ul>';
nBlocLevel := 0;
try
aKeywordList := HndKeywords.GetKeywordList(False);
for nCurKeyword := 0 to length(aKeywordList) - 1 do
begin
sCurrentKeyword := aKeywordList[nCurKeyword].id;
nCurKeywordLevel := HndKeywords.GetKeywordLevel(sCurrentKeyword);
nCurKeywordChildrenCnt := HndKeywords.GetKeywordDirectChildrenCount(sCurrentKeyword);
sKeywordLink := '#';
sKeywordRelated := '[]';
aAssociatedTopics := HndTopicsKeywords.GetTopicsAssociatedWithKeyword(sCurrentKeyword);
nCountAssociatedTopics := Length(aAssociatedTopics);
if nCountAssociatedTopics > 0 then
begin
GetSortedTopicIDs(aAssociatedTopics, nCountAssociatedTopics);
// Code snipped
end;
end;
finally
Result := Result + '</ul>';
end;
end;
The script compiled in the HelpNDoc internal editor with no issues. But when I go to actually build my HTML documentation I encounter a problem:
The HelpNDoc API is explained here.
Is there something wrong with my code?
I decided to go about it a different way and used a simpler technique:
procedure GetSortedTopicIDs(var aTopics : array of String; iNumTopics : Integer);
var
iTopic : Integer;
// List of output
aList: TStringList;
begin
// Init list
aList := TStringList.Create;
// Build a new array of "nnn x"
// - nnn is the help context id
// - x is the topid id
// Note: I know that the context ID values are within the range 0 - 200
for iTopic := 0 to iNumTopics - 1 do
// We pad the context id with 0. We could increase the padding width to
// make the script mre useful
aList.Add(Format('%0.3d %s', [
HndTopics.GetTopicHelpContext(aTopics[iTopic]),
aTopics[iTopic]
]));
// Now we sort the new array (which basically sorts it by context id)
aList.Sort;
// Update original array
for iTopic := 0 to iNumTopics - 1 do
// We ignore the "nnn " part of the string to get just the topic id
aTopics[iTopic] := copy(aList[iTopic],5, length(aList[iTopic])-4);
// Tidy up
aList.Free;
end;
This compiles and I get the sorted array of topic IDs at the end of it. So the pop-up help is now listed as I want.

"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_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