Combinational logic can be described with concurrent signal assignments, conditional signal assignments, with-select statements and processes.
To begin with, let us start with an AND function with 4 inputs with four different code versions.
library ieee; use ieee.std_logic_1164.all; entity and_gate is port( a_i : in std_ulogic_vector(3 downto 0); y_o : out std_ulogic); end and_gate; architecture rtl of and_gate is begin y_o <= a_i(3) and a_i(2) and a_i(1) and a_i(0); end architecture rtl;
architecture rtl of and_gate is begin y_o <= '1' when a_i = "1111" else '0'; end architecture rtl;
architecture rtl of and_gate is begin with a_i select y_o <= '1' when "1111", '0' when others; end architecture rtl;
This is a process with a “sensitivity list” which contains “a_i”. When a_i changes, then the process is evaluated and processes the statements in a sequential manner as in a standard programming language. The last assignment for y_o that is hit in the code flow defines the value that is assigned to y_o. y_o does not change the value during the execution of the process. The value is only changed at the end of the process. Note that this “sequential processing” does not mean that something is processed in several steps in time. The result of this hardware description will still be an AND gate.
architecture rtl of and_gate is begin and_p : process(a_i) begin process y_o <= '0'; if a_i = "1111" then y_o <= '1'; end if; end process; end architecture rtl;
Concurrent signal assignments evaluate the expression on the right side of “⇐”. The value after the evaluation of the expression is then assigned to the target signal or output port.
target <= EXPRESSION(signals, input ports);
If the target signal or output port is of type std_ulogic or std_ulogic_vector, the expression can be made with the operators which are defined in the IEEE std_logic_1164 package. That is and, nand, or, nor, xor, xnor, not. The operators are defined for std_ulogic and std_ulogic_vector type signals and ports.
Literals are also expressions, i.e. you can assign '0' or '1' to a target of type std_ulogic. You can assign “1100” to a std_ulogic_vector of length 4.
Example:
architecture rtl of some_circuit is signal a,b,c : std_ulogic; signal d,e : std_ulogic_vector(2 downto 0); begin d <= e; e <= "000"; a <= b or c; end architecture rtl;
The syntax of a conditional signal assignment is
target <= EXPR1 when COND1 else EXPR2 when COND2 ... else EXPRn;
The expressions can be boolean expressions. The conditions can be comparisons. For std_ulogic and std_ulogic_vector the following comparsion operators can check equality or inequality.
Operator | Description |
---|---|
= | Equal |
/= | Not Equal |
Also the logical operators like “and” or “or” can be used for the conditions.
Example:
architecture rtl of some_circuit is signal y : std_ulogic_vector(3 downto 0); signal a,b : std_ulogic_vector(1 downto 0); signal b : std_ulogic; begin y <= "1111" when (a = "11" or b = "01") else "0110" when b = '1' else "1011" when a /= b else "0010"; end architecture rtl;
The syntax for a with-select statement is
architecture rtl of some_circuit is begin with SELECTOR select TARGET <= VALUE1 when CHOICEA [| CHOICE B...], VALUE2 when CHOICEV, VALUE3 when CHOICEK, ... [VALUE4 when others]; end architecture rtl;
Example:
architecture rtl of some_circuit is signal sel : std_ulogic_vector(3 downto 0); signal y : std_ulogic_vector(1 downto 0); begin with sel select y <= "00" when "0001" | "0010" | "0100", "01" when "0000", "10" when others; end architecture rtl;
Processes for combinational circuits can also use variables. The assignment operator for a variable assignment is “:=”. Variables change the value immediately, but the variable only has a scope inside the process. Inside a process the sequential statements like “if..then..else if..else..end if” or “case” or “for” can be used.
The following example code shows the use of a process with a for loop and and if statement to compute the majority function. m is '1' when more than half of the bits of “a” are '1'. Otherwise m is '0'.
architecture rtl of majority_circuit is signal a : std_ulogic_vector(7 downto 0); signal m : std_ulogic; begin maj_p : process(a) variable cnt : integer range 0 to 8; begin process m <= '0'; cnt := 0; for i in 0 to 7 loop if a(i) = '1' then cnt := cnt + 1; end if; end loop; if cnt > 4 then m <= '1'; end if; end process; end architecture rtl;
Output ports or signals of type std_ulogic or std_ulogic_vector can not have multiple drivers. The following code will not work, because there are two drivers for y_o which is of type “std_ulogic”.
architecture rtl of and_gate is begin y_o <= a_i(3) and a_i(2) and a_i(1) and a_i(0); y_o <= '1' when a_i(2) = '0' else '0'; end architecture rtl;
Combinational circuits must always have an assignment of a value when a statement is evaluated. Otherwise the synthesis software will infer an unwanted memory element. That is usually a latch. You must make sure that vhdl code that is supposed to describe combinational logic always assigns a value.
The following code will assign the value of “b” to “y” only when “a” has the value '1'. What should happen when a is '0'? The synthesis will infer a latch to remember the last value when “a” is going from '1' to '0'. This is really bad and will result in timing problems.
architecture rtl of crap_code is signal a,b,y : std_ulogic; begin y <= b when a = '1'; end architecture rtl;