Differfence in the assignment of a variable to a signal with and without condition

F

filmil

Greetings.

I noticed a difference between the simulation results from the two
different architectures given below. The architectures are almost the
same, except for the lines marked with "-- XXX Difference".

The intention of both architectures is that the signal 'output' be set
to the value of the variable v. The signal 'output' is only read when
rd = '1'. The first architecture sets output to an undefined value
for other values of rd, while the second drives output irrespective of
the value of rd. Thus, I expect the value of 'output', when rd='1' to
be the same in both cases.

The simulation shows results I didn't expect. In case of behavior_1,
the signal is driven as expected. In case of behavior_2, the output
signal has value "(others => '0')", which is not equal to the value of
'v'.

Why do I see different behaviors? I suspect it has to do with 'v'
being a variable and not a signal, but I don't know why.

FYI, this is a minimal example that shows the issue. The actual code
I used when I found this had to do with a FIFO. In its architecture I
used a shared variable for a memory array instead of a signal array,
for simulation efficiency.

f

--- Snippet: entity driver, architectures behavior_1 and behavior_2
library ieee;
use ieee.STD_LOGIC_1164.all;
use ieee.NUMERIC_STD.all;

entity driver is
generic( CLK_PERIOD : Time := 4 ns );
end;
architecture behavior_1 of driver is
constant WIDTH : positive := 3;
subtype t is std_logic_vector(WIDTH-1 downto 0);

constant UNDEF : t := (others => 'X');

signal clk, rd : std_logic;
signal output : t;
shared variable v : integer range 0 to (2**WIDTH-1) := 0;
begin

rdgen : process
variable i : integer;
begin
wait for 5 * CLK_PERIOD;
for i in 1 to 5 loop
wait until rising_edge(clk);
rd <= '1';
wait until rising_edge(clk);
rd <= '0';
end loop;
wait;
end process;

clkgen : process
begin
clk <= '1';
wait for CLK_PERIOD / 2;
clk <= '0';
wait for CLK_PERIOD / 2;
end process;

sequential : process(clk)
begin
if rising_edge(clk) then
v := v + 1;
end if;
end process;
output <= std_logic_vector(to_unsigned(v, WIDTH)) when rd = '1' else
UNDEF; -- XXX Difference
end;

architecture behavior_2 of driver is
constant WIDTH : positive := 3;
subtype t is std_logic_vector(WIDTH-1 downto 0);

constant UNDEF : t := (others => 'X');

signal clk, rd : std_logic;
signal output : t;
shared variable v : integer range 0 to (2**WIDTH-1) := 0;
begin

rdgen : process
variable i : integer;
begin
wait for 5 * CLK_PERIOD;
for i in 1 to 5 loop
wait until rising_edge(clk);
rd <= '1';
wait until rising_edge(clk);
rd <= '0';
end loop;
wait;
end process;

clkgen : process
begin
clk <= '1';
wait for CLK_PERIOD / 2;
clk <= '0';
wait for CLK_PERIOD / 2;
end process;

sequential : process(clk)
begin
if rising_edge(clk) then
v := v + 1;
end if;
end process;
output <= std_logic_vector(to_unsigned(v, WIDTH)); -- XXX Difference
end;
 
M

Mike Treseler

filmil said:
Why do I see different behaviors? I suspect it has to do with 'v'
being a variable and not a signal, but I don't know why.

Suggestion.
Don't use shared variables.
Declare and use regular variables inside one process.
If you need to wire processes, use signals.
See: http://home.comcast.net/~mike_treseler/
for examples.

-- Mike Treseler
 
F

Filip Miletic

Mike said:
Suggestion.
Don't use shared variables.

Just wondering: what is so bad about shared variables? Why are they
there if they are not to be used?

f
 
J

Jim Lewis

Filip,
Just wondering: what is so bad about shared variables? Why are they
there if they are not to be used?

f
I suspect that was the short way of telling your usage
of shared variables was deprecated with VHDL-2000. Historically,
they were added in VHDL-93, however, there was concern over
concurrent access to them. As a result, in VHDL-2000 they were extended
and are currently required to use a protected type (which is
different from a regular type). Hence, your usage of a shared
variable is illegal.

BTW, personally I use shared variables for modeling in testbenches.
A protected type is similar to a class, however, it is currently
missing inheritance and polymorphism (which are planned to be
added in the next revision). I find them most useful for data
structures and such.

Cheers,
Jim
SynthWorks VHDL Training
http://www.synthworks.com
 
M

Mike Treseler

Filip said:
Just wondering: what is so bad about shared variables? Why are they
there if they are not to be used?

Not bad, but an advanced topic and easy to get wrong.
In my opinion, not indicated for a simple testbench.
And there are the complications that Jim outlined.

They can be handy for variable length testbench data,

http://groups.google.com/group/comp.lang.vhdl/search?q=treseler+simple_ok

but be sure to declare them in a package,
not in an architecture (where instances collide)

-- Mike Treseler
 
A

Andy

I suspect that was the short way of telling your usage
of shared variables was deprecated with VHDL-2000. Historically,
they were added in VHDL-93, however, there was concern over
concurrent access to them. As a result, in VHDL-2000 they were extended
and are currently required to use a protected type (which is
different from a regular type). Hence, your usage of a shared
variable is illegal.

BTW, personally I use shared variables for modeling in testbenches.
A protected type is similar to a class, however, it is currently
missing inheritance and polymorphism (which are planned to be
added in the next revision). I find them most useful for data
structures and such.

Cheers,
Jim
SynthWorks VHDL Traininghttp://www.synthworks.com

What Jim said, plus...

Concurrent signal assignments are implied processes with a sensitivity
list including all signals on the right hand side of the assignment
operator.

The concurrent assignment to output in behavior_1 is sensitive to rd,
so every time rd changes, it re-executes and uses the value of v at
that time. In behavior_2, it is sensitive to nothing (no signals), so
it only executes once, when v is 0.

Andy
 
J

Jim Lewis

Mike,
In VHDL-2000 shared variables were extended and
and are currently required to use a protected type.
Hence, your usage of a shared variable is illegal.

The value of protected types is not readily apparent
until you start to use them for verification data
structures. Here is a simple example of a protected type:

type ErrorCountProtectedType is protected
procedure IncErrorCount ;
impure function GetErrorCount return integer ;
end protected ErrorCountProtectedType ;
type ErrorCountProtectedType is protected body
variable ErrCnt : integer := 0 ;
procedure IncErrorCount is
begin
ErrCnt := ErrCnt + 1 ;
end procedure IncErrorCount ;
impure function GetErrorCount return integer is
begin
return ErrCnt ;
end GetErrorCount ;
end protected body ErrorCountProtectedType ;


The following example creates a single error counter
that can be incremented by multiple different processes
using the method IncErrorCount.

architecture Test of ErrorCount is
use work.ErrorCountPkg.all ;

shared variable ErrCnt : ErrorCountProtectedType ;
begin
TestProc1 : process
begin
for i in 1 to 10 loop
ErrCnt.IncErrorCount ; wait for 1 ns ;
end loop ;
write(output, "P1 " & integer'image( ErrCnt.GetErrorCount ) & LF);
wait ;
end process ;

TestProc2 : process
begin
for i in 1 to 10 loop
ErrCnt.IncErrorCount ; wait for 2 ns ;
end loop ;
write(output, "P2 " & integer'image( ErrCnt.GetErrorCount ) & LF);
wait ;
end process ;
end Test ;


Cheers,
Jim
P.S.
The verification data structures seem quite limited until
you start to factor in the package generics that were just
added to the language.
 
M

Mike Treseler

Jim said:
Mike,
In VHDL-2000 shared variables were extended and
and are currently required to use a protected type.
Hence, your usage of a shared variable is illegal.

Please don't rat me out to the IEEE :)

Modelsim gives now gives a warning,
but the old code still runs.

Thanks for the examples of protected types.
Those are hard to find.

-- Mike Treseler
 
M

Mike Treseler

Mike said:
Thanks for the examples of protected types.
Those are hard to find.

Jim's example runs fine in Modelsim
if I put the protected types in architecture
scope instead of a package:


______________________________________________
use std.textio.all;
entity protected_error_counter is
end entity protected_error_counter;

architecture sim of protected_error_counter is
type ErrorCountProtectedType is protected
procedure IncErrorCount;
impure function GetErrorCount return integer;
end protected ErrorCountProtectedType;

type ErrorCountProtectedType is protected body
variable ErrCnt : integer := 0;
procedure IncErrorCount is
begin
ErrCnt := ErrCnt + 1;
end procedure IncErrorCount;

impure function GetErrorCount return integer is
begin
return ErrCnt;
end GetErrorCount;
end protected body ErrorCountProtectedType;

shared variable ErrCnt : ErrorCountProtectedType;
begin
TestProc1 : process
begin
for i in 1 to 10 loop
ErrCnt.IncErrorCount; wait for 1 ns;
end loop;
write(output, "P1 " & integer'image(ErrCnt.GetErrorCount) & LF);
wait;
end process;
TestProc2 : process
begin
for i in 1 to 10 loop
ErrCnt.IncErrorCount; wait for 2 ns;
end loop;
write(output, "P2 " & integer'image(ErrCnt.GetErrorCount) & LF);
wait;
end process;
end sim;

___________________________

56 Thu May 24 /evtfs/home/tres/vhdl/play>
vsim -c protected_error_counter
Reading /flip/usr1/modeltech/tcl/vsim/pref.tcl

# 6.2a

# vsim -c protected_error_counter

# // ModelSim SE 6.2a Jun 16 2006 Linux 2.6.16.21-0.25-smp
# Loading /flip/usr1/modeltech/linux/../std.standard
# Loading /flip/usr1/modeltech/linux/../std.textio(body)
# Loading work.protected_error_counter(sim)
VSIM 1> run
# P1 16
# P2 20
VSIM 2>
 
P

Paul Uiterlinden

Mike said:
Jim's example runs fine in Modelsim
if I put the protected types in architecture
scope instead of a package:

I usually do it the other way around: put everything, including the
declaration of the protect shared variable itself (not only the type
definition) in a package. This package is then used in all bus functional
models I create. Also the testbench uses the package. This is a very
convenient way to create a global error counter.

The models then will increment this error counter every time an error is
detected. At the end of the simulation, all the testbench has to do is read
the error counter and write out a PASS/FAIL message, thus creating a self
checking testbench.
 
M

Mike Treseler

Paul said:
I usually do it the other way around: put everything, including the
declaration of the protect shared variable itself (not only the type
definition) in a package.

Ahh. So. Of course. I missed the package body.
It works fine fully packaged.
Thanks to Jim and Paul for the tutorial.

-- Mike Treseler
________________________________________

package ErrorCountPkg is
type ErrorCountProtectedType is protected
procedure IncErrorCount;
impure function GetErrorCount return integer;
end protected ErrorCountProtectedType;
shared variable ErrCnt : ErrorCountProtectedType;
end package ErrorCountPkg;

package body ErrorCountPkg is
type ErrorCountProtectedType is protected body
variable ErrCnt : integer := 0;
procedure IncErrorCount is
begin
ErrCnt := ErrCnt + 1;
end procedure IncErrorCount;
impure function GetErrorCount return integer is
begin
return ErrCnt;
end GetErrorCount;
end protected body ErrorCountProtectedType;
end package body ErrorCountPkg;

use std.textio.all;
use work.ErrorCountPkg.all;
entity protected_error_counter is
end entity protected_error_counter;

architecture sim of protected_error_counter is
begin
TestProc1 : process
begin
for i in 1 to 10 loop
ErrCnt.IncErrorCount; wait for 1 ns;
end loop;
write(output, "P1 " & integer'image(ErrCnt.GetErrorCount) & LF);
wait;
end process;
TestProc2 : process
begin
for i in 1 to 10 loop
ErrCnt.IncErrorCount; wait for 2 ns;
end loop;
write(output, "P2 " & integer'image(ErrCnt.GetErrorCount) & LF);
wait;
end process;
end sim;
 

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
473,982
Messages
2,570,186
Members
46,739
Latest member
Clint8040

Latest Threads

Top