Assistance with INOUT Records

A

Analog_Guy

Hi ... I've been a bit stumped with all the issues I am having with
records, when trying to implement a client/server approach between two
entities.

Basically, I keep getting X's in the simulation for cmd_rec.a and
cmd_rec.b, even though they appear to be properly defined.

The records are initialized at the ENTITY PORT level of the two
components (test_ctrl_clv.vhd and cpu_bfm_clv.vhd). They are assigned
in cpu_bfm_pkg_clv.vhd ... and this is when I get X's.

I have noted two things:

1. If I uncomment the assignment to cmd_rec.c in cpu_bfm_clv.vhd
everything works fine! Why do I have to make a signal assignment to
cmd_rec.c in order for this to work? The record element cmd_rec.c is
already initialized to a value at the ENTITY PORT level ... isn't that
good enough?

2. If i leave the assignment to cmd_rec.c in cpu_bfm_clv.vhd commented
out, and instead change CMD_REC_TYPE_INIT to all 'Z' entries everything
works fine! I really want some of the signals to have initial values,
so I didn't want to have to do this.

What are the rules related to RECORDs to ensure I am not running into
all these conflicts? I have chosen all RECORD elements to be STD_LOGIC
so that they should all be resolved. I can't seem to find anything
relevant in the LRM regarding this. What is the standard a




A very simplified example (which compiles and simulates) is presented
below:
************************************************************************************************
1. TOP-LEVEL (tb_top_test_clv.vhd)
--============================================
-- General Library Declarations
LIBRARY ieee;
USE ieee.std_logic_1164.ALL;
USE ieee.numeric_std.ALL;
--
-- BFM Package Declarations
LIBRARY rec_test;
USE rec_test.cpu_bfm_pkg_clv;
--
--===============================================
ENTITY tb_top_test_clv IS
--
END ENTITY tb_top_test_clv;
--
--================================================
ARCHITECTURE tb OF tb_top_test_clv IS
--
-- DUT Interface Signals
SIGNAL M_CTRL_CLK : STD_LOGIC; -- Main PLD clock.
SIGNAL RESETn : STD_LOGIC; -- Main PLD reset.
--
-- Transaction Based Signals
SIGNAL ctrl_rec : cpu_bfm_pkg_clv.CTRL_REC_TYPE;
SIGNAL cmd_rec : cpu_bfm_pkg_clv.CMD_REC_TYPE;
--
--------------------------------------------------------------------------------
-- COMPONENT DECLARATIONS
-- ----------------------
--
COMPONENT test_ctrl_clv IS
PORT (
CLOCK : IN STD_LOGIC; -- Main PLD clock.
RESETn : IN STD_LOGIC; -- Main PLD reset.
ctrl_rec : INOUT cpu_bfm_pkg_clv.CTRL_REC_TYPE;
cmd_rec : INOUT cpu_bfm_pkg_clv.CMD_REC_TYPE
);
END COMPONENT test_ctrl_clv;
--
--+++++++++++++++++++++++++++++++++++++++++++++++++
COMPONENT cpu_bfm_clv IS
PORT (
CLOCK : IN STD_LOGIC; -- Main PLD clock.
RESETn : IN STD_LOGIC; -- Main PLD reset.
ctrl_rec : INOUT cpu_bfm_pkg_clv.CTRL_REC_TYPE;
cmd_rec : INOUT cpu_bfm_pkg_clv.CMD_REC_TYPE
);
END COMPONENT cpu_bfm_clv;
--
--------------------------------------------------------------------------------
BEGIN
--
-- COMPONENT INSTANTIATIONS
-- ------------------------
test_ctrl_u1 : test_ctrl_clv
PORT MAP (
CLOCK => M_CTRL_CLK,
RESETn => RESETn,
ctrl_rec => ctrl_rec,
cmd_rec => cmd_rec
);
--
--+++++++++++++++++++++++++++++++++++++++++++++++++
cpu_bfm_u1 : cpu_bfm_clv
PORT MAP (
CLOCK => M_CTRL_CLK,
RESETn => RESETn,
ctrl_rec => ctrl_rec,
cmd_rec => cmd_rec
);
--
END ARCHITECTURE tb;
--
--=========================================




**************************************************************************************************
2. TEST CONTROL (test_ctrl_clv.vhd)
--=====================================================
-- General Library Declarations
LIBRARY ieee;
USE ieee.std_logic_1164.ALL;
--
-- BFM Package Declarations
LIBRARY rec_test;
USE rec_test.cpu_bfm_pkg_clv;
--
--===================================================
ENTITY test_ctrl_clv IS
PORT (
CLOCK : IN STD_LOGIC;
RESETn : IN STD_LOGIC;
ctrl_rec : INOUT cpu_bfm_pkg_clv.CTRL_REC_TYPE :=
cpu_bfm_pkg_clv.CTRL_REC_TYPE_INIT;
cmd_rec : INOUT cpu_bfm_pkg_clv.CMD_REC_TYPE :=
cpu_bfm_pkg_clv.CMD_REC_TYPE_INIT
);
END ENTITY test_ctrl_clv;
--
--================================================
ARCHITECTURE behav OF test_ctrl_clv IS
BEGIN
--
main_testcase : PROCESS
BEGIN
WAIT UNTIL RISING_EDGE(RESETn);
-- Exercise CPU READs
cpu_bfm_pkg_clv.cpu_read(X"0000_0000", X"A5A5_A5A5_A5A5_A5A5",
ctrl_rec, cmd_rec);
cpu_bfm_pkg_clv.cpu_read(X"0000_0001", X"5A5A_5A5A_5A5A_5A5A",
ctrl_rec, cmd_rec);
-- Extend Simulation Time
WAIT FOR 1000 ns;
-- Terminate Simulation (Current settings in ModelSim break simulation
on severity
-- level of FAILURE)
ASSERT (FALSE)
REPORT "*** Ignore Failure *** : Simulator Terminated Normally!"
SEVERITY FAILURE;
-- Suspend Process
WAIT;
END PROCESS main_testcase;
END ARCHITECTURE behav;
--
--=========================================




*********************************************************************************
3. BFM PACKAGE (cpu_bfm_pkg_clv.vhd)
--============================================
-- General Library Declarations
LIBRARY ieee;
USE ieee.std_logic_1164.ALL;
--
--=============================================
PACKAGE cpu_bfm_pkg_clv IS
-- Transaction Record Declarations
TYPE CTRL_REC_TYPE IS RECORD
req : STD_LOGIC;
ack : STD_LOGIC;
END RECORD CTRL_REC_TYPE;

TYPE CMD_REC_TYPE IS RECORD
a : STD_LOGIC;
b : STD_LOGIC;
c : STD_LOGIC;
END RECORD CMD_REC_TYPE;
--
--++++++++++++++++++++++++++++++++++++++++++
-- Transaction Record Initializations
CONSTANT CTRL_REC_TYPE_INIT : CTRL_REC_TYPE :=
( req => '0',
ack => 'Z');

CONSTANT CMD_REC_TYPE_INIT : CMD_REC_TYPE :=
( a => 'Z',
b => 'Z',
c => 'Z'
);
--
--------------------------------------------------------------------------------
-- CPU BFM PROCEDURE DECLARATIONS
-- ------------------------------
--
-- Transaction Level Read
-- ----------------------
PROCEDURE cpu_read ( CONSTANT addr : IN STD_LOGIC_VECTOR;
CONSTANT data : IN STD_LOGIC_VECTOR;
SIGNAL ctrl_rec : INOUT CTRL_REC_TYPE;
SIGNAL cmd_rec : INOUT CMD_REC_TYPE
);
--
END PACKAGE cpu_bfm_pkg_clv;
--
--===================================================
PACKAGE BODY cpu_bfm_pkg_clv IS
--
-- CPU BFM PROCEDURE DEFINITIONS
-- -----------------------------
--
-- Transaction Level Read
-- ----------------------
PROCEDURE cpu_read ( CONSTANT addr : IN STD_LOGIC_VECTOR;
CONSTANT data : IN STD_LOGIC_VECTOR;
SIGNAL ctrl_rec : INOUT CTRL_REC_TYPE;
SIGNAL cmd_rec : INOUT CMD_REC_TYPE) IS
BEGIN
-- Put Transaction into Record
cmd_rec.a <= '0';
cmd_rec.b <= '1';
-- Handshake with BFM
ctrl_rec.req <= NOT(ctrl_rec.req);
WAIT UNTIL ctrl_rec.ack'ACTIVE;
END PROCEDURE cpu_read;
--
END PACKAGE BODY cpu_bfm_pkg_clv;
--
--============================================




****************************************************************************************
4. BFM (cpu_bfm_clv.vhd)
--================================================
-- General Library Declarations
LIBRARY ieee;
USE ieee.std_logic_1164.ALL;
--
-- BFM Package Declarations
LIBRARY rec_test;
USE rec_test.cpu_bfm_pkg_clv;
--
================================================
ENTITY cpu_bfm_clv IS
PORT (
CLOCK : IN STD_LOGIC;
RESETn : IN STD_LOGIC;
ctrl_rec : INOUT cpu_bfm_pkg_clv.CTRL_REC_TYPE :=
cpu_bfm_pkg_clv.CTRL_REC_TYPE_INIT;
cmd_rec : INOUT cpu_bfm_pkg_clv.CMD_REC_TYPE :=
cpu_bfm_pkg_clv.CMD_REC_TYPE_INIT
);
END ENTITY cpu_bfm_clv;
--
--================================================
ARCHITECTURE behav OF cpu_bfm_clv IS
--
BEGIN
--
-- INTERFACE SPECIFICATION
-- -----------------------
--
-- CPU READ Control
-- ----------------
bfm_ctrl_1 : PROCESS
BEGIN
IF (RESETn = '0') THEN
ctrl_rec.ack <= '0';
ELSIF (ctrl_rec.req'ACTIVE) THEN
-- Execute Functionality.
WAIT UNTIL CLOCK = '1' and CLOCK'EVENT;
-- cmd_rec.c <= '1' AFTER 5 ns;
WAIT FOR 100 ns;
WAIT UNTIL CLOCK = '1' and CLOCK'EVENT;
ctrl_rec.ack <= NOT(ctrl_rec.ack); -- Toggle acknowledge to
Client, indicating end of access.
END IF;
WAIT ON RESETn, ctrl_rec;
END PROCESS bfm_ctrl_1;
--
END ARCHITECTURE behav;
--
--=========================================



Sorry for the length of the post, I am not sure how to attach files
instead of just copying the text.

Your comments would be appreciated.
 
A

Analog_Guy

Analog_Guy said:
I have noted two things:

1. If I uncomment the assignment to cmd_rec.c in cpu_bfm_clv.vhd
everything works fine! Why do I have to make a signal assignment to
cmd_rec.c in order for this to work? The record element cmd_rec.c is
already initialized to a value at the ENTITY PORT level ... isn't that
good enough?

2. If i leave the assignment to cmd_rec.c in cpu_bfm_clv.vhd commented
out, and instead change CMD_REC_TYPE_INIT to all 'Z' entries everything
works fine! I really want some of the signals to have initial values,
so I didn't want to have to do this.

OOPS ... the code presented actually contains the RECORD
initializations mentioned in point #2 above.

The actual RECORD assignment that produces the X's is as follows
(cpu_bfm_pkg_clv.vhd):

CONSTANT CMD_REC_TYPE_INIT : CMD_REC_TYPE :=
( a => '1',
b => '0',
c => 'Z'
);


Thanks.
 
J

Jim Lewis

Analog_Guy,
To communicate through the record, one side has to
drive an inactive value ('Z') while the other side
drives an active value ('0' or '1').

There are numerous ways to accomplish this. For example:
1)
Initialize ports to all 'Z' with a constant.
Driving side can drive when appropriate.
Complication:
Does starting at 'Z' break anything in your model?
It does make doing an initial not on a signal difficult.
For one model I create a constant to toggle values - see below.

type stdulogic_indexby_stdulogic is array (std_ulogic) of std_ulogic;
constant toggle_sl_table : stdulogic_indexby_stdulogic := (
'0' => '1',
'L' => '1',
others => '0'
);

Are your storing any long term values in the record?
In the _past_, I would store error counts in the record -
somewhere these would need to get initialized so I used method 2.
Now I store error counts in the model and have written a
transaction that has it display a summary and return an error count.

2)
Initialize each side ports to specific values.
An element must be initialized to 'Z' if the model does
not drive it.
Initialize important signals the model drives to a valid value.
This is where I formerly initialized error counts.

CONSTANT TEST_CTRL_CMD_REC_INIT : CMD_REC_TYPE :=
( a => '1',
b => '0',
c => 'Z'
);

CONSTANT CPU_BFM_CMD_REC_INIT : CMD_REC_TYPE :=
( a => 'Z',
b => 'Z',
c => '1'
);

3)
Don't initialize ports.
In the process that drives the record, assign elements
the model does not drive to 'Z' (early) and assign elements
the model does drive to an appropriate starting value.


I used to think the records worked only with std_logic
family and 'Z', however, recently I realized that this
can be done for any resolved type with the right resolution
functions. I have some proof of concept stuff working with
integer and time, but based on this, it can work for anything.
My big hang up was that std_logic lulled me into thinking
I needed a type to have a non-driving value. Instead think
of a wired or with an identity value (such as 0) as the
non-driving value. I will be writing a paper on this soon.

Cheers,
Jim

P.S. (and now the short advertisement)
SynthWorks teaches these techniques in our
VHDL Testbenches and Verification classes.
--
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Jim Lewis
Director of Training mailto:[email protected]
SynthWorks Design Inc. http://www.SynthWorks.com
1-503-590-4787

Expert VHDL Training for Hardware Design and Verification
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
A

Analog_Guy

Jim said:
Analog_Guy,
To communicate through the record, one side has to
drive an inactive value ('Z') while the other side
drives an active value ('0' or '1').

There are numerous ways to accomplish this. For example:
1)
Initialize ports to all 'Z' with a constant.
Driving side can drive when appropriate.
Complication:
Does starting at 'Z' break anything in your model?
It does make doing an initial not on a signal difficult.
For one model I create a constant to toggle values - see below.

type stdulogic_indexby_stdulogic is array (std_ulogic) of std_ulogic;
constant toggle_sl_table : stdulogic_indexby_stdulogic := (
'0' => '1',
'L' => '1',
others => '0'
);

Are your storing any long term values in the record?
In the _past_, I would store error counts in the record -
somewhere these would need to get initialized so I used method 2.
Now I store error counts in the model and have written a
transaction that has it display a summary and return an error count.

2)
Initialize each side ports to specific values.
An element must be initialized to 'Z' if the model does
not drive it.
Initialize important signals the model drives to a valid value.
This is where I formerly initialized error counts.

CONSTANT TEST_CTRL_CMD_REC_INIT : CMD_REC_TYPE :=
( a => '1',
b => '0',
c => 'Z'
);

CONSTANT CPU_BFM_CMD_REC_INIT : CMD_REC_TYPE :=
( a => 'Z',
b => 'Z',
c => '1'
);

3)
Don't initialize ports.
In the process that drives the record, assign elements
the model does not drive to 'Z' (early) and assign elements
the model does drive to an appropriate starting value.


I used to think the records worked only with std_logic
family and 'Z', however, recently I realized that this
can be done for any resolved type with the right resolution
functions. I have some proof of concept stuff working with
integer and time, but based on this, it can work for anything.
My big hang up was that std_logic lulled me into thinking
I needed a type to have a non-driving value. Instead think
of a wired or with an identity value (such as 0) as the
non-driving value. I will be writing a paper on this soon.

Cheers,
Jim

P.S. (and now the short advertisement)
SynthWorks teaches these techniques in our
VHDL Testbenches and Verification classes.
--
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Jim Lewis
Director of Training mailto:[email protected]
SynthWorks Design Inc. http://www.SynthWorks.com
1-503-590-4787

Expert VHDL Training for Hardware Design and Verification
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Jim,

Thank you very much for the response ... it is greatly appreciated.
All three approaches you suggest make sense ... I think I am leaning
towards Option #2 (because signals are all assigned at time 0, and I
don't have to make initializations within the processes).

Do you know why the simulation "broke" in my example? The one record
subelement I didn't create a signal assignment for, caused the other
two to be invalid. It was initialized to Z at the ports of both
components it is connected between. Does failing to create a signal
assignment for one subelement of the record cause problems with the
whole record?

I have not convinced myself that it is related, but LRM 2000
(1.1.1.2-105) states "It is an error if some of the subelements of a
composite formal port are connected and others are either unconnected
or unassociated." Doesn't assigning Z to a subelement that has no
other signal assignments count as creating a driver for that
subelement?


Thanks.
 
P

Paul Uiterlinden

Jim said:
I used to think the records worked only with std_logic
family and 'Z', however, recently I realized that this
can be done for any resolved type with the right resolution
functions.

Would that be by any chance after reading a post in comp.lang.vhdl on
September 16, 2006? ;-)
 
J

Jim Lewis

Paul,
>
> Would that be by any chance after reading a post in comp.lang.vhdl on
> September 16, 2006? ;-)

Actually no, as I would have attributed it to you then.
I do like your approach though. If I do adopt and/or modify
your approach do you mind if I cite your name? I don't read
all of the posts here - I wish I had seen yours sooner.
Did you know you did more than one post on the 16th :).

BTW with ideas like yours should be participating in the
Accellera VHDL technical subcommittee and its subgroups.


Currently I do all of the handshaking in std_logic so it
is already resolved. It was dealing with the other
items that are more naturally represented as integer, time,
real that I was have trouble with. For these, I am switching
to a resolution function that revolves around having identity
values (such as 0) in the type and "wire-or"ing them
together (something I would have thought of much sooner if
only I was 5 years older. For this to work of course, the
models have to cooperate by driving the identity value
on the net when they are not driving (0, 0 ns, 0.0) respectively.

The resolution function I use by the way is based on one
Ofer Hofman of Sital showed me shortly after your post
(however he was using it for something entirely different).
I posted one for real in my post on 9/30/2006 with the title
"Re: VHDL switch in real numbers."

Cheers,
Jim
--
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Jim Lewis
Director of Training mailto:[email protected]
SynthWorks Design Inc. http://www.SynthWorks.com
1-503-590-4787

Expert VHDL Training for Hardware Design and Verification
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
P

Paul Uiterlinden

Jim said:
Paul,

Actually no, as I would have attributed it to you then.

Ah, ok. I was just curious.
I do like your approach though. If I do adopt and/or modify
your approach do you mind if I cite your name?

Be my guest, I would be honored. Just make sure you spell my name
right. ;-)

And if possible, I would like to know of any adoptations/modifications
you have in mind. Sometimes nice things are created if ideas are
bounced back and forth a few times.
I don't read
all of the posts here - I wish I had seen yours sooner.
Did you know you did more than one post on the 16th :).

Oops, I missed that. Sorry for the confusion it may have caused.
BTW with ideas like yours should be participating in the
Accellera VHDL technical subcommittee and its subgroups.

Actually, this idea has been dormant in my mind for some time now.
Perhaps it's time to actually do that. How is this organized? Is it
via mailing lists?
Currently I do all of the handshaking in std_logic so it
is already resolved. It was dealing with the other
items that are more naturally represented as integer, time,
real that I was have trouble with. For these, I am switching
to a resolution function that revolves around having identity
values (such as 0) in the type and "wire-or"ing them
together (something I would have thought of much sooner if
only I was 5 years older. For this to work of course, the
models have to cooperate by driving the identity value
on the net when they are not driving (0, 0 ns, 0.0) respectively.

Yep, you're right about the cooperativeness. I dealt with the same
problem. I use the req and ack record members. Only one model may
send an ack. This however is not a real problem, because I only use
one model per cmd signal. If a model is instantiated multiple times,
there also are multiple cmd signals. If you don't do that, you cannot
have real concurrency between these models.

Because the signal is resolved anyway, sending request (commands) from
different processes is allowed. One limitation though: they may not
occur at exact the same time. This is guarded in the resolution
function by an assert statement.

To make it just a bit more versatile, I added another record memmer to
the cmd type. This member is called prio (not shown in my post to
keep it simple). As you may have guessed, clients may put a priority
on their request. The cooperativeness with regard of the priority
part is included in my do_req procedure. The resolution function
returns the highest priority and the do_req function checks if the
returned priority is its own. If not, it backs off and waits for the
other command the finish and then tries again.

The only limitation now is that no two requests may occur at the same
time with the same priority.

With this, you can do neat things, such as creating a low priority
background process, while your actual testcase does its stuff using a
higher priority.
 

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

Members online

No members online now.

Forum statistics

Threads
473,769
Messages
2,569,579
Members
45,053
Latest member
BrodieSola

Latest Threads

Top