VHDL - processes, race conditions, & Verilog

T

Taras_96

Hi everyone

I've just started to learn VHDL, for the purpose of synthesising the
code onto an FPGA. I have previously worked with synthesisable Verilog
at RTL, and am trying to get my head around a couple of the mechanisms
VHDL offers. Warning: the following post is a bit long, but I tried to
make myself as clear as possible. My uncertainty involves race
conditions, and how VHDL handles signal assignments inside processes.

A Verilog Example.

always @ (posedge sysclk)
begin
b<=c
end

//other code....

always @ (posedge sysclk)
begin
a<=b
end

The type of assignment above is called 'non-blocking' assignment. The
non-blocking assignment mechanism ensures that your are getting the old
value of 'b', not the new one. Of course, if this mechanism was not
supported, you could ensure those statements which required the
'old'values of 'b' (eg: a<=b) appear before the statements that assign
b its new value (eg: c<=b) - but this means you have to make sure this
always happens, which is time consuming and error prone. The order in
which the two blocks are written doesn't matter. NB: for contrast, the
following does result in a race condition because the order in which
the two blocks execute is not specified:

always @ (posedge sysclk)
begin
a = b
end

always @ (posedge sysclk)
begin
b = c
end

How does VHDL protect against such race conditions?

On a similar topic, how do you model pipelines in VHDL (notice that the
Verilog code given above models a pipeline)? Perhaps something like the
following code...

process (clock)
begin
if clock'event and clock = '1' then
b<=c
end if;
end process

process (clock)
begin
if clock'event and clock = '1' then
a<=b
end if;
end process

I'm not sure what would happen here, because the book I've got (VHDL
for logic synthesis - Andrew Rushton) doesn't have an example of what
the simulator does when two processes are triggered from the same
event. As I understand it, if a process gets triggered, and a signal
assignment is made within the statement, a *transaction* is added to
the queue of the signal that was assigned to. The actual change of the
variable (Rushton describes this as "the point where a transaction
becomes due on a signal, that signal becomes active") occurs when the
process execution phase is finished; it occurs at the beginning of the
event processing phase (and this assignment can cause new processes to
be triggered).

How does the queueing mechanism (of the queued transactions) work?
Does it queue an assignment to take place, using the value of the RHS
at the time the signal assignment was come across, or does it
re-calculate the value to use when the transaction is to be executed in
the event processing stage and then assign that value? The former
option seems to be a more logical explanation to me, in order to
preserve the property that processes themselves are concurrent
(although I'm a bit confused by that as well, for now, I'll assume that
concurrent processes means it doesn't matter where they appear in the
code - see below). If the second option was used (it re-calculates the
value in the event processing stage based on what is stored in the
signal at the time the transaction in the queue is getting processed -
where the signal becomes "active") then the above two processes
wouldn't be concurrent. This is illustrated by an example. If the
transaction for the assignment b<=c was executed first (in the event
processing state), 'b' would get the value of 'c'. 'a' in the
assignment a<=b would then get the 'new' value of 'b' when its
transaction is executed in the event processing stage. Obviously
concurrency is broken - if the assignment a<=b happened before the
assignment b<=c, using this second option of queuing, the processes
wouldn't be concurrent.

If the former option of queueing I mentioned is actually the one used,
the pipeline example would work ('a' would get the 'old' value of 'b')
if *all processes that were triggered in the same delta time period run
to completion*. This would mean that during process execution using
the example above, 'b' would get *scheduled* to be assigned the value
of 'c', and 'a' would be *scheduled* to be assigned the (old) value of
'b'; these assignments will take place in the next event processing
cycle. Is this right? Is this what happens when multiple processes are
triggered at the same time?

What would happen in this case?

process (clock)
begin
if clock'event and clock = '1' then
b<=c
a<=b
end if;
end process

Would it result in similar behaviour to the above case? (Intuitively I
think it should). However, if the first queueing algorithm was used
(where signals get assigned 'old' values - the value of the variable
when it's assignment was encountered in the process execution stage),
wouldn't this result in non-sequential assignment inside the process
block ('b' would be assigned the 'old' value of 'c', 'a' will be
assigned the 'old' value of 'b' - if we switch the order of these
signal assignments around, then the result will be the same, thus the
assignments aren't sequential)?

Now I've confused myself :).

Coming from an RTL for synthesis point of view, is there any
application of the process (who's main feature, as I see it, is that
assignments are done sequentially rather than concurrently), other than
for the purpose of modelling a flip-flop? I seem to remember a book
saying that "processes are concurrent statements" - does this just mean
the position of processes in the code doesn't matter?

Finally, is there a use of variables in synthesisable VHDL at RTL?

Thanks

Taras
 
E

Egbert Molenkamp

Taras_96 said:
Hi everyone

I've just started to learn VHDL, for the purpose of synthesising the
code onto an FPGA. I have previously worked with synthesisable Verilog
at RTL, and am trying to get my head around a couple of the mechanisms
VHDL offers. Warning: the following post is a bit long, but I tried to
make myself as clear as possible. My uncertainty involves race
conditions, and how VHDL handles signal assignments inside processes.


VHDL is rather easy :)

Processes can only communicate with each other with signals (I forget about
shared variables here).

One thing to remember "signals are never updated immediatly"! Simulation
performs to successive steps:
1. execution of the processes (with the frozen signals values!)
2. update of signals values (if no explicit delay is given, this delay is
often called delta delay)
In this way you won't have the order dependent behaviour.

your example:

process (clock)
begin
if clock'event and clock = '1' then
b<=c;
end if;
end process;

process (clock)
begin
if clock'event and clock = '1' then
a<=b;
end if;
end process;


This is exactly the same as:
process (clock)
begin
if clock'event and clock = '1' then
a<=b;
b<=c;
end if;
end process;

And this is exactly the same as (notice the reordening).:
process (clock)
begin
if clock'event and clock = '1' then
b<=c;
a<=b;
end if;
end process;

In all these cases your synthesis tool will realize this with two flipflops;
c is the input of the first FF en the output (b) is connected with de second
FF en that output is equal to a.
(Simulation shows you the same result).

And variables? Variables may only be declared in the sequential part of
language (in a process, function, procedure) therefore a variable can never
be used to communicate between concurrent statements. Variables are always
updated immediatly. If the value of a variable is needed in the next clock
cycle a FF is inserted by the synthesis tool.
process (clock)
variable b : std_logic;
begin
if clock'event and clock = '1' then
a<=b;
b:=c;
end if;
end process;
In this example if "a <= b" is executed the variable b is not assigned a new
value. So the tool will insert a FF to remember the old value.
Result is again two FF (as above).

process (clock)
variable b : std_logic;
begin
if clock'event and clock = '1' then
b:=c;
a<=b;
end if;
end process;
And this? Well 'b' is updated immediatly. That new value is assigned to 'a'.
And after a delta delay 'a' is updated. This results in only 1 FF
c is input and a is output.

Hope this helps,
Egbert Molenkamp
 
R

Ralf Hildebrandt

Egbert Molenkamp wrote:

[some good examples]

Let me add a "rule for the beginner":
Verilog blocking signal assignment ("=") is similar to VHDL variables,
while non-blockling signal assignment ("<=") is similar to VHDL signals.


Ralf
 
J

Jonathan Bromley

I've just started to learn VHDL, for the purpose of synthesising the
code onto an FPGA. I have previously worked with synthesisable Verilog
at RTL, and am trying to get my head around a couple of the mechanisms
VHDL offers.

Luckily you have a pretty good insight into the way Verilog does it,
so you should have absolutely no trouble understanding what VHDL does.
A Verilog Example.

always @ (posedge sysclk)
begin
b<=c
end

//other code....

always @ (posedge sysclk)
begin
a<=b
end

The type of assignment above is called 'non-blocking' assignment.

Indeed. Signal assignment in VHDL behaves somewhat like nonblocking
assignment in Verilog, although there are important differences
(which I'll try to outline later). The equivalent VHDL is
race-free in exactly the same way that NBA assures freedom from
races in Verilog.

[snip commentary on Verilog mechanisms]
always @ (posedge sysclk)
a = b;

always @ (posedge sysclk)
b = c;

How does VHDL protect against such race conditions?

It doesn't have to. VHDL has a perfectly good blocking
assignment mechanism: the := assignment to variables.
However, in VHDL a variable is declared, and visible,
locally to a process and cannot be accessed in any
other process. Consequently, the race you coded
in Verilog cannot occur in VHDL. (There's a caveat:
To allow for certain types of software description,
VHDL has the concept of "shared variables". These
can, indeed, be shared among processes. However,
they are not synthesisable and don't concern us here.)
On a similar topic, how do you model pipelines in VHDL?
Perhaps something like the following code...

process (clock)
begin
if clock'event and clock = '1' then
Please use the much more readable
"if rising_edge(clock) then"
b<=c
end if;
end process

process (clock)
begin
if clock'event and clock = '1' then
a<=b
end if;
end process

I'm not sure what would happen here

Two flip-flops, no races - a pipeline.
As I understand it, if a process gets triggered, and a signal
assignment is made within the statement, a *transaction* is added to
the queue of the signal that was assigned to. The actual change of the
variable (Rushton describes this as "the point where a transaction
becomes due on a signal, that signal becomes active") occurs when the
process execution phase is finished; it occurs at the beginning of the
event processing phase (and this assignment can cause new processes to
be triggered).

How does the queueing mechanism (of the queued transactions) work?
Does it queue an assignment to take place, using the value of the RHS
at the time the signal assignment was come across

Yes. Just like nonblocking assignment in Verilog.
If the [sensible, VHDL]
option of queueing I mentioned is actually the one used,
the pipeline example would work ('a' would get the 'old' value of 'b')
if *all processes that were triggered in the same delta time period run
to completion*. This would mean that during process execution using
the example above, 'b' would get *scheduled* to be assigned the value
of 'c', and 'a' would be *scheduled* to be assigned the (old) value of
'b'; these assignments will take place in the next event processing
cycle. Is this right? Is this what happens when multiple processes are
triggered at the same time?
Yes.

What would happen in this case? [two missing semicolons added]
process (clock)
begin
if clock'event and clock = '1' then
b<=c;
a<=b;
end if;
end process

Would it result in similar behaviour to the above case? (Intuitively I
think it should). However, if the first queueing algorithm was used
(where signals get assigned 'old' values - the value of the variable
when it's assignment was encountered in the process execution stage),
wouldn't this result in non-sequential assignment inside the process
block ('b' would be assigned the 'old' value of 'c', 'a' will be
assigned the 'old' value of 'b' - if we switch the order of these
signal assignments around, then the result will be the same, thus the
assignments aren't sequential)?

Yes and no. The *results* would certainly be the same. The key is
that you are making nonblocking assignment ("signal assignment" in
VHDL-speak) to two DIFFERENT signals, so it doesn't matter in which
order they are executed. Consider, however, the following...

process (clock) begin
if rising_edge(clock) then
a <= '0';
if (some_complicated_condition) then
a <= '1';
end if;
end if;
end process;

The statement "a <= '0'" puts a transaction on to a's queue
for one delta cycle into the future; this transaction will
assign '0' to a. However, if the condition is true, the
second assignment statement "a <= '1'" will also execute,
and it will put a different transaction on to a's queue,
also for one delta cycle into the future.

If you did this in Verilog, both scheduled assignments would
be present on the nonblocking assignment (NBA) queue, and
both would be executed at the moment when NBAs take effect;
however, the Verilog NBA queue preserves the order in which
assignments were executed, and so the '1' assignment would
happen *after* the '0' assignment, and would therefore "win".
In principle there is a zero-width glitch on "a", but in
practice this is unimportant.

In VHDL, the mechanism is somewhat different but (for all
synthesisable code) it has the same effect. Signal assignment
statements place their update transactions on to the queue
using "inertial" delay. In effect, the second assignment
of '1' to "a" will delete all other transactions from the
queue, so that ONLY the later assignment will take effect.
NOTE: this description is somewhat over-simplified, but
for synthesisable code where you always make assignments
with no delay, it is entirely adequate.
Now I've confused myself :)

No need; VHDL's model is far simpler and less confusing
than Verilog's.
Coming from an RTL for synthesis point of view, is there any
application of the process (who's main feature, as I see it, is that
assignments are done sequentially rather than concurrently), other than
for the purpose of modelling a flip-flop? I seem to remember a book
saying that "processes are concurrent statements" - does this just mean
the position of processes in the code doesn't matter?

Finally, is there a use of variables in synthesisable VHDL at RTL?

Many questions, same answer: In processes you can write sequential
code. This brings two major benefits:

(1) you can assign something to a signal, and then change your
mind about the assignment, later in the process. In many
realistic situations this is very convenient.
(2) you can do complicated sequences of calculations, typically
using variables to store the intermediate results so that
they can be used immediately by code later in the process,
and then assign the results of those calculations to a signal.

You are of course right to say that "the position of processes in
the code doesn't matter", in just the same way that the ordering
of "always" blocks in a Verilog module is irrelevant.

Note that VHDL does not possess Verilog's distinction between
nets and registers. You can make continuous assignments in
VHDL just as in Verilog...

Verilog: assign some_wire = some_expression;
VHDL: some_signal <= some_expression;

but in VHDL this is EXACTLY equivalent to the process...

process (all_signals_used_in_expression)
begin
some_signal <= some_expression;
end process;

And there is a big difference between VHDL and Verilog
concerning what happens when two processes write to the
same signal at different times: in Verilog the most
recent update takes effect and all others are ignored;
in VHDL each process independently updates its own
driver on the signal, and the values of all drivers on
the signal are then "resolved" in rather the same way
that the combined results of all continuous assignments
to the same net are resolved in Verilog.

Hope this helps.
--
Jonathan Bromley, Consultant

DOULOS - Developing Design Know-how
VHDL, Verilog, SystemC, Perl, Tcl/Tk, Verification, Project Services

Doulos Ltd. Church Hatch, 22 Market Place, Ringwood, BH24 1AW, UK
Tel: +44 (0)1425 471223 mail:[email protected]
Fax: +44 (0)1425 471573 Web: http://www.doulos.com

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

Mike Treseler

Taras_96 wrote:

[many excellent questions omitted]
Finally, is there a use of variables in synthesisable VHDL at RTL?

Yes.
Process variables allow me to describe
a complex logic operation sequentially.
This type of description is easy for me
to write now and to understand later.

Suppose I want to describe a complex logic
operation, let's say shift left,
followed by a mask inversion,
then add 42 -- all in one clock tick.
With variables (or blocking assignments)
I can just say exactly that in three statements and
synthesis will work out the details very efficiently.
While I could work out the details myself
at the signal, gates and flops level, the
resulting description would likely a require
a simulation run to understand.

Signal assignments (or non-blocking assignments)
handle the job of transferring the needed results to
the entity port or architecture signal.

See
http://www.designabstraction.co.uk/Articles/Advanced Synthesis Techniques.htm
for a good explanation by Ian Lang of variable/blocking assignments
for synthesis with VHDL and verilog examples.

See
http://home.comcast.net/~mike_treseler/uart.vhd
for my take on one of Ian's VHDL examples.


-- Mike Treseler
 
T

Taras_96

Hi everyone

Thanks a lot for your replies, VHDL makes a lot more sense now :). I
do have a few resulting questions; they're a bit late because I had to
think about the contents for a while (warning: long post).

Blocking and blocking assignments in VHDL & Verilog
---------------------------------------------------

I was hoping someone could tell me if the following analysis was
correct, as I'm a bit rusty?

Example 1:

Assume
a = 1
b = 2
c = 3

a<=b; //a is scheduled to be assigned 2
c<=a; //c is scheduled to be assigned 1
b = b + a; //b is assigned 3 - nb: this does not create race
conditions because variable assignments (blocking) are local to the
process. As a side effect, there is no need for other non-blocking
assignments in other processes to run up to this point (contrast with
Verilog below)
f<=b; //f is scheduled to be assigned 3
g<=a; //g is scheduled to be assigned 1 (ie: a<=b hasn't been executed
yet) - correct?

Example 2:
a<=b //a scheduled to be assigned 2
c<=a //c scheduled to be assigned 1
b:=c //b is set to 3
d<=b //d scheduled to be assigned 3

Example 3:
always@(posedge clock)
begin
a<=b;
c<=a;
b = b + a; // the simulator must wait for 5 & 6 to execute before
proceeding - (is this correct?)
f<=b;
end

always@(posedge clock)
begin
g <= b //5
f <= b + a //6
d = c + b //7
end

Sequential Assignment
---------------------------------------------------
The 'sequentiality' comes from being able to do this:
a<=b
a<=c
this wouldn't make sense in concurrent VHDL for synthesis because it
represents:

------b----
|------a---------
------c----
Yes and no. The *results* would certainly be the same. The key is
that you are making nonblocking assignment ("signal assignment" in
VHDL-speak) to two DIFFERENT signals, so it doesn't matter in which
order they are executed.

So are you saying that my code was the same because the signals were
different. However, with something like that shown below, it wouldn't
be the same:

eg 1:
process(clk)
begin
if rising_edge(clk) then
a<=b;
end if;
end process;

process(clk)
begin
if rising_edge(clk) then
if (complicated expression) then
a<=c
end if;
end if;
end process

is NOT the same as eg 2:
process(clk)
begin
if rising_edge(clk) then
a<=b
if (complicated expression) then
a<=c
end if;
end if;
end process;

The first instance requires the value of 'a' to be resolved, while the
second example will just 'overwrite' the value to be assigned to 'a' (I
think).

Concurrent assignments are approximately the same as assignments in
Verilog processes in VHDL corresponds to always blocks in Verilog.
Having said that, I would like to discuss some general design
guidelines, referring to Mike's way.

I think what Mike is suggesting is that, using his example:

Instead of writing (in VHDL pseudocode):

shift_result <= shift(input);
mask_result <= mask_inversion(shift_result);
add_result <= mask_result + 42;
process (clock)
if rising_edge(clock) then
output <= add_result;
end if;
end process;

We write:
process (clock)
shift_result := shift(input);
mask_result := mask_inversion(shift_result);
if rising_edge(clock) then
output <= mask_result+42;
end if;
end process;

What is the advantage of the bottom notation? Or is this more obvious
in more complex examples (admittedly I haven't read the linked article
in depth, in particular the UART example).

The top example maps into Verilog as:

assign shift_result = shift(input);
assign mask_result = mask_inversion(shift_result);
assign sum_result = mask_result + 42;

always@(posedge clock)
begin
output <= sum_result;
end

While the bottom example maps (I think) into Verilog as:
always@(posedge clock)
begin
shift_result = shift(input);
mask_result = mask_inversion(shift_result);
output <= mask_result + 42;
end

I was taught these basic rules for Verilog coding:

1) Do not use complex combinational relationships in
assign values (I didn't know why at first, but I found this quote
on a Usenet board:

"If you wanted the latch or combinational logic. I would decide which
construct to use based on the other features available in continuous
assignments vs. procedural assignments. These are delays and strengths.
You can give strengths to wires and not regs (so you would use the
continuous assignment if you wanted this feature). The delay semantics
for a continuous assignment and a procedural assignment are very
different (see the IEEE1364-1995 spec).)"

2) If you wish to model combinational logic, use an
always@(/*AUTOSENSE*/) block with blocking assignments (AUTOSENSE
automatically generates a complete sensitivity list, thus avoiding
latch inference)

3) If you wish to model synchronous logic, use an always@(posedge
clock) block with non-blocking assignments.

4) Never mix the two blocks:

Some quotes from Usenet:

"In summary then, one should use non-blocking assignments to
synchronous signals and blocking assignments in combinational
constructs. Don't mix them together. "
...."sequential blocks use non-blocking <=
combinational blocks use blocking ="

Now, the bottom example in Verilog, which is what Mike suggests what
variables are 'good' for, mixes both blocking and non-blocking
statements, which breaks guideline 4. Any comments?

Additionally, I'm considering using the following 'template' for the
code I wish to write:

architecture behavioural of blah is
//signal definitions
begin
//do the combinational logic part here
//using concurrent assignments
//assign the result to some 'temporary
//variable' called comb_out for example
process (clock)
begin
if rising_edge(clock) then
//possibly do some muxing here to
//choose the input based on the state
//for example if(reset) then module_out<= 0;
module_out<=comb_out;
end if;
end process;
end behavioural;

Apart from the problems highlighted in the document linked by Mike, is
there any problems with doing this? The additional problem with using
the method suggested in the document linked by Mike is that a fair bit
of trust is put in the synthesiser. Usually this wouldn't be a
problem, but the chip I'm designing, an RSA decryption chip, should be
resistant to 'Timing Analysis' attacks, which means that every
decryption should take *exactly* the same amount of time. This means
that in some instances we may do an add and store it in a dummy
register even if we don't require the results of the add. I'm afraid
that the synthesiser might 'optimise away' this required architecture,
and thus maybe my template is the best approach - any ideas?

Egbert
---------------------------------------------------
On a final note, with Egbert's example, I was initially a bit confused
about why in the bottom example, b:=c synthesises into a flip-flop. My
initial train of thought was "but b can only be assigned to c on the
rising edge of the clock, however the synthesis results tell us that b
is always the value of c!". I think the answer to this is: I remember
reading somewhere (I can't find where now, annoyingly), that variables
retain their values until the next process execution. This means that
while the process isn't executing, b == c, thus, for all time, b == c
(even though the assignment is done within the clocked process)

Thanks again everyone

Taras
 
M

Mike Treseler

Taras_96 said:
I was hoping someone could tell me if the following analysis was
correct, as I'm a bit rusty?

Consider getting a simulator so that
you can work through your own examples.
A simulator is faster and more
accurate than a newsgroup.
I think what Mike is suggesting is that, using his example:

No. I'm afraid you missed the point.

The example was meant to be a small
part of the "update_process_regs" portion
of a synchronous process structured like:

clockedLogic : process (clock, reset) is
if reset = '1' then
init_all_regs_and_ports;
elsif rising_edge(clock) then
init_fast_regs; -- init dynamic registers every clk
update_process_regs;
update_output_ports;
end if;
end process clockedLogic;

the code fragment might look like

reg_v := in_port; -- get a byte
reg_v := shift_left(reg_v, 1 ); -- shift left
reg_v := reg_v xor mask_c; -- mask inversion
reg_v := reg_v + 42; -- add 42

My point was that variable (blocking) assignments
can make code that is very easy to understand compared
to an equivalent schematic or netlist of gates and flops.
3) If you wish to model synchronous logic, use an always@(posedge
clock) block with non-blocking assignments.

This is the conventional wisdom.
I disagree. I prefer to use signal (non-blocking)
assignments _only_ for the purpose of transferring
a variable (register) value to a port or to
another process.

-- Mike Treseler
 
T

Taras_96

Hi Mike

I will try to find a simulator somewhere so I can check the assignments
out (probably a good idea :))

I think I understand the point you were trying to make - blocking
assignments improve the readibility of the code. I suppose blocking
assignments are less 'dangerous' in VHDL because variables are local to
the process. A big reason (I was told, anyway) not to use blocking
assignments in Verilog is to avoid having race conditions.

Thanks for your help

Taras
 

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,743
Messages
2,569,478
Members
44,898
Latest member
BlairH7607

Latest Threads

Top