Differfence in the assignment of a variable to a signal with

F

filmil

Guest
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;
 
filmil wrote:

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
 
Mike Treseler wrote:
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
 
Filip,
Mike Treseler wrote:
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
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
 
Filip Miletic wrote:
Mike Treseler wrote:
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?
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
 
On May 23, 5:29 pm, Jim Lewis <j...@synthworks.com> wrote:
Filip,> Mike Treseler wrote:
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

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
 
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.


Filip Miletic wrote:
Mike Treseler wrote:
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?

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
 
Jim Lewis wrote:
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
 
Mike Treseler wrote:

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>
 
Mike Treseler wrote:

Thanks for the examples of protected types.
Those are hard to find.
This paper gives a good overview of protected shared variables, including
examples: http://www.ececs.uc.edu/~paw/lab/papers/vhdl/ieee-dnt99.pdf.gz

--
Paul Uiterlinden
www.aimvalley.nl
e-mail addres: remove the not.
 
Mike Treseler wrote:

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.

--
Paul Uiterlinden
www.aimvalley.nl
e-mail addres: remove the not.
 
Mike Treseler wrote:
Jim's example runs fine in Modelsim
if I put the protected types in architecture
scope instead of a package:
Paul Uiterlinden wrote:
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;
 

Welcome to EDABoard.com

Sponsor

Back
Top