Problem with ASSERT ... REPORT and NUL

A

Analog_Guy

I have written a function to pad an input string with NUL to the
chosen string width (set by a constant):

SUBTYPE NAMETYPE IS STRING(1 TO STRING_WIDTH);

FUNCTION pad_string (name : STRING) RETURN NAMETYPE IS
VARIABLE name_pad : NAMETYPE;
BEGIN
name_pad(1 TO name'LENGTH) := name;
name_pad((name'LENGTH + 1) TO name_pad'LENGTH) := (OTHERS => NUL);
RETURN name_pad;
END FUNCTION pad_string;

The FUNCTION works fine, and I can now pass the string to and from
modules through records. However, when I try to use an ASSERT ...
REPORT to print out some NOTES, nothing prints out after the
concatenated record element.

ASSERT (FALSE)
REPORT "*** Signal of Interest: " & reset_cmd.name & " end of
line!"
SEVERITY NOTE;

The final "end of line!" text does not print??? If I substitute
whitespace into the FUNCTION instead of the NUL, then the full REPORT
line prints to the screen. Is the NUL in the reset_cmd.name killing
the rest of the line, and why?
 
K

KJ

The final "end of line!" text does not print??? If I substitute
whitespace into the FUNCTION instead of the NUL, then the full REPORT
line prints to the screen. Is the NUL in the reset_cmd.name killing
the rest of the line
Yes

and why?

Because the 'report' statement uses the NUL character to determine
where to stop 'reporting' but the concatenate operator '&' does not.
To get what you want you'll need to use (or reinvent) the 'strcat'
function or create a different function (say 'trim()') that returns a
string up to but not including the NUL character. Then you would
report this like...
ASSERT (FALSE)
REPORT "*** Signal of Interest: " & trim(reset_cmd.name) & "
end of
line!"
SEVERITY NOTE;

KJ
 
H

HT-Lab

KJ said:
Yes

and why?

Because the 'report' statement uses the NUL character to determine
where to stop 'reporting' but the concatenate operator '&' does not.
To get what you want you'll need to use (or reinvent) the 'strcat'
function or create a different function (say 'trim()') that returns a
string up to but not including the NUL character. Then you would
report this like...
ASSERT (FALSE)
REPORT "*** Signal of Interest: " & trim(reset_cmd.name) & "
end of
line!"
SEVERITY NOTE;

KJ

NULL, not NUL

Hans
www.ht-lab.com
 
J

Jonathan Bromley

I have written a function to pad an input string with NUL to the
chosen string width (set by a constant):

SUBTYPE NAMETYPE IS STRING(1 TO STRING_WIDTH);

FUNCTION pad_string (name : STRING) RETURN NAMETYPE IS
VARIABLE name_pad : NAMETYPE;
BEGIN
name_pad(1 TO name'LENGTH) := name;
name_pad((name'LENGTH + 1) TO name_pad'LENGTH) := (OTHERS => NUL);
RETURN name_pad;
END FUNCTION pad_string;

The FUNCTION works fine, and I can now pass the string to and from
modules through records. However, when I try to use an ASSERT ...
REPORT to print out some NOTES, nothing prints out after the
concatenated record element.

ASSERT (FALSE)
REPORT "*** Signal of Interest: " & reset_cmd.name & " end of
line!"
SEVERITY NOTE;

The final "end of line!" text does not print??? If I substitute
whitespace into the FUNCTION instead of the NUL, then the full REPORT
line prints to the screen. Is the NUL in the reset_cmd.name killing
the rest of the line, and why?

Clearly it is; I'm not sure what the LRM says about this, but if
you imagine that tools are probably using C strings internally
then it's not too surprising - the NUL character will terminate
the string, if it's copied to a C string internally.

The solution is for you to write a complementary function to your
pad_string(), and then invoke it in the string concatenation:

function unpad(s: in string) return string is
-- Normalise the string to (1 to N), just in case
-- someone's messing you around :)
constant ns: string(1 to s'length) := s;
-- Variable to store position of last non-null char
variable p: integer := s'length;
begin
-- 1. find where the first NUL happens
for i in ns'range loop
if ns(i) = NUL then
p := i-1;
exit;
end if;
end loop;
-- 2. return the non-NUL part of the string
return ns(1 to p);
end;
....
report "Prefix " & unpad(reset_cmd.name) & " Suffix";

Alternatively, since you are putting your string into a record,
it may be easier to add a "name length" integer to the record.
Then you don't even need to bother with the pad() function;
you can simply copy the name string to the appropriate part
of the record field...

type cmd_record is record
...
name: string (1 to STRING_WIDTH);
name_len: integer;
...
end record;
procedure set_name(cmd: inout cmd_record; name: string) is
begin
cmd.name_len = name'length;
cmd.name(1 to name'length) := name;
end;
function get_name(cmd: cmd_record) return string is
begin
return cmd.name(1 to cmd.name_len);
end;
....
set_name(reset_cmd, "reset");
....
report "Prefix " & get_name(reset_cmd) & " Suffix";

Executive summary: VHDL strings are poo, but you can make them
bearable by a suitable combination of cunning subprograms and
record types. VHDL functions with dynamically-elaborated
return subtypes are brilliant.

Thought for the day: You can build "object-oriented programming"
in VHDL by creating a package with a record definition for your
data, and a bunch of procedures each of which takes a variable
of the record type as its first parameter, and operates on that
variable. The package then works a bit like a class definition.
OK, instead of saying "my_object.method(params);" you must say
"method(my_object, params);" but it's a small price to pay.
And yes, I know there's a pile of important OOP stuff you *can't*
do (inheritance, polymorphism) but hey, it's better than nothing.
--
Jonathan Bromley, Consultant

DOULOS - Developing Design Know-how
VHDL * Verilog * SystemC * e * Perl * Tcl/Tk * Project Services

Doulos Ltd., 22 Market Place, Ringwood, BH24 1AW, UK
(e-mail address removed)
http://www.MYCOMPANY.com

The contents of this message may contain personal views which
are not the views of Doulos Ltd., unless specifically stated.
 
J

Jonathan Bromley

NULL, not NUL

Really? I thought the constant STD.CHARACTER'VAL(0)
was named NUL. NULL is a VHDL keyword.
--
Jonathan Bromley, Consultant

DOULOS - Developing Design Know-how
VHDL * Verilog * SystemC * e * Perl * Tcl/Tk * Project Services

Doulos Ltd., 22 Market Place, Ringwood, BH24 1AW, UK
(e-mail address removed)
http://www.MYCOMPANY.com

The contents of this message may contain personal views which
are not the views of Doulos Ltd., unless specifically stated.
 
A

Analog_Guy

Clearly it is; I'm not sure what the LRM says about this, but if
you imagine that tools are probably using C strings internally
then it's not too surprising - the NUL character will terminate
the string, if it's copied to a C string internally.

The solution is for you to write a complementary function to your
pad_string(), and then invoke it in the string concatenation:

function unpad(s: in string) return string is
-- Normalise the string to (1 to N), just in case
-- someone's messing you around :)
constant ns: string(1 to s'length) := s;
-- Variable to store position of last non-null char
variable p: integer := s'length;
begin
-- 1. find where the first NUL happens
for i in ns'range loop
if ns(i) = NUL then
p := i-1;
exit;
end if;
end loop;
-- 2. return the non-NUL part of the string
return ns(1 to p);
end;
....
report "Prefix " & unpad(reset_cmd.name) & " Suffix";

Alternatively, since you are putting your string into a record,
it may be easier to add a "name length" integer to the record.
Then you don't even need to bother with the pad() function;
you can simply copy the name string to the appropriate part
of the record field...

type cmd_record is record
...
name: string (1 to STRING_WIDTH);
name_len: integer;
...
end record;
procedure set_name(cmd: inout cmd_record; name: string) is
begin
cmd.name_len = name'length;
cmd.name(1 to name'length) := name;
end;
function get_name(cmd: cmd_record) return string is
begin
return cmd.name(1 to cmd.name_len);
end;
....
set_name(reset_cmd, "reset");
....
report "Prefix " & get_name(reset_cmd) & " Suffix";

Executive summary: VHDL strings are poo, but you can make them
bearable by a suitable combination of cunning subprograms and
record types. VHDL functions with dynamically-elaborated
return subtypes are brilliant.

Thought for the day: You can build "object-oriented programming"
in VHDL by creating a package with a record definition for your
data, and a bunch of procedures each of which takes a variable
of the record type as its first parameter, and operates on that
variable. The package then works a bit like a class definition.
OK, instead of saying "my_object.method(params);" you must say
"method(my_object, params);" but it's a small price to pay.
And yes, I know there's a pile of important OOP stuff you *can't*
do (inheritance, polymorphism) but hey, it's better than nothing.
--
Jonathan Bromley, Consultant

DOULOS - Developing Design Know-how
VHDL * Verilog * SystemC * e * Perl * Tcl/Tk * Project Services

Doulos Ltd., 22 Market Place, Ringwood, BH24 1AW, UK
(e-mail address removed)://www.MYCOMPANY.com

The contents of this message may contain personal views which
are not the views of Doulos Ltd., unless specifically stated.- Hide quoted text -

- Show quoted text -

Thank you very much for your advice and tips. I don't have any
experience with C or OOP and couldn't find what I was looking for in
the various texts I have.

I will play around with both methods you suggested.

With your second suggestion, what happens to the rest of the string?
Say string length is 10 and your first assignment is 5 characters, and
then your second assignment is 3 characters. I assume that the
defined string will have artifacts from previous assignments, but I
guess it doesn't really matter because you are not looking at the
whole string anyway.
 
K

KJ

Jonathan Bromley said:
Thought for the day: You can build "object-oriented programming"
in VHDL by creating a package with a record definition for your
data, and a bunch of procedures each of which takes a variable
of the record type as its first parameter, and operates on that
variable. The package then works a bit like a class definition.
OK, instead of saying "my_object.method(params);" you must say
"method(my_object, params);" but it's a small price to pay.
And yes, I know there's a pile of important OOP stuff you *can't*
do (inheritance, polymorphism) but hey, it's better than nothing.

My fav OOP-ish thing to do in VHDL in function overloading. All of the
read/write ports with their various bit fields gets defined in a record.
Each one will then get a 'To_Std_Logic_Vector' and 'From_Std_Logic_Vector'
function defined that handles the obligatory conversions to/from
std_logic_vector types. On the testbench side override some 'Read_Port' and
'Write_Port' functions so that the processor model becomes a straightforward
listing of reads and writes to ports without being cluttered up with
conversion functions and other nonsense.

Now, if someone can tell me how to override the 'deallocate' function I'd be
happy. The basic idea is that I have some record type, one element of which
is a pointer to something. I'd like to override 'deallocate' so that I can
first deallocate the record element and then call the deallocate procedure
that would normally have been called, had I not overridden it. Sort of
like...

type t_My_Type is record
....
pSomething: t_Pointer_Type_To_Something;
end record;
type ptr_My_Type is access My_Type;

procedure deallocate(p: ptr_My_Type) is
begin
if p.pSomething /= NUL then
deallocate(p.pSomething; -- Free up the memory if it has been
allocated.
end if;
deallocate(p); --**** Don't think this will work ***
end procedure deallocate;

The line "deallocate(p)" seems problematic since it is not calling the
'base' deallocate procedure but would end up calling itself. Once that
little problem is worked out one can basically have the equivalent of a
'class destructor'....the next thing would be to override 'new' to create
constructors at some point also. Seems like it should be possible, just not
quite sure how one goes about calling the 'base deallocate' procedure (or
even really if 'new' and 'deallocate' can even be overridden for that
matter).

Any thoughts?

KJ
 
J

Jonathan Bromley

My fav OOP-ish thing to do in VHDL in function overloading. All of the
read/write ports with their various bit fields gets defined in a record.
Each one will then get a 'To_Std_Logic_Vector' and 'From_Std_Logic_Vector'
function defined that handles the obligatory conversions to/from
std_logic_vector types. On the testbench side override some 'Read_Port' and
'Write_Port' functions so that the processor model becomes a straightforward
listing of reads and writes to ports without being cluttered up with
conversion functions and other nonsense.

Yes. Nice.
Now, if someone can tell me how to override the 'deallocate' function

I don't believe you can, but I confess I've never bothered to
investigate. It's a "built-in" and such things are often strange.
When I have the same problem I tend to create "dispose" procedures
for any interesting access types. Those can of course be
overloaded by argument type, and each object's "dispose" then
starts by calling "dispose" on any appropriate child objects,
before finally calling "deallocate" on the original pointer
argument once you're sure there is nothing left dangling.
It's obviously harder if your data structure has cycles...
Any thoughts?

Only rarely :)
--
Jonathan Bromley, Consultant

DOULOS - Developing Design Know-how
VHDL * Verilog * SystemC * e * Perl * Tcl/Tk * Project Services

Doulos Ltd., 22 Market Place, Ringwood, BH24 1AW, UK
(e-mail address removed)
http://www.MYCOMPANY.com

The contents of this message may contain personal views which
are not the views of Doulos Ltd., unless specifically stated.
 
J

Jonathan Bromley

With your second suggestion, what happens to the rest of the string?
Say string length is 10 and your first assignment is 5 characters, and
then your second assignment is 3 characters. I assume that the
defined string will have artifacts from previous assignments, but I
guess it doesn't really matter because you are not looking at the
whole string anyway.

Exactly so. When you do this kind of thing, it's best to make a
contract with yourself so that you *never* access the fields of
the record directly, but instead make use of the set/get
functions/procedures that you have thoughtfully provided.
VHDL can't enforce that contract, unfortunately, but it's a
good discipline and it is likely to make your code much more
robust against future changes to the details of the data structure.
--
Jonathan Bromley, Consultant

DOULOS - Developing Design Know-how
VHDL * Verilog * SystemC * e * Perl * Tcl/Tk * Project Services

Doulos Ltd., 22 Market Place, Ringwood, BH24 1AW, UK
(e-mail address removed)
http://www.MYCOMPANY.com

The contents of this message may contain personal views which
are not the views of Doulos Ltd., unless specifically stated.
 
M

Mike Treseler

Jonathan said:
VHDL functions with dynamically-elaborated
return subtypes are brilliant.

.... and woefully underused.
I can fill in just about anything
that I think vhdl is "missing"
by writing a function.
Some examples:
http://home.comcast.net/~mike_treseler/min_vec_len.vhd
Thought for the day: You can build "object-oriented programming"
in VHDL by creating a package with a record definition for your
data, and a bunch of procedures each of which takes a variable
of the record type as its first parameter, and operates on that
variable. The package then works a bit like a class definition.
OK, instead of saying "my_object.method(params);" you must say
"method(my_object, params);" but it's a small price to pay.
And yes, I know there's a pile of important OOP stuff you *can't*
do (inheritance, polymorphism) but hey, it's better than nothing.

Yes this works well for me.
Once I am comfortable using
variables, the next step is
complex data structures and
the procedures to update them.
Yes, this code looks
nothing like a netlist for new
users, but an RTL viewer can
solve even that problem.

-- Mike Treseler
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Similar Threads


Members online

Forum statistics

Threads
473,756
Messages
2,569,535
Members
45,008
Latest member
obedient dusk

Latest Threads

Top