Strange signal non-assignment

A

Andy Peters

Here's an odd one. Following is a stripped-down test entity which has
two shift registers, one "good" and one "bad." I ginned this up
because a colleague wrote some code that uses the latter and it showed
a strange simulation result.

---- code here:
library ieee;
use ieee.std_logic_1164.all;

entity shift_test is
end entity shift_test;

architecture test of shift_test is

signal gSysClk : std_logic := '1'; -- the clock
signal aReset : std_logic := '1'; -- async reset
signal sReset : std_logic; -- sync reset

signal srStart : std_logic := '0'; -- start shift
signal srStop : std_logic := '0';

signal goodSR : std_logic_vector(7 downto 0);
signal badSR : std_logic_vector(7 downto 0);

begin -- architecture test

bit0 : process (gSysClk) is
begin -- process bit0
if rising_edge(gSysClk) then
if gReset = '1' then
goodSR(0) <= '0';
badSR(0) <= '0';
elsif srStart = '1' then
goodSR(0) <= '1';
badSR(0) <= '1';
elsif srStop = '1' then
goodSR(0) <= '0';
badSR(0) <= '0';
end if;
end if;
end process bit0;

shifters : process (gSysClk) is
begin -- process shifters
if rising_edge(gSysClk) then
goodSR(goodSR'high downto 1) <= goodSR(goodSR'high - 1
downto 0);

badshiftloop : for i in 1 to badSR'high loop
badSR(i) <= badSR(i-1);
end loop badshiftloop;
end if;
end process shifters;

-- toggle the start and stop.
MainTest : process is
begin -- process MainTest
wait until sReset = '0';
wait for 500 NS;
wait until rising_edge(gSysClk);
srStart <= '1';
wait until rising_edge(gSysClk);
srStart <= '0';
wait for 500 NS;
wait until rising_edge(gSysClk);
srStop <= '1';
wait until rising_edge(gSysClk);
srStop <= '0';
wait until rising_edge(gSysClk);
wait;
end process MainTest;

-- clock:
gSysClk <= not gSysClk after 100 NS;
aReset <= '0' after 666 NS;

SyncReset : process (gSysClk) is
begin -- process SyncReset
if rising_edge(gSysClk) then
sReset <= aReset;
end if;
end process SyncReset;

end architecture test;
------- end code

Basically, we want to expressly set or clear the input to the shift
register. This is done in the bit0 process, depending on the state of
srStart and srStop.

The shifters process never explicitly assigns bit 0 as that is
ostensibly handled in bit0. The goodSR shift register functions as one
expects: goodSR(0) is set one clock after srStart and remains true
until srStop, and the bits shift through properly.

However, in the badSR, none of the bits are ever assigned ... the
shift register remains "UUUUUUUU". Looking at the drivers on this
signal, bit0 is actually assigned a '1' in the bit0 process at the
correct time, but it also has a driver shown for the shifters process,
and this driver's value is 'U'. The bit0 process creates a driver for
goodSR(0) but there is no driver in the shifters process like there is
for badSR(0). So it's the extra driver in shifters on badSR(0) that is
overriding the bit0 assignment (one delta cycle later).

But why is this the case? Why the second driver on the loop but not on
the simpler assignment? I don't see how the simulator infers a driver
in shifters on badSR(0) but not goodSR(0) when in neither case is an
explicit assignment made to either.

FWIW, I get the same results in ModelSim and in Active-HDL.

-a
 
M

Magne Munkejord

HT-Lab said:
Google for longest static prefix,

Also see a simpler example here : http://www.ht-lab.com/question.jpg

Hans
www.ht-lab.com

Well,
I googled it and tried to understand;
- Longest static prefix of goodSR in process shifters is 7 downto 1
since it is defined explicitly, hence no driver for goodSR(0) in that
process.
- L.S.P. of badSR is all elements of badSR since it is indexed by
variable (not static) and therefore all elements gets assigned a driver
in that process.

Then we have 2 drivers on badSR(0) of type std_logic. Since process
shifters does not explicitly assign any value to badSR(0) it will drive
it with the value it had at the delta time the process is executed.
Since the signal is initialized with 'U', and 'U' will always win the
resolution, process bit0 will never be able to force any other value on
the signal.

Does that make sense? Hopefully, I got it right and learned something
new today!
 
P

Paul Uiterlinden

Magne said:
Well,
I googled it and tried to understand;
- Longest static prefix of goodSR in process shifters is 7 downto 1
since it is defined explicitly, hence no driver for goodSR(0) in that
process.
- L.S.P. of badSR is all elements of badSR since it is indexed by
variable (not static) and therefore all elements gets assigned a driver
in that process.

Then we have 2 drivers on badSR(0) of type std_logic. Since process
shifters does not explicitly assign any value to badSR(0) it will drive
it with the value it had at the delta time the process is executed.

I think this sentence is not correct, or at least I do not understand it
completely.

The thing is: if a process creates a driver for a signal, the initial value
of the driver is 'U' for a std_logic (or in general: the left-most value of
the type). This is so from time zero and delta zero, and it remains so to
the end of the simulation if there is not a signal assignment. This happens
with badSR(0) in process shifters.

A driver is a permanent thing. It is not only active at the moment a value
is assigned to a signal. The assignment just changes the value of the
driver.

The actual value of a signal with a resolved type (here: std_logic) is
determined by the resolution function of std_logic. That function receives
the value of all drivers and returns the resulting value. In case of
std_logic, 'U' wins over any other value. So badSR(0) wil remain 'U' for
ever.
Since the signal is initialized with 'U', and 'U' will always win the
resolution, process bit0 will never be able to force any other value on
the signal.

Does that make sense?

Yes, absolutely.
Hopefully, I got it right and learned something new today!

What I would like to add: I do not see any reason to have two processes in
the example of the OP. It has been said in this news group before (and
rightfully so): limit the number of processes. Having multiple processes
makes debugging harder. This is my experience as well.
 
R

rickman

What I would like to add: I do not see any reason to have two processes in
the example of the OP. It has been said in this news group before (and
rightfully so): limit the number of processes. Having multiple processes
makes debugging harder. This is my experience as well.

That makes sense to me. At least, I would not put the assignment of
bit 0 in a separate process. Instead I might separately define the
input to bit 0. That could be done either in a process or a
concurrent assignment. I often do that when the logic expressed in an
if-then tree would cloud the rest of the structure of the register
process.

Rick
 
A

Andy Peters

Well,
I googled it and tried to understand;
- L.S.P. of badSR is all elements of badSR since it is indexed by
variable (not static) and therefore all elements gets assigned a driver
in that process.

Ahhhh, got it.
Then we have 2 drivers on badSR(0) of type std_logic. Since process
shifters does not explicitly assign any value to badSR(0) it will drive
it with the value it had at the delta time the process is executed.
Since the signal is initialized with 'U', and 'U' will always win the
resolution, process bit0 will never be able to force any other value on
the signal.

Yes, this seems like it's exactly the problem. I've never run into
this because I always use the non-loop construct for the shift
register, and I would have put the bit 0 assignment in the same
process.

File this under "really dusty corners of VHDL."

-a
 
M

Magne Munkejord

Paul said:
I think this sentence is not correct, or at least I do not understand it
completely.
Yes, it was badly written and not very accurate, in some way incorrect.
What I meant to say is that VHDL will explicitly assign a value to a
signal it has a driver for, even if no such signal assignment exists in
the code or are not executed due to conditional constructs. In this case
badSR(0) was initialized to 'U' (the default) so it is equivalent to
badSR(0) <= 'U' until the process assigns another value to it (which it
doesn't).
If badSR(0) was initialized to 'Z', the signal would resolve to the
value driven by process bit0. But the driver for process shifter would
still be 'Z'.
The thing is: if a process creates a driver for a signal, the initial value
of the driver is 'U' for a std_logic (or in general: the left-most value of
the type). This is so from time zero and delta zero, and it remains so to
the end of the simulation if there is not a signal assignment. This happens
with badSR(0) in process shifters.

I am sure you know, but it can be initialized to another value when the
signal is declared. If
signal badSR : std_logic_vector(7 downto 0) := (others => '1');
then I assume all drivers for the signal(s) will be initialized to '1'?
 
A

Andy

That makes sense to me.  At least, I would not put the assignment of
bit 0 in a separate process.  Instead I might separately define the
input to bit 0.  That could be done either in a process or a
concurrent assignment.  I often do that when the logic expressed in an
if-then tree would cloud the rest of the structure of the register
process.

Rick

I agree. This is one of those cases where using a variable to capture
combinatorial logic in a clocked process makes particularly good
sense. You get the separation of a separate process or concurrent
assignment, with the simulation efficiency and scope control of a
single process.

Andy
 
P

Paul Uiterlinden

Magne said:
Yes, it was badly written and not very accurate, in some way incorrect.
What I meant to say is that VHDL will explicitly assign a value to a
signal it has a driver for, even if no such signal assignment exists in
the code or are not executed due to conditional constructs. In this case
badSR(0) was initialized to 'U' (the default) so it is equivalent to
badSR(0) <= 'U' until the process assigns another value to it (which it
doesn't).
If badSR(0) was initialized to 'Z', the signal would resolve to the
value driven by process bit0. But the driver for process shifter would
still be 'Z'.


I am sure you know, but it can be initialized to another value when the
signal is declared. If
signal badSR : std_logic_vector(7 downto 0) := (others => '1');
then I assume all drivers for the signal(s) will be initialized to '1'?

Good question (sorry for the late answer).

My initial though was: yes.
Then I started thinking and doubt set in.
So time for a little experiment:

ENTITY sig_init_val IS
END ENTITY sig_init_val;

LIBRARY ieee;
USE ieee.std_logic_1164.ALL;

ARCHITECTURE arch OF sig_init_val IS
SIGNAL s : std_logic := '1';
BEGIN
driver1: PROCESS IS
BEGIN
WAIT FOR 1 ns;
s <= '0';
WAIT;
END PROCESS driver1;

driver2: PROCESS IS
BEGIN
WAIT FOR 2 ns;
s <= '0';
WAIT;
END PROCESS driver2;

mon_s: PROCESS(s) IS
BEGIN
REPORT "s = " & std_logic'IMAGE(s) & ".";
END PROCESS mon_s;
END ARCHITECTURE arch;


The outcome of this is:

# ** Note: s = '1'.
# Time: 0 ps Iteration: 0 Instance: /sig_init_val
# ** Note: s = 'X'.
# Time: 1 ns Iteration: 1 Instance: /sig_init_val
# ** Note: s = '0'.
# Time: 2 ns Iteration: 1 Instance: /sig_init_val

So the signal starts at '1' at time=0 ns, delta=0. Then it goes to 'X' at
time=1 ns, delta=1. Finally it goes to '1' at time=2 ns, delta=1.

Perhaps the surprising part is the transition at 1 ns to 'X'. So driving a
value in the future (1 ns later) has an effect now (at 1 ns).

The explanation of course is that both processes create a driver for signal
s. The initial value of both drivers is '1', as you already suspected.

At 1 ns driver1 changes value to '0', while driver2 stay unchanged at '1'.
Hence the resulting 'X'

At 2 ns driver2 changes value to '0', so now both driving values are '0'.
Hence the transition to '0'.

Ahh, I love it when theory and practice coincide. ;-)
 

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

Forum statistics

Threads
474,053
Messages
2,570,431
Members
47,075
Latest member
TysonV438

Latest Threads

Top