- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
Hello all
Since the VHDL variables topic strikes as a little bit odd even to VHDL designers who have some experience I would like to ask about VHDL variables in order to clarify a few things. If we have the usual case of register A feeding register B through some combinational logic (registers in same clock domain) then, until now,when I needed to make some complicated combinational code to represent the combinational code between the two registers, I used to do it in another non-clocked process having as inputs the outputs of register stage A and the outputs of the combinational code feeding the input of register stage B. Then inside the clocked process of stage B assign the values of the input. I was wondering, in order to make the code more readable and maintanable, is it possible to use variables to implement the complicated combinational code inside the clocked process so that I do not have to use another non-clocked process? Since I havent found somewhere a clear answer about the use of variables, I wanted to ask, is that the purpose of existence of VHDL variables? Do variables exist in order simplify the writing of combinational code inside a clocked process? Note: If I used signals inside the clocked process to help me store intermediate results,that would lead to register inference which is not intended if the designer wants to implement pure combionational code inside the clocked process.Link Copied
25 Replies
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
--- Quote Start --- Hello all I was wondering, in order to make the code more readable and maintanable, is it possible to use variables to implement the complicated combinational code inside the clocked process so that I do not have to use another non-clocked process? Since I havent found somewhere a clear answer about the use of variables, I wanted to ask, is that the purpose of existence of VHDL variables? Do variables exist in order simplify the writing of combinational code inside a clocked process? Note: If I used signals inside the clocked process to help me store intermediate results,that would lead to register inference which is not intended if the designer wants to implement pure combionational code inside the clocked process. --- Quote End --- That is one of the 'great' uses of variables: splitting up a long series of complicated comb code inside a clocked process over multiple lines. The other use is: instead of declaring globally visible 'signals' which are only used in this process, you can declare variables locally and make them infer registers. You have perfect control whether a variable infers registers or not: if you assign to the variable first before 'reading' it, no registers will be inferred. I made a 'contrived' example:
--variables.vhd
library ieee ;
use ieee.std_logic_1164.all ;
use ieee.numeric_std.all ;
entity variables is
port(
Clk : in std_logic;
Reset : in std_logic;
A : in natural range 0 to 255;
B : in natural range 0 to 255;
C : in natural range 0 to 255;
Y : out natural range 0 to 1023
);
end variables;
architecture arch of variables is
begin
process ( Clk , Reset ) is
variable t1 : natural ; -- combinatorial
variable t2 : natural ; -- registered
begin
if (Reset = '1') then
Y <= 0 ;
elsif rising_edge( Clk ) then
Y <= t2 + C ; -- this will infer registers for t2
if (A > B) then
t1 := A ;
else
t1 := B ;
end if ;
t2 := t1 + B ;
end if;
end process ;
end arch ;
If we run this through the Analysis & Synthesis we see the following in the RTL Viewer: see .pdf attached. You can see the combinatorial logic for 't1' and the inferred registers for 't2'.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
No more to say!
Thanks a lot!- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
Oh. Another question came up though.
What happens when the designer wants to export asynchronously a variable from a clocked process? Declaring a signal and assigning it to the variable inside the clocked process would infer a register... I suppose doing the assignment after the "clocked" IF, ELSEIF(rising edge) would still keep the signal asynchronous...right?- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
You are right but I think it's a matter of readibility and avoiding mistakes.
I find it more easy to read splitting the RTL into a combination process and a sequential process. So, no need for variable. when you need the _next value(comb) right away in the sequential process just use the LVALUE (next) in the decision making. It's always harder to spot errors in simulation using Variables and simulation/synthesis mismatch ocurrs more. YMMV- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
--- Quote Start --- Oh. Another question came up though. What happens when the designer wants to export asynchronously a variable from a clocked process? Declaring a signal and assigning it to the variable inside the clocked process would infer a register... I suppose doing the assignment after the "clocked" IF, ELSEIF(rising edge) would still keep the signal asynchronous...right? --- Quote End --- No. I just modified my 'contrived' example to try this out.
--variables.vhd
library ieee ;
use ieee.std_logic_1164.all ;
use ieee.numeric_std.all ;
entity variables is
port(
Clk : in std_logic;
Reset : in std_logic;
A : in natural range 0 to 255;
B : in natural range 0 to 255;
C : in natural range 0 to 255;
Y : out natural range 0 to 1023;
Y5 : out natural range 0 to 255;
Y2 : out natural range 0 to 255;
Y3 : out natural range 0 to 511;
Y4 : out natural range 0 to 1023
);
end variables;
architecture arch of variables is
begin
process ( Clk , Reset , A, B) is
variable t0 : natural range 0 to 255 ;
variable t1 : natural range 0 to 255 ;
variable t2 : natural range 0 to 511 ;
variable t4 : natural range 0 to 255 ;
begin
if (Reset = '1') then
Y <= 0 ;
t2 := 0 ;
elsif rising_edge( Clk ) then
Y <= t2 + C ;
if (A > B) then
t1 := A ;
t0 := B ;
else
t1 := B ;
t0 := A ;
end if ;
t2 := t0 + B ;
end if;
Y2 <= t1 ;
Y3 <= t2 ;
Y4 <= t1 + t2 ;
if (A > B) then
t4 := A ;
else
t4 := B ;
end if ;
Y5 <= t4 ;
end process ;
end arch ;
In variables3.pdf you can see what happens:
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
Why does t1 become a register?
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
--- Quote Start --- Why does t1 become a register? --- Quote End --- Puzzles me too :) It would have been very nice had it stayed combinatorial. This may be one of the oddities Aprado hinted at. Modelsim may behave differently than Quartus II. Until now I've only used variables as I show in my first code example. (Playing safe)
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
Well it has to do with the involvement of t2. Did you get any warnings? Because t1 now has no reset value.
If you have put the assignment of t4 inside the clocked if (it would still be combinational) and assign it to Y5 outside the clocked if then would Y5 still be combinational? By answering to this question i also solve the asynchronous propagation of my variables outside of the process (as soon as the variables do not interact with clocked variables or signals, as we saw here with t1 and t2)- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
--- Quote Start --- Puzzles me too :) It would have been very nice had it stayed combinatorial. This may be one of the oddities Aprado hinted at. Modelsim may behave differently than Quartus II. Until now I've only used variables as I show in my first code example. (Playing safe) --- Quote End --- t1 is not register. It is y2 and y4 that are registered.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
--- Quote Start --- You are right but I think it's a matter of readibility and avoiding mistakes. I find it more easy to read splitting the RTL into a combination process and a sequential process. So, no need for variable. when you need the _next value(comb) right away in the sequential process just use the LVALUE (next) in the decision making. It's always harder to spot errors in simulation using Variables and simulation/synthesis mismatch ocurrs more. YMMV --- Quote End --- I don't know of any mismatch between tools regarding variables as long as they conform to vhdl standards. One case when I need to use variable is modulo n accumulator when n is not power of 2. For example if I need to add 7 modulo 100. I need to prevent counter going > 99 so I need to use variable to check value of counter and force it:
variable count : integer range 0 to 99 := 0;
begin
...
if count > 99 then
count := count -100;
else
count := count + 7;
end if;
...
if you use signal then count will be > 99 for one sample. Here one cay use if (count + 7) > 99... but I prefer variable
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
--- Quote Start --- t1 is not register. It is y2 and y4 that are registered. --- Quote End --- I think the opposite view is more appropriate. Viewn from the outside of the clock sensitive code, t1 behaves as a register because it's assigned under clock control. The view of variable assignments as "combinational code" inside a clocked process isn't completely true. The behaviour is only combinational for succeeding references to the variable inside the clock sensitive code. It's registered for preceeding references and those not controlled by the same clock edge. This can lead to hardly readable behaviour in some cases. In so far I understand why people want to separate combinational and registered processes and consider multiple clock sensitive events in a process as sacrileg. But if you want to write compact code that keeps functionally related actions close together, these "mixed" processes can serve a purpose. Assigning a variable in the clocked code and reading it in the combinational code, as done in the previous example, is probably not the best idea, because it's "avoidable confusing". The latest integer variable example has implications of synthesis to simulation mismatch, but they are not primarly specific to variables, I think. Exceeding the range of 0 to 99 in simulation causes an exception. To keep the code simulation compatible, the range must be increased (or the code changed).
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
From my observation a variable always means:
(1) combinatorial logic for the given assignment even though it is inside clocked process. Thus t1 := A and B; y <= t1; means just that t1 is output of ANDed A,B without any register(no clock period delay) . y is a register on output of ANDed inputs (2) in the case t1 is read before its assignment e.g. C <= t1; t1 := A AND B; then C is obviously registered. t1 is not but the tool understands t1 to keep its last value which is to be assigned later so a further register is added to save t1 for C assignment leading to two registers (one is named C, the other one on t1 is not known as t1 to the tool since t1 refers to the combinatorial node but this might depend on tool) regarding my example (count integer range 0 to 99), yes it should be 0 to say anything suitable more than 99.- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
The Quartus compiler seems to create a combinational and a registered variant of variable t1 and uses it to fed the different expressions containing t1 respectively. Interestingly, the functionally identical register t1 and D~reg0 are kept separate.
I think, it makes no sense to say variable t1 is not registered. Another example of registered variable usage is the Quartus binary counter template.library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
entity test is
Port ( clk: in std_logic;
A: in STD_LOGIC;
B: in STD_LOGIC;
C : out STD_LOGIC;
D : out STD_LOGIC;
E : out STD_LOGIC);
end test;
architecture rtl of test is
begin
process (clk)
variable t1: std_logic;
begin
if rising_edge(clk) then
C <= t1;
t1 := A and B;
D <= t1;
end if;
E <= t1;
end process;
end rtl;
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
As you an see even though
C <= t1; D <= t1; literally means both are driven by t1 but not exactly in sequential sense. first t1 implied registered version of t1 (or say after process update in software mindset) regarding name of register, the habit is to name it by its output and not by its input and if you search for t1 reg in timequest I doubt you will find it. On the other hand the counter example I gave above gives more insight into behaviour of variables: if counter is variable or signal you end up with same counting logic (adder and register) but the test logic is put on different nodes. For variable count the test logic is on the count node (i.e. input to register). For signal count the test is on count node again (but is now output of register). by the way the statement: count := count + 1; is similar to the case of reading variable before its assignment.- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
The assignment C <= t1 ; reads t1 before t1 is assigned to, hence it infers a register
The assignment D <= t1 ; reads t1 after t1 has been assigned with 'A and B' hence D is fed with the combinatorial The assignment E <= t1 ; also infers a register, but in this case this is not visible as the compiler can re-use the previously inferred register (from C <= t1; ) Splitting the example over the three different assignments shows it better:-- test1------------------------
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
entity test1 is
Port(clk : in std_logic;
A : in STD_LOGIC;
B : in STD_LOGIC;
Y : out STD_LOGIC);
end test1;
architecture rtl of test1 is
begin
process(clk)
variable t1 : std_logic;
begin
if rising_edge(clk) then
Y <= t1; -- read first
t1 := A and B;
end if;
end process;
end rtl;
-- test2------------------------
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
entity test2 is
Port(clk : in std_logic;
A : in STD_LOGIC;
B : in STD_LOGIC;
Y : out STD_LOGIC);
end test2;
architecture rtl of test2 is
begin
process(clk)
variable t1 : std_logic;
begin
if rising_edge(clk) then
t1 := A and B; -- assign first
Y <= t1;
end if;
end process;
end rtl;
-- test3------------------------
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
entity test3 is
Port(clk : in std_logic;
A : in STD_LOGIC;
B : in STD_LOGIC;
Y : out STD_LOGIC;
Yb : out std_logic);
end test3;
architecture rtl of test3 is
begin
process(clk)
variable t1 : std_logic;
begin
if rising_edge(clk) then
t1 := A and B;
Yb <= t1;
end if;
Y <= t1; -- attempt to produce a comb output fails
end process;
end rtl;
-----------------------------
-- all
-----------------------------
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
entity test is
Port(clk : in std_logic;
A : in STD_LOGIC;
B : in STD_LOGIC;
Y1, Y2, Y3, Y3b : out STD_LOGIC);
end test;
architecture rtl of test is
begin
t1 : entity work.test1 port map(clk => clk, A => A, B => B, Y => Y1);
t2 : entity work.test2 port map(clk => clk, A => A, B => B, Y => Y2);
t3 : entity work.test3 port map(clk => clk, A => A, B => B, Y => Y3, Yb => Y3b);
end rtl;
producing this RTL Viewer image:
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
--- Quote Start --- The assignment .... --- Quote End --- Josyb can you please try doing again test3 but now with using another variable t2 to store t1 before setting Yb?
process(clk)
variable t1 : std_logic;
variable t2 : std_logic;
begin
if rising_edge(clk) then
t1 := A and B;
t2 := t1;
Yb <= t1;
end if;
Y <= t2; -- does this attempt fail to export asynchronous t2?
end process;
Can you do this test and provide the RTL diagram again?
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
--- Quote Start --- Can you do this test and provide the RTL diagram again? --- Quote End --- You can try this yourself too, you know? I doesn't make a difference the register called 't1' is now called 't2' :)
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
--- Quote Start --- Josyb can you please try doing again test3 but now with using another variable t2 to store t1 before setting Yb?
process(clk)
variable t1 : std_logic;
variable t2 : std_logic;
begin
if rising_edge(clk) then
t1 := A and B;
t2 := t1;
Yb <= t1;
end if;
Y <= t2; -- does this attempt fail to export asynchronous t2?
end process;
Can you do this test and provide the RTL diagram again? --- Quote End --- t2 will be just wired to t1. The assignment Y <= t2; being outside clock edge is firstly very uncommon. Secondly it implies that t2 drive Y whether t2 is assigned inside clock construct or occurs outside. We know that if clock edge occurs then t2 is assigned but if clock edge is not true then t2 must retains its value and this implies registered version of t2 to drive Y
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
--- Quote Start --- t2 will be just wired to t1. The assignment Y <= t2; being outside clock edge is firstly very uncommon. Secondly it implies that t2 drive Y whether t2 is assigned inside clock construct or occurs outside. We know that if clock edge occurs then t2 is assigned but if clock edge is not true then t2 must retains its value and this implies registered version of t2 to drive Y --- Quote End --- Yes you are right. I tried it too. In general my problem is to be able to create one solid process per logic entity with complex combinational logic that could also be exported out of the process as asynchronous signals to be used for other processes. In order to achieve that I suppose I have to use variables that get assigned outside the clocked if (but also read but not assigned inside the clocked if) and when I need to export them I can assign the variables-to-be-exported to signals but outside the clocked if. Another question is that maybe inside the clocked process I need to make a variable assignment. Is it possible to split the clocked if in two (so have two if(rising edge)) inside the same process and in between make asynchronous variable assignments?
process(clk)
variable t1 : std_logic;
variable t2 : std_logic;
begin
t1 := A and B;
if rising_edge(clk) then
Yb <= t1;
end if;
Y <= t1 XOR Yb;
t2 := A or B;
if rising_edge(clk) then
Yc <= t2 OR Y;
end if;
end process;
Is this mixture possible?
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
--- Quote Start --- The assignment Y <= t2; being outside clock edge is firstly very uncommon --- Quote End --- Whether it is uncommon doesn't matter. It is valid VHDL. (neither QuartusII nor ModelSim complained ...) This method allows you to make a Mealy state machine factoring in the inputs after the clocked process. Very concise, everything in one process. (That does appeal to you, doesn't it?) --- Quote Start --- Secondly it implies that t2 drive Y whether t2 is assigned inside clock construct or occurs outside. We know that if clock edge occurs then t2 is assigned but if clock edge is not true then t2 must retains its value and this implies registered version of t2 to drive Y --- Quote End --- Saying it simpler: "Any output leaving a clocked process (the part between the 'if rising_edge() then' and the matching 'end if ;' or as you call it 'inside the clock edge' ) will be registered". I'm not sure whether all VHDL compilers will generate the same RTL. I couldn't find any specifics on that in the LRM, but I only did a quick text search for variable.

Reply
Topic Options
- Subscribe to RSS Feed
- Mark Topic as New
- Mark Topic as Read
- Float this Topic for Current User
- Bookmark
- Subscribe
- Printer Friendly Page