SIGNAL a, b, c, x, z : INTEGER; x <= a + b; z <= c + x; z <= c + x; x <= a + b;(This is where the delta time comes from.) The easiest way to visualize it is to think of the hardware these two statments imply. Try to think of a schematic that implements these two statements:
+-------+ a -----| | | x +-------+ | -+- |---------------| | | b -----| | | | -+- |------ z +-------+ +----| | | | +-------+ c ------------------------+It really does not matter which box (adders) you draw first, we will get the same hardware! There are some things that you have done when writing traditional software, that may not make much sense in describing hardware. Consider the statement x <= x + y. In software, x and y are register locations. The statement just mentioned means we take the content of x add it to y and store the final result back in register x. So assigning to itself is OK in software. But this is not what you think you are specifying in concurrent hardware statements. As a concurrent state, the same line of code is describing an adder with no implied storage registers. Hence we are describing a hardware with feedback around combinational logic! This logic is thus asynchronous and may exibit different behavior depending on the delay of the hardware implementation.
+-------+ y -----| | | x | -+- |----------+ x --+--| | | | | +-------+ | | | +---------------------+So it is important to remember that we are describing hardware using VHDL.
As we have mentioned, all sequential statements are within the PROCESS block. Or in other word, all statements within PROCESS are sequential. The PROCESS block starts with the keyword PROCESS. It has an optional label and a sensitivity list. Following the PROCESS is the statement BEGIN. Every statement between BEGIN and the "END PROCESS label;" are sequential statements. An example is shown here:
mux: PROCESS (a, b, sel) BEGIN IF sel = '1' THEN z <= a; ELSE z <= b; END IF; END PROCESS mux;A process, unlike concurrent statements, does not excute continuously. It is invoked when one of the signals in its sensitivity list changes its value. In other word, when there is an event on any of the signals on the sensitivity list. While each proccess block executes the statements within the block sequentially, multiple processes interact with each other just like concurrent statements - concurrently. Each process executes when there is an event on one of the signals on its sensitivity list causing events to occurs on signals that it assigns to. From the VHDL language construct/syntax point of view, a process must be written within an architecture. However, VHDL allows multiple numbers of processes to be described within the same architecture. For example:
ARCHITECTURE abc OF def IS BEGIN -- ------ concurrent processes p1: PROCESS BEGIN -- -- sequential statements END PROCESS p1; p2: PROCESS BEGIN -- -- sequential statements END PROCESS p2; END abc;
OK, let us look at an example to clearify the concepts we have covered so far. Consider the following two statements:
z <= a AND b; z <= c AND d;Assume z is of a resolving type, if these two statements are not within the PROCESS block, then these two are concurrent assignments. The hardware the describe has two AND gates driving the same output - z. If z is not a resolving data type, there will be an error flagged. However if these two statements are inside the PROCESS blk, these two statements have different behavior. Becasue a PROCESS (as a whole) is considered as a concurrent statement, A process only place one driver on a signal and the final value of the signal is determined by the last assignment within the process. In this case signal z will be assigned with c AND d. To ensure that the signal update is a clean transition, signals assigned to within a process are not actually updated with their new value until the process suspends. Therefore in this example, z will never get assigned with the value of a AND b at any time. let us look at a few more examples:
No-problem Example: ... BEGIN PROCESS BEGIN ... x <= a; -- this assignment is executed before the next one. y <= b; ... END PROCESS; ... Scheduling Example: ... BEGIN PROCESS BEGIN ... x <= a AFTER 10 NS; -- the value of a is scheduled for x after 10ns y <= b AFTER 6 NS; -- the value of b is scheduled for y after 6 ns ... END PROCESS; ... -- first assignment b to y is executed -- then in 4 ns, assignment a to x is executed -- signal y receives the value of b 4 ns sooner than x, even though the scheduling of the former was done after that of the latter. "Trouble" Example: x=`0` ... BEGIN PROCESS BEGIN ... x <= `1`; -- `1` is scheduled for x after delta delay; IF x=`1` THEN -- this statement is executed immediately after the execution of x<=`1` assignment; action_1; ELSE action_2; END IF ... END PROCESS; ... -- Since these two statements (x<=... and IF) are executed during the same simulation cycle (in zero time), the new `1` value of x is not available for the IF. -- Had x been a variable (x:=`1`), its new value, `1`, would be available (action_1 would have been performed).
Sequential statements assigning value to the same signal follow the rules listed here:
-- try to understand sequential assignment -- ENTITY saving_all IS END saving_all; ARCHITECTURE sequential OF saving_all IS TYPE trit IS ('0', '1', 'Z'); SIGNAL xt : trit := 'Z'; SIGNAL yt : trit := 'Z'; SIGNAL zt : trit := 'Z'; SIGNAL wt : trit := 'Z'; SIGNAL xi : trit := 'Z'; SIGNAL yi : trit := 'Z'; SIGNAL zi : trit := 'Z'; SIGNAL wi : trit := 'Z'; SIGNAL xr : trit := 'Z'; SIGNAL yr : trit := 'Z'; SIGNAL zr : trit := 'Z'; SIGNAL wr : trit := 'Z'; BEGIN p1:PROCESS BEGIN xt <= TRANSPORT '1' AFTER 5 NS; xt <= TRANSPORT '0' AFTER 8 NS; WAIT; END PROCESS p1; p2:PROCESS BEGIN yt <= TRANSPORT '1' AFTER 5 NS; yt <= TRANSPORT '0' AFTER 3 NS; WAIT; END PROCESS p2; p3:PROCESS BEGIN zt <= '1' AFTER 5 NS; zt <= TRANSPORT '0' AFTER 8 NS; WAIT; END PROCESS p3; p4:PROCESS BEGIN wt <= '1' AFTER 5 NS; wt <= TRANSPORT '0' AFTER 3 NS; WAIT; END PROCESS p4; p5:PROCESS BEGIN xi <= '1' AFTER 5 NS; xi <= '0' AFTER 8 NS; WAIT; END PROCESS p5; p6:PROCESS BEGIN yi <= '1' AFTER 5 NS; yi <= '0' AFTER 3 NS; WAIT; END PROCESS p6; p7:PROCESS BEGIN zi <= TRANSPORT '1' AFTER 5 NS; zi <= '0' AFTER 8 NS; WAIT; END PROCESS p7; p8:PROCESS BEGIN wi <= TRANSPORT '1' AFTER 5 NS; wi <= '0' AFTER 3 NS; WAIT; END PROCESS p8; p9:PROCESS BEGIN xr <= '1' AFTER 5 NS; xr <= '0' AFTER 10 NS; WAIT; END PROCESS p9; pa:PROCESS BEGIN yr <= '1' AFTER 5 NS; yr <= '0' AFTER 11 NS; WAIT; END PROCESS pa; pb:PROCESS BEGIN zr <= '1' AFTER 5 NS; zr <= REJECT 2 NS INERTIAL '0' AFTER 8 NS; WAIT; END PROCESS pb; pc:PROCESS BEGIN wr <= '1' AFTER 5 NS; wr <= REJECT 5 NS INERTIAL '0' AFTER 8 NS; WAIT; END PROCESS pc; END sequential; -- -- TIME |----SIGNAL NAMES------------------------------------| -- | -- (NS) | Xt Yt Zt Wt Xi Yi Zi Wi Xr Yr Zr Wr -- | -- 0 | 'Z' 'Z' 'Z' 'Z' 'Z' 'Z' 'Z' 'Z' 'Z' 'Z' 'Z' 'Z' -- 1 | 'Z' 'Z' 'Z' 'Z' 'Z' 'Z' 'Z' 'Z' 'Z' 'Z' 'Z' 'Z' -- 2 | 'Z' 'Z' 'Z' 'Z' 'Z' 'Z' 'Z' 'Z' 'Z' 'Z' 'Z' 'Z' -- 3 | 'Z' '0' 'Z' '0' 'Z' '0' 'Z' '0' 'Z' 'Z' 'Z' 'Z' -- 4 | 'Z' '0' 'Z' '0' 'Z' '0' 'Z' '0' 'Z' 'Z' 'Z' 'Z' -- 5 | '1' '0' '1' '0' 'Z' '0' 'Z' '0' 'Z' 'Z' '1' 'Z' -- 6 | '1' '0' '1' '0' 'Z' '0' 'Z' '0' 'Z' 'Z' '1' 'Z' -- 7 | '1' '0' '1' '0' 'Z' '0' 'Z' '0' 'Z' 'Z' '1' 'Z' -- 8 | '0' '0' '0' '0' '0' '0' '0' '0' 'Z' 'Z' '0' '0' -- 9 | '0' '0' '0' '0' '0' '0' '0' '0' 'Z' 'Z' '0' '0' -- 10 | '0' '0' '0' '0' '0' '0' '0' '0' '0' 'Z' '0' '0' -- 11 | '0' '0' '0' '0' '0' '0' '0' '0' '0' '0' '0' '0'
Recall the first example we have given :
-- mux: PROCESS (a, b) mux: PROCESS (a, b, sel) BEGIN IF sel = '1' THEN z <= a; ELSE z <= b; END IF; END PROCESS mux;This code describe the simple 2:1 MUX. If there is an event on a, b, or sel then the PROCESS is active (called). Depending on the value of sel z is assigned with the value of either a or b. If sel is missing from the sensitivity list, the behavior is changed. Since a PROCESS is active only when there is an event on the signals of the sensitivity lists, in this case only when a, or b has an event the value of z is assigned with a value. Therefore given a, b, and sel as follows, the z will change value only when a, b changes their value.
sel 0001111100000000000001111100 a 0111111111110000011111100000 b 1000000000011111000001111111 z 1000000000011111000001100000Convience yourself that the z value is as depicted. In this case z sometimes has the right result and sometimes has the wrong result. This type of error is very difficult to debug. Therefore as a good coding style, one should always include all signals read in the PROCESS on the sensitivity lists. This rule is also adapted by almost all of today's synthesis tool.
EX. 1: In which part of a VHDL code do you have concurrent signal assignemnts? EX. 2: When is a signal updated with its new value as the result of having an assignmenet to it within a process?
As we have mentioned before there are three statements which can be used only in the sequential body of the VHDL code. They are IF THEN, CASE and FOR LOOP.
IF condition THEN -- sequential statements ELSE -- sequential statements END IF;You may have nested IF and ELSE as shown here:
IF condition THEN -- sequential statements ELSIF condition THEN -- sequential statements ELSIF condition THEN -- sequential statements ELSE -- sequential statements END IF;In this structure only one of the branch is executed depending on the condition. Even there are several condition which are true in this structure only the first TRUE condition will be followed. After the execution of the sequential statements within the first true condition the statements after the END IF will be executed next. Therefore it is very import to write the order of IF and ELSIF according to your desired behavior. Here is an example:
PROCESS (a, b, c, x) BEGIN IF (x = "0000") THEN z <= a; ELSEIF (x <= "101") THEN z <= b; ELSE z <= c; END IF; END PROCESS;In this example when x is "0000" z is assigned with value of a even though both conditions are satisfied. Here the second condition's syntax is correct. Recall the comparison of arrays, if two different length arrays are compared, they are aligned from the left. In IF and ELSEIF structure, there is a build in priority.
Here is an exmaple of the syntax:
CASE object IS WHEN value1 => -- statemetns WHEN value2 => -- statemetns WHEN value3 => -- statemetns WHEN value4 => -- statemetns -- etc. END CASE;The case considers all of the possible values that an object can take and execute a different branch depending on the current value of the object. IThis is different from the IF THEN ELSEIF structure. You may also include range and operators such as To and |:
PROCESS (a, b, c, x) BEGIN CASE x IS WHEN 0 TO 4 => z <= b; WHEN 5 => z <= a; WHEN 6 | 9 | 12 => z <= c; WHEN OTHERS => z <= 0; END CASE; END PROCESS;There is also the OTHERS which covers alll left over values not included in the CASE which have not been specified.
As we have mentioned that CASE is quite different from IF and ELSEIF structure. Only one condition can be true in the CASE structure. That is one may not specify two conditions that are both true. For example:
PROCESS (a, b, c, x) BEGIN CASE x IS WHEN 0 TO 4 => z <= b; WHEN 3 => z <= a; WHEN 6 | 9 | 12 => z <= c; WHEN OTHERS => z <= 0; END CASE; END PROCESS;The second condition has been already specifiedaand cannot be included again. Another rule of the CASE structure is that one must specify all possible values of the object. In this case omitting OTHERS is not allowed.
There are couple more things about CASE. First the object can be an expression instead of simple object such as a variable or signal. Second, all possible values in the CASE structure must be constants and they must be of the same type of the object.
Just like any loops in an procedural language, FOR LOOP structure allows the execution of a block of codes iteratively. The LOOP structure should have an index. The syntax is:
FOR i IN 0 To 7 LOOP -- statements END LOOP;In this example the LOOP will execute the statements 8 times. In VHDL, there is no need to declare the loop variable "i". It is implicitly declared! Most of the synthsis tools support the FOR LOOP construct when the range it iterate is fixed. Usually the LOOP body is unroled (unfolded) and logic described by the statements of the LOOP body is duplicated once for each iteration it specified. Therefore in the previous example the logic is duplicated 8 times. Of course once the logic is duplicated, optimization on these newly generated logic is performed. Here is an example which uses the LOOP statement. It loops for 16 times and assignment a value when it suspends. Remember all we have talked about so far on PROCESS and LOOP. Please read the code and think about the behavior it describes.
ENTITY ex_loop IS PORT (a: IN STD_LOGIC_VECTOR(0 TO 15); sel: INTEGER RANGE 0 TO 15; z: OUT STD_LOGIC); END ex_loop; ARCHITECTURE rtl OF ex_loop IS BEGIN ex3 : PROCESS (a, sel) BEGIN FOR i IN 0 TO 15 LOOP IF sel = i then z <= a(i); END IF; END LOOP; END PROCESS ex3; END rtl;Now here is the exercise:
EX.3 : What is the function of the peice of VHDL described above??
Finally, can you write another piece of VHDL code which descibes the same hardware? (Hint: use CASE)
As we have already mentioned that statements within the PROCESS is executed sequentially. The value of a signal is updated only at the time when the PROCESS suspends. We have also mentioned that an Architecture body may have multiple processes. We say that each process can be treated as a single concurrent statement. Therefore when there are multiple processes, there are multple concurrent statements in the architecture. Let us look at an example again:
example: PROCESS (a, b, m) BEGIN y <= a; m <= b; z <= m;
In this example the signal m is first written then read. Let us assume that there is an event on the signal b. When it happneds the PROCESS becomes active and it is called because b is on the sensitivity list. Since the value of m is not updated until the process is suspended. Therefore if we start with values of (a, b, m) = (0, 0, 0) and b changes to 1 and creating an event. The final value of z after the PROCESS suspends is still 0 NOT 1 !! Of course the value of m at the end of the PROCESS is 1 since it is assigned with b. Convince yourself this is true. However, after the PROCESS suspends, there is an event on the signal m now. Therefore the PROCESS is called again and this time z is updated with the value of 1. Here is an example of the delta delay which we have mentioned before. Even though there is no delays associated with each signal the simulator must cycle twice to determine the final values of signals. We will return to this subject again later. For the time being we will discuss briefly the simulation cycle. At a given point of simulation time, there are two queues: one of signals to be updated and one of processes to be executed. When a signal is updated at a specific point in simulation time, all processes that are sensitive to that signal are placed on a "Process Execution" queue. Each process executes in turn, and signals that are assigned to in each process are not updated immediately the process suspends, but are placed on the "signal Update" queue. When all of the processes have executed, then the signals are updated. As a result of the signals being update, further processes may be placed on the process execution queue. One loop around this sequence is known as a "delta cycle".
Now we have look at this delta time concept, let us revisit the simulation process here again. A given point of simulation time, there are two "queues" maintained by the simulator. One of the queue is used to keep track of alll signals to be updated and the other one contains the processes to be executed. When a signal is updated at a specific point in simulation time, all processes that are sensitive to the signal are placed on a "Process Execution" queue. Each process in the queue executes in turn. Signals assigned to in each process are not updated immediately the process usupends but are placed on the "Signal Update" queue. When all the processes have executed then the signals are updated. As a result of signals being updated, further processes may be placed on the process executon queue. One loop around this sequence is known as a "delta cycle". Let me stress again the importance of knowing that the multiple delta cycles we have discussed are heppening at the "same" simulation time. Therefore the full model of VHDL simulation consists of multiple delta cycles at each point of simualtion time. We execute at a single point of simulaiton time until there are no further signal assignements or process executions queud. After there is no more signal assignments, then the simualtion time advances to the next time (resolution) where there are either processes to be executed or signals to be updated. There are only two statements in VHDL that can cause time to advance. One is the signal assignement using the AFTER clause, where the signal assignment is put on a signal update queue at some time in the future instead of at the currrent time. We have seen many example so far on AFTER. The second way for time to advance is when a process is suspended until some time in the future. This can be done with a "WAIT FOR" statement. We will look at the WAIT statement in more detail later.
THE VHDL defines two styles of describing a PROCESS. One is done with sensitivity list and the other is done without a list but with WAIT statement. The one without the senstivity list suspends when it executes a WAIT statement while the one with the list suspends at the end of the PROCESS automatically. Examples:
PROCESS (a, b) BEGIN IF (a='1' OR b='1') THEN z <= '1'; ELSE z <= '0'; END IF; END PROCESS; -- suspends here PROCESS BEGIN IF (a='1' OR b='1') THEN z <= '1'; ELSE z <= '0'; END IF; WAIT ON a, b; -- suspends here END PROCESS;
A PROCESS without a sensitivity list is defined to be an infinite loop. When the execution reaches the bottom (end of the process), it automatically starts to execute again from the begining. It suspends on on WAIT statements. The VHDL language defines that a process with a sensitivity list cannot contain WAIT statements. Therefore it is a shorthand way of writing a PROCESS with a signal WAIT statement at the bottom which waits for an event on one or more of the signals in the sensitivity list of its equivalent. The processes we have shown in the previous example are equivalent. However, for describing combinational logic you are recommended to use the form the contains the sensitivity list as it is accepted by all synthesis tools.
There are four forms of WAIT statements.
This causes the process to re-execute after a specific mount of time has passed. As we have used earlier, this can be useful in writing test benches to create stimulus where signals are required to change value over time.
Example: stimulus: PROCESS BEGIN sel <= '0'; bus_a <= "1111"; bus_b <= "0000"; WAIT FOR 10 ns; sel <= '1'; WAIT FOR 10 ns; -- ..... END PROCESS stimulus;
The WAIT ON statement waits for an event on one or more of the signals in a list specified before re-executing.
PROCESS BEGIN IF (a='1' OR b='1') THEN z <= '1'; ELSE z <= '0'; END IF; WAIT ON a, b; END PROCESS;
The WAIT UNTIL statement has a condition which returns either TRUE or FALSE. The statement waits for an event on one of the signals in the condition, and if as a result of the event the condition has become ture, then the process re-executes.
PROCESS BEGIN WAIT UNTIL clk='1'; q <= d; END PROCESS;
Lastly, it is possible to just use WAIT to suspend a process forever. You may ask, what is the usage of this structure? Well, it can be used in a test bench to make sure your infinite loop feature of the processes from causing your stimulus to repeat once complete.
stimulus: PROCESS BEGIN sel <= '0'; bus_a <= "1111"; bus_b <= "0000"; WAIT FOR 10 ns; sel <= '1'; WAIT; END PROCESS stimulus;
Ex.5. From where in a process does execution commence when a WAIT statement causes the PROCESS to be re-invoked?
We have mentoned several objects in VHDL. One of them is variables. Let us look at variables more closely. We look at variable now because variables are used in sequential part of the VHDL code. That is, a variable in VDL can only be declared and used within a PROCESS. Avariable is different from a signal in that it is assigned its value immediately. just as any procedure languages. This is very important to note. To distinguish the differnce between signals and variables, we use a different assignment symbol ":=" for variables instead of the "<=" used for signals. A variable can only be used in the PROCESS that it is declared. This is known as the "scope" of the variable in programming languages. A variable retains its value between calls to a PROCESS. That is when a PROCESS suspends it retain its variables' values. When the PROCESS is re-invoked it remembers the values created from the previous PROCESS call. So it is possible to imply the behavior of a memory element if required. To prevent any problems with describing combinational logic, always assign a value to your variable before you read its value as the following example:
PROCESS (a, b, c) VARIABLE m, n : INTEGER; BEGIN m := a; n := b; z <= m + n; -- z is assigned with a+b, if the first statement is not there then z = b+c m := c; -- this value of m is retained between calls to this process. y <= m + n; END PROCESS;You will noticed in this example that we have freely assigned signals to variables and variables to signals. It is possible to do this if signals and variables are of a particular type. That is the rules about matching data types still needs to be observed.
Now let us look at how a variabel is used in describing a design in VHDL. Because a variable is updated immediately, variable is commonly used to calculate an "intermediate" value within a process, which is then used later on in the process execution. Since all variables scope is only within the current PROCESS where the variable is declared, it is always necessary to assign the final values of variables to signals if they are used outside of the process. Therefore, we usually use variables when we want to perform complex calculations or we need to implement complex algorithms where it is easier to do them in steps. In these cases, values enter the PROCESS using signals, they are assigned to variables and usd for calculation and algorithm implementation. The final result is assigned back to signals to communicate to the outside. Here is an example where we calcuated the parity of the input data and output the reuslt.
.... -- a is of type std_logic_vector(15 DOWNTO 0) -- z is the output of type std_logic; .... PROCESS (a) VARIABLE tmp : std_logic; BEGIN tmp := '0'; -- FOR i IN a'low TO a'high LOOP FOR i IN 0 TO 15 LOOP tmp := tmp XOR a(i); END LOOP; odd <= tmp; END PROCESS;Note that this design describes a combinational logic implementation of the parity generator. Remember what we said about FOR loop before that this design is unfolded and optimized by the tool when synthesiszed. Now draw the hardware in your mind described by the VHDL code above. In this example we used the ATTRIBUTE of an array. We will come back to that in more detail later.
Now let us review the difference between signals and variables again with an exercise.
Ex 6: Recall the example we had: PROCESS (a, b, c) VARIABLE m, n : INTEGER; BEGIN m := a; n := b; z <= m + n; -- z is assigned with a+b, if the first statement is not there then z = b+c m := c; -- this value of m is retained between calls to this process. y <= m + n; END PROCESS; Now if signals are used instead of variables for m and n we have: ENTITY ... PORT (a, b, c : IN INTEGER); ... ARCHITECTURE SIGNALS m, n : INTEGER; PROCESS (a, b, c, m, n) BEGIN m <= a; n <= b; z <= m + n; -- z is assigned with a+b, if the first statement is not there then z = b+c m <= c; -- this value of m is retained between calls to this process. y <= m + n; END PROCESS; QUESTION: What are the final values of y and z in terms of a, b, c?
Ex.1 : in the architecture body
Ex.2 : When a process suspends
Ex.3 : a 16 to 1 MUX
ENTITY ex_case IS PORT (a: IN STD_LOGIC_VECTOR(0 TO 15); sel: INTEGER RANGE 0 TO 15; z: OUT STD_LOGIC); END ex_loop; ARCHITECTURE rtl OF ex_loop IS BEGIN ex3 : PROCESS (a, sel) BEGIN CASE sel IS WHEN 0 => z <= a(0); WHEN 1 => z <= a(1); WHEN 2 => z <= a(2); WHEN 3 => z <= a(3); WHEN 4 => z <= a(4); WHEN 5 => z <= a(5); WHEN 6 => z <= a(6); WHEN 7 => z <= a(7); WHEN 8 => z <= a(8); WHEN 9 => z <= a(9); WHEN 10 => z <= a(10); WHEN 11 => z <= a(11); WHEN 12 => z <= a(12); WHEN 13 => z <= a(13); WHEN 14 => z <= a(14); WHEN 15 => z <= a(15); END CASE; END PROCESS ex3; END rtl;
Ex.5: The line immediately below the WAIT statement. Ex 6: BOT y and z are c+b.