Signals in VHDL sequential statements - vhdl

I'm new to VHDL. I learned that the sequential statement under the process is always sequential. But in my snippet below, Q will have the old temp value. It seems that it contradicts to the sequential statements since Q will not update to the newest temp value.
process (CLK)
begin
if(rising_edge(CLK)) then
temp <= D;
Q <= temp;
end if;
end
process

The signals temp and Q will be two separate flip-flops. The value of a flip-flop only updates on each rising edge of CLK.
Note that it is not like programming. You are essentially connecting flip-flops, but the their values will not be updated until the next rising edge. With that, the order of the assignements within the process does not matter. (as long as you have only one assignement for each signal)
The scematic in hardware will look something like this. Note, each flip-flop will only update on the next rising edge.
------ -----
|temp| ---> | Q |
------ -----
If you need a different behavour (where temp is not a separate flip-flop), temp needs to be a variable instead of a signal.
I hope that helps.

Related

concurrent and sequential statements in VHDL

I have a fundamental question on VHDL.
Consider the following process:
process(Clk)
begin
if(rising_edge(Clk)) then
a <= data_in;
b <= a;
c <= b;
data_out <= c;
end if;
end process;
The above process acts as a delay register, where data_in is output to data_out after 4 clock cycles.
From my understanding this happens because signals are assigned parallelly. But then why does the statements inside a process called sequential?
For example:
process(Clk)
begin
if(rising_edge(Clk)) then
a <= b or c;
a <= b and c;
end if;
end process;
In the above process the 'a' takes the value from the 2nd statement and I understand, how it works in a sequential way unlike the first process.
Please help.
It's actually very simple: all statements inside a VHDL process are executed sequentially, in order, from top to bottom, no exceptions. However,
the left hand side of a signal assignment operator (<=) does not
take its new value until the process (and all other processes) have
suspended (either hit the bottom or hit a wait statement) and
if you assign to a signal again (as in your second example) the last
assignment executed overwrites the previous ones.
Now you know that, simulate the above two processes in your head and you will see that they behave as you say they will. (The statements in your first example are NOT executed in parallel. But because of (1) above, it seems like they are.)

sequential execution in process statement in vhdl

for process statement in vhdl, it is said that the order of execution inside a process statement is sequential. My question is that, please look at the code below first, are a, b and c signals assigned to their new values concurrently or sequentially in if statement which is in process statement?
process(clk) is
begin
if rising_edge(clk) then
a <= b ;
b <= c ;
c <= a;
end if;
end process;
So if this is sequential, I must say that after the end of process, a is equal to b, b is equal to c and c is equal to b because we assigned b to a before we assigned a to c. However, this does seem not possible for hardware to do.
Constructing a Minimal, Complete, and Verifiable example containing your process:
library ieee;
use ieee.std_logic_1164.all;
entity sequent_exec is
end entity;
architecture foo of sequent_exec is
signal a: std_ulogic := '1';
signal b, c: std_ulogic := '0';
signal clk: std_ulogic := '0';
begin
CLOCK:
process
begin
wait for 10 ns;
clk <= not clk;
if now > 200 ns then
wait;
end if;
end process;
DUT:
process(clk) is
begin
if rising_edge(clk) then
a <= b ;
b <= c ;
c <= a;
end if;
end process;
end architecture;
We see a, b and c shift values from one to another as a recirculating shift register:
Why that occurs is do to how VHDL's simulation cycle operates.
See IEEE Std 1076-2008
10.5 Simple Signal assignments (10.5.1 General):
A signal assignment statement modifies the projected output waveforms contained in the drivers of one or more signals (see 14.7.2), schedules a force for one or more signals, or schedules release of one or more signals (see 14.7.3).
A signal assignment queues a new value for signal update. How the projected output waveform queue is operated is described in 10.5.2.2 Executing a simple assignment statement:
Evaluation of a waveform element produces a single transaction. The time component of the transaction is determined by the current time added to the value of the time expression in the waveform element. For the first form of waveform element, the value component of the transaction is determined by the value expression in the waveform element.
An assignment without a time expression is to the current simulation time. (A delta cycle will occur - a simulation cycle without advancing the simulation time). The sequence of transactions described in
10.5.2.2 tell us old transactions to the same simulation time are deleted.
This means there's only one queue entry for any simulation time and explains why the last assignment to a particular signal is the one resulting in a transaction (and producing an event for a signal a process is sensitive to).
14.7 Execution of a model contains information about how a simulation cycle operates (14.7.5 Model execution).
14.7.5.1 General:
The execution of a model consists of an initialization phase followed by the repetitive execution of process statements in the description of that model. Each such repetition is said to be a simulation cycle. In each cycle, the values of all signals in the description are computed. If as a result of this computation an event occurs on a given signal, process statements that are sensitive to that signal will resume and will be executed as part of the simulation cycle.
14.7.5.3 Simulation cycle describes the simulation cycle, the IEEE Std 1076-1993 is used here for simplicity not being cluttered with VHPI actions:
12.6.4 The simulation cycle
The execution of a model consists of an initialization phase followed by the repetitive execution of process statements in the description of that model. Each such repetition is said to be a simulation cycle. In each cycle, the values of all signals in the description are computed. If as a result of this computation an event occurs on a given signal, process statements that are sensitive to that signal will resume and will be executed as part of the simulation cycle.
At the beginning of initialization, the current time, Tc, is assumed to be 0 ns.
The initialization phase consists of the following steps:
-- The driving value and the effective value of each explicitly declared signal are computed, and the current value of the signal is set to the effective value. This value is assumed to have been the value of the signal for an infinite length of time prior to the start of simulation.
-- The value of each implicit signal of the form S'Stable(T) or S'Quiet(T)is set to True. The value of each implicit signal of the form S'Delayed(T) is set to the initial value of its prefix, S.
-- The value of each implicit GUARD signal is set to the result of evaluating the corresponding guard expression.
-- Each nonpostponed process in the model is executed until it suspends.
-- Each postponed process in the model is executed until it suspends.
-- The time of the next simulation cycle (which in this case is the first simulation cycle), Tn, is calculated according to the rules of step f of the simulation cycle, below.
A simulation cycle consists of the following steps:
a. The current time, Tc is set equal to Tn. Simulation is complete when Tn= TIME'HIGH and there are no active drivers or process resumptions at Tn.
b. Each active explicit signal in the model is updated. (Events may occur on signals as a result.)
c. Each implicit signal in the model is updated. (Events may occur on signals as a result.)
d. For each process P, if P is currently sensitive to a signal S and if an event has occurred on S in this simulation cycle, then P resumes.
e. Each nonpostponed process that has resumed in the current simulation cycle is executed until it suspends.
f. The time of the next simulation cycle, Tn, is determined by setting it to the earliest of
TIME'HIGH,
The next time at which a driver becomes active, or
The next time at which a process resumes.
If Tn = Tc, then the next simulation cycle (if any) will be a delta cycle.
g. If the next simulation cycle will be a delta cycle, the remainder of this step is skipped. Otherwise, each postponed process that has resumed but has not been executed since its last resumption is executed until it suspends. Then Tn is recalculated according to the rules of step f. It is an error if the execution of any postponed process causes a delta cycle to occur immediately after the current simulation cycle.
Signal values don't change during the execution of a process. Their updates are queued and applied in a different step in the execution of a simulation cycle.
back to -2008:
Sequential statements, 10.1 General
The various forms of sequential statements are described in this clause. Sequential statements are used to define algorithms for the execution of a subprogram or process; they execute in the order in which they appear.
We see the order of sequential signal assignment execution doesn't relate to the order signals are updated.
Signals assignments are events that are queued, see the links in the comments. So after evaluation a(new)=b(old), b(new)=c(old), and c(new)=a(old).
If you really want sequential assignment, you can use variables (but preferably don't, because you can easily make a mistake)
process(clk) is
variable i_a, i_b, i_c : [some type];
begin
if rising_edge(clk) then
-- initialize with signal value
i_a := a;
i_b := b;
i_c := c;
--- modify
i_a := i_b;
i_b := i_c;
i_c := i_a;
-- write back to signal
a <= i_a;
b <= i_b;
c <= i_c;
end if;
end process;
Now c(new)=a(new)=b(old) and b(new)=c(old)

Latch signal without delay

I would like to latch a signal, however when I try to do so, I get a delay of one cycle, how can I avoid this?
myLatch: process(wclk, we) -- Can I ommit the we in the sensitivity list?
begin
if wclk'event and wclk = '1' then
lwe <= we;
end if;
end process;
However if I try this and look into the waves during simulation lwe is delayed by one cycle of wclk. All I want tp achieve is to sample we on the rising edge of wclk and keep it stable till the next rising edge. I then assign the latched signal to another entities port map which is defined in the architecture.
==============================================
Well I figured out that I have to omit the wclk'event to get a latch instead of a flip flop. This seems rather unintuitive to me. By simply shortening the time where I sample the signal to be latched I go from latch to flip flop. Can anyone explain why this is and where my perception is wrong. (I am a vhdl beginner)
First off, a few observations on the process you pasted above:
myLatch: process(wclk, we)
begin
if wclk'event and wclk = '1' then
lwe <= we;
end if;
end process;
The signal we can be omitted from the sensitivity list because you have described a clocked process. The only signals required in the sensitivity list of a process like this are the clock and the asynchronous reset if you choose to use one (a synchronous reset would not need to be added to the sensitivity list).
Instead of using if wclk'event and wclk = '1' then you should instead use if rising_edge(wclk) then or if falling_edge(wclk) then, there's a good blog post on the reasons why here.
By omitting the wclk'event you changed the process from a clocked process to a combinatorial process, like so:
myLatch: process(wclk, we)
begin
if wclk = '1' then
lwe <= we;
end if;
end process;
In a combinatorial process all inputs should be present in the sensitivity list, so you would be correct to have both wclk and we in the list as they had an influence on the output. Normally you would ensure that lwe is assigned in all cases of your if statement to avoid inferring a latch, however this appears to be your intention in this case.
Latches in general should be avoided, so if you find yourself needing one you should perhaps pause and consider your approach. Doulos have a couple of articles on latches here and here that you might find useful.
You stated that all you want to achieve is to sample we on the rising edge of wclk and keep it stable until the next rising edge. The process below will accomplish this:
store : process(wclk)
begin
if rising_edge(wclk) then
lwe <= we;
end if;
end process;
With this process, lwe will be updated with the value of we upon every rising edge of wclk and it will remain valid for a single clock cycle.
Let me know if this clears things up for you.
Believe it or not, the issue is actually in your testbench. This has to do with how the VHDL simulation model works.
VHDL is usually used for synchronous hardware design -- that means, using flip-flops that sample on the rising edge and set outputs on the falling edge, so that there are no race conditions between reading and writing. But in VHDL this master/slave logic is not actually simulated using opposite clock edges.
Consider a process
process (clock) begin
if rising_edge(clock) then
a <= b;
end if;
end process;
At the start of a simulation timestep, if clock has just risen, the if will execute. Then the assignment a <= b will be executed, and this will not immediately cause an assignment to take place, but schedule the assignment for the end of the timestep.
After all processes have been run, then all scheduled assignments take place. This means that no process will "see" the new value of a until the next timestep.
Time a b Actions
Start of ts 1 '0' '1' a <= '1' is scheduled
End of ts 1 '1' '0' a <= '1' is executed
Start of ts 2 '1' '0' a <= '0' is scheduled
End of ts 2 '0' '1' a <= '0' is executed
So when you look on the waveform viewer, what you will see is a apparently being set on the rising edge of the clock, and following b delayed by one clock cycle; you don't see the intermediate scheduling of assignments that causes this to happen.
Of course, in real life, there is no "end of the timestep", and the actual changing of signal a happens when the slave part of the flip-flop triggers, ie, on the negative edge. (Maybe it would have been less confusing for VHDL to just use the negative edge; but, oh well, this is how it works).
Here are two testbenches for your latch code:
Test bench 1, using rising edges
Test bench 2, using falling edges
In the first, if you look in the waveform viewer you will see exactly what you describe -- lwe appears to be delayed by 1 clock cycle -- but really, the delay is happening in the non-blocking assignment that sets counter -- so when the rising edge happens, we does not actually have its new value yet. And in the second, you see no such delay; lwe is set exactly on the rising edge to the value of we at that time.
For a related topic in Verilog, see Nonblocking Assignments in Verilog Synthesis, Coding Styles That Kill .
The process you have is what you want according to your description, although 'we' should be removed from the sensitivity list. If this doesn't work as you believe it should it is almost certainly a problem with your test bench/simulation. (See Owen's answer.) Specifically you are probably changing the value of 'we' too late, so that the flip-flop latches the previous value instead of the new one.
I'm interested to know what the source of this signal is though, if it's an asynchronous signal that can change at any time you will have to add some logic to protect against metastability.
To answer your second question about latches, it is correct that omitting wclk'event will result in a latch. This process will not do what you want, however, because it will propagate changes to 'we' to 'lwe' during the whole positive half-period of the clock. The short answer to your question is that implementing this type of behavior requires a latch, while the behavior described by the original process requires a flip-flop.

how are process'es evaluated in practice

I have two process'es like below.
If say A=1, B=2 and C=3, what happens in simulation is on rising_edge B=1 and C=2, which is the result I want.
But am I guaranteed that this is also true when the design is implemented into an fpga?
What worries me is the delay associated with the extra if-state in process BC.
AB : process(A,clk)
begin
if rising_edge(clk) then
B <= A;
end if;
end process;
BC : process(B,clk)
begin
if rising_edge(clk) then
if (some_statement) then
C <= B;
end if;
end if;
end process;
B will take on the value of A (B=1) and C will take on the value of B (C=2).
However, I guess you are not actually describing what you want to. The problem is that you have A and B in the sensitivity list of the two processes. This means that in process AB, B will change each time A changes as well as when rising_edge(clk) is true. The same holds for process BC. Assuming you want to describe two registers in series, your code should be
AB : process(clk)
begin
if rising_edge(clk) then
B <= A;
end if;
end process;
BC : process(clk)
begin
if rising_edge(clk) then
if (some_statement) then
C <= B;
end if;
end if;
end process;
In this case, if you synthesize this code onto an FPGA, you will infer two registers. The register in process BC will use the registers enable signal which is connected to the boolean output of some_statement. If some_statement is already a single std_logic signal, this will not introduce additional delay but require some routing ressources, so you should still avoid using the enable signal where you don't really need it.
I think Simon answered the question perfectly, just to clarify the issue a bit further:
If initial values of your data is A=1, B=2 and C=3 then you will have the following during the simulation:
During start A=1, B=2 and C=3
After first rising edge of the clock A=1, B=1 and C=2
After second rising edge of the clock A=1, B=1 and C=1
After that, all signals will be 1.
The delay of the if statement must be more than 'clock period' - 'hold time necessary for internal registers' to cause any problem for you. Unless you have an extremely complicated logic with signals from multiple clock domains there, there is a little risk you get into problem with your code (the more accurate code is the one sent by Simon).
But am I guaranteed that this is also true when the design is implemented into an fpga?
The synthesizer ought to produce an FPGA which matches the behaviour of your VHDL in the simulator. If not, it's a bug!
Note that there are some "accepted" deviations - for example, if you miss a right-hand side signal off the sensitivity list, the synthesizer will assume you meant to put it there, but the simulator will assume you know what you're doing, and there will be a mismatch. Personally, I regard that behaviour as a bug, but it is too firmly entrenched by too many tools, I don't see it ever changing.
What worries me is the delay associated with the extra if-state in process BC.
Everything in a clocked process like yours "executes" within a single clock tick. If there is too much logic (for example, each nested if introduces a new layer of logic), you may find that a clock tick has to last longer than you desire.
(Not like software on most modern micros, where everything "takes as long as it takes", and is often unpredictable, depending on the state of caches, TLBs etc.)

In VHDL when is the right time to use a Process statement?

I'm going through the phases of learning VHDL for the second or third time now. (this time armed with a very good and free e-book ) and I'm finally starting to "get" quite a bit of it. Now I'm learning about behavioral styles and the process statement and most of it makes sense. However, I've read in many places that processes are to be avoided except for in certain cases. I mean, in theory can't everything be implemented in data-flow instead of behavioral?
When exactly should it be obvious that a process statement should be used?
The process statement is extremely useful, in what situations have you been told not to use them?
There are many different cases where you would use a process statement, I'll outline a few of these below:
One of the most common usages of the process statement (for synthesis) is to describe logic which is synchronous to a clock signal, for example a simple counter that increments every clock cycle when not in reset could be described as:
DATA_REGISTER : process(CLOCK)
begin
if rising_edge(CLOCK) then
if RESET = '1' then
COUNTER <= (others => '0');
else
COUNTER <= COUNTER + 1; --COUNTER is assumed to be of type 'unsigned'
end if;
end if;
end process;
As your designs grow more complex you will inevitably implement a state machine at some point, this will employ one or more processes depending on the style of state machine you choose to implement.
For behavorial code you can use processes in conjunction with wait statements to generate test vectors or to model the behaviour of a real system. Here's a really basic example of a 100MHz clock generator taken from one of my testbenches:
architecture BEH of ethernet_receive_tb is
signal s_clock : std_logic := '0'; --Initial assignment to clock kicks off the process.
begin
CLOCKGEN : process(s_clock)
begin
s_clock <= not s_clock after 5 NS;
end process CLOCKGEN;
...
You can also describe asynchronous logic with processes, in this case you need to include all signals which are read in the process in the sensitivity list and you need to make sure that any outputs are always defined to avoid inferred latches.
IF_ELSE: process (SEL, A, B)
begin
F <= B; -- Default assignment
if SEL = '1' then
F <= A;
end if;
end process;
Hopefully you can see that the process statement is very useful and that you will use it in many different situations. I hope this answered your question!
Process blocks are your friend.
They provide a way of saying "This block of code is related. It's inputs are X,Y,Z and it drives A,B,C". The inputs are documented by the sensitivity list (unless it's a clocked process in which case it should be in your comments). If anything else drives the same signals then you'll get warnings, errors, X's in simulation (depending on your tools). Whatever you get it's pretty obvious.
Personally I would be quite happy writing multiple processes in a single entity, but everyone has their styles. For example, if I have multiple pipe-line stages, each stage is a process. If I have parallel non-interfering paths each will be in a separate process. By doing it this way the code is structured in small, easy to read blocks. Small simple logic synthesizes into small fast blocks (in general).
You could view my style as using them as lightweight entities.
In synthesisable code, processes are required any time you need to keep information from one clock cycle to another. "To store state" in the jargon.
(Note that a process can implied by code such as
d <= q when rising_edge(clk);
)
If non-synthesisable code, processes are useful for getting events to happen in a particular order:
p1: process
begin
data <= "--------";
WE <= '0';
wait until reset = '1';
wait until processor_initialised = '1';
assert ACK = '0' report "ACK should be low!" severity error;
data <= X"16";
WE <= '1';
wait until ACK = '1';
end process;
Most of my code has a single process per entity. Each entity does some useful, well-defined and small-enough-to-be-testable task

Resources