The Case-When statement will cause the program to take one out of multiple different paths, depending on the value of a signal, variable, or expression. It’s a more elegant alternative to an If-Then-Elsif-Else statement with multiple Elsif’s.
Other programming languages have similar constructs, using keywords such as a switch, case, or select. Among other things, Case-When statements are commonly used for implementing multiplexers in VHDL. Continue reading, or watch the video to find out how!
This blog post is part of the Basic VHDL Tutorials series.
The basic syntax for the Case-When statement is:
case <expression> is when <choice> => code for this branch when <choice> => code for this branch ... end case;
The <expression>
is usually a variable or a signal. The Case statement may contain multiple when
choices, but only one choice will be selected.
The <choice>
may be a unique value like "11"
:
when "11" =>
Or it can be a range like 5 to 10
:
when 5 to 10 =>
It can contain several values like 1|3|5
:
when 1|3|5 =>
And most importantly, the others
choice. It is selected whenever no other choice was matched:
when others =>
The others
choice is equivalent to the Else
branch in the If-Then-Elsif-Else statement.
Exercise
In this video tutorial, we will learn how to create a multiplexer using the Case-When statement in VHDL:
The final code we created in this tutorial:
library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
entity T14_CaseWhenTb is
end entity;
architecture sim of T14_CaseWhenTb is
signal Sig1 : unsigned(7 downto 0) := x"AA";
signal Sig2 : unsigned(7 downto 0) := x"BB";
signal Sig3 : unsigned(7 downto 0) := x"CC";
signal Sig4 : unsigned(7 downto 0) := x"DD";
signal Sel : unsigned(1 downto 0) := (others => '0');
signal Output1 : unsigned(7 downto 0);
signal Output2 : unsigned(7 downto 0);
begin
-- Stimuli for the selector signal
process is
begin
wait for 10 ns;
Sel <= Sel + 1;
wait for 10 ns;
Sel <= Sel + 1;
wait for 10 ns;
Sel <= Sel + 1;
wait for 10 ns;
Sel <= Sel + 1;
wait for 10 ns;
Sel <= "UU";
wait;
end process;
-- MUX using if-then-else
process(Sel, Sig1, Sig2, Sig3, Sig4) is
begin
if Sel = "00" then
Output1 <= Sig1;
elsif Sel = "01" then
Output1 <= Sig2;
elsif Sel = "10" then
Output1 <= Sig3;
elsif Sel = "11" then
Output1 <= Sig4;
else -- 'U', 'X', '-' etc.
Output1 <= (others => 'X');
end if;
end process;
-- Equivalent MUX using a case statement
process(Sel, Sig1, Sig2, Sig3, Sig4) is
begin
case Sel is
when "00" =>
Output2 <= Sig1;
when "01" =>
Output2 <= Sig2;
when "10" =>
Output2 <= Sig3;
when "11" =>
Output2 <= Sig4;
when others => -- 'U', 'X', '-', etc.
Output2 <= (others => 'X');
end case;
end process;
end architecture;
The waveform window in ModelSim after we pressed run and zoomed in on the timeline:
The output to the simulator console when we pressed the run button in ModelSim:
VSIM 2> run # ** Warning: NUMERIC_STD."=": metavalue detected, returning FALSE # Time: 50 ns Iteration: 1 Instance: /t14_casewhentb # ** Warning: NUMERIC_STD."=": metavalue detected, returning FALSE # Time: 50 ns Iteration: 1 Instance: /t14_casewhentb # ** Warning: NUMERIC_STD."=": metavalue detected, returning FALSE # Time: 50 ns Iteration: 1 Instance: /t14_casewhentb # ** Warning: NUMERIC_STD."=": metavalue detected, returning FALSE # Time: 50 ns Iteration: 1 Instance: /t14_casewhentb
Analysis
First, we created a process using If-Then-Elsif-Else that would forward one of the signals Sig1
, Sig2
, Sig3
, or Sig4
, based on the value of the selector signal Sel
.
Then we created a process that did exactly the same, using the Case-When statement. We can see from the waveform that the output signals from the two processes, Output1
and Output2
, behave exactly the same.
In our example, the Sel
signal has only four legal values. But if there had been a higher number of possibilities, we can easily see that the Case-When statement can help making code more readable. This is the preferred way of creating such a component by most VHDL designers.
Understanding of the multiplexer was the bonus point of this exercise. Multiplexers, or MUX’s for short, are central components in digital design. It is simply a switch that selects one of several inputs, and forwards it to the output.
This is an illustration of how our MUX forwards the selected input signal:
We used the others
clause to catch all values of Sel
which were not ones or zeros. As we learned in the std_logic tutorial, these signals can have a number of values which are not '0'
or '1'
. It’s good design practice to deal with these values by outputting 'X'
. This indicates an unknown value on this signal, and it will be visible in downstream logic as well.
We can see from the waveform that when the Sel
signal turned red, Output1
and Output2
also changed to "XX"
. This is when others =>
in action.
Additionally, the console output in ModelSim shows a warning because of the Sel
signal being set to "UU"
. The “** Warning: NUMERIC_STD.”=”: metavalue detected, returning FALSE” messages appear at 50 ns simulation time, which is exactly when the signals turn red.
Get free access to the Basic VHDL Course
Download the course material and get started.
You will receive a Zip with exercises for the 23 video lessons as VHDL files where you fill in the blanks, code answers, and a link to the course.
By submitting, you consent to receive marketing emails from VHDLwhiz (unsubscribe anytime).
Takeaway
- Case-When can be used instead of multiple If-Then-Elsif statements
- The
when others =>
can be used to implement a default choice - Multiplexers are preferably created using Case-When statements
Thanks for your tutorials. Quick question:
When a ‘when xxx =>’ block is evaluated, can one and only one choice be evaluated for each scheduled event?
In other words, if something changes in the first ‘when xxx =>’ block that could cause the condition in the second ‘when xxx =>’ block immediately below to be true, does this get ignored until the next event, or will VHDL run through every ‘when xxx =>’ block (like a C switch statement with no ‘break’ between cases)?
Keep up the good work.
Hi Joel,
Interesting question!
The Case statement is not like a C switch, hence there is no ‘break’ keyword. Only one When-branch will be run each time the program reaches the Case statement.
What happens in a situation where the condition (the ‘Sel’ signal) is changed from within a When-branch, depends on the process that the Case is inside of.
If we managed to change the Sel signal inside of the ‘when “10” =>’ for example (even though that would not work because of multiple drivers). Then, the process would immediately run once more because the Sel signal which the process is sensitive to has changed. This time, another When branch would be selected.
If the above algorithm results in an infinite loop, the simulator may produce a warning, or it may freeze or hang. I have seen both happen.
The thousand dollar question is of course what kind of logic this would produce if used in an RTL (production) module. The results can be unpredictable. Therefore you should only use such creative code in testbenches 🙂
Makes sense. Thanks for your time!
Hallo Jonas,
if I have input of 8 bits and i want to use case statment it would be something ugly like this:
CASE input IS
WHEN “000000000” => output output output output <= D;
…
…
WHEN others ………….;
CASE;
I should write 255 lines to cover all cases. Is there a smarter way to manage this?
Is it possible to use operation of the component inside case statement?
For example i have created a component that do some specific operation for certain period of time.
and I want to use the component to get result when the switch is on case2 or case 3.
Please give some idea regarding this.
You can instantiate the component in your VHDL file outside of the process. Instantiations are always in the architecture region, not inside of a process.
Then you can interact with the module instance through signals.
Check out this tutorial where we use a Case-When statement to implement an FSM with a timed delay:
https://vhdlwhiz.com/finite-state-machine/