Guide to VHDL for embedded software developers: Part 3 - ALU logic & FSMs
Twos complement
An integral part of subtraction in logic systems is the use of ‘twos complement’.
This enables us to execute a subtraction function using only an adder rather
than requiring a separate subtraction function. Twos complement is an extension
to the basic ones complement (or basic inversion of bits) previously considered.
If we consider an ‘unsigned’ number system based on 4 bits, then the range of the numbers is 0–15 (0000–1111). If we consider a ‘signed’ system, however, the Most Significant Bit (MSB) is considered to be the sign ( or ) of the number system and therefore the range of numbers with 4 bits will instead be from 8 to 7. The method of conversion from positive to negative number in binary logic is a simple two stage process of first inverting all the bits and then adding 1 to the result.
Consider an example. Take a number 00112. In signed number form, the MSB is 0, so the number is positive and the lower three bits 011 can be directly translated into decimal 3. To get the twos complement (3), we first invert all the bits to get 1100, and then add a single bit to get the final twos complement value 1101.
To check that this is indeed the inverse in binary, simply add the number 0011 to its twos complement 1101 and the result should be 0000. This function can be implemented simply in VHDL using the following model:
library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;entity twoscomplement is
generic (
n : integer := 8
);
port (
input : in std_logic_vector((n-1) downto 0);
output : out std_logic_vector((n-1) downto 0));
end;architecture simple of twoscomplement is
begin
process(input)
variable inv : unsigned((n-1) downto 0);
begin
inv := unsigned(NOT input);
inv := inv + 1;
output <= std_logic_vector(inv);
end process;
end;
As can be seen from the VHDL, we operate using logic functions first (NOT) and then convert to unsigned to utilise the addition function (inv 1), and finally convert the result back into a std_logic_vector type. Also notice that the generic n allows this model to be configured for any data size.
In this example, the test bench is used to check that the function is operating correctly by using two test circuits back to back, inverting and re-inverting the input word and checking that the function returns the same value.
While this does not guarantee correct operation (the same bug could
be present in both transfor ms!), it is a simple quick check that is very
useful and makes generation of test data and checks very easy as the input
and final output signal check can be XORd to check for differences:
library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;entity twoscomplementtest is
end twoscomplementtest ;architecture stimulus of twoscomplementtest is
signal rst : std_logic := ‘0’;
signal clk : std_logic:= ‘0’;
signal count : std_logic_vector (7 downto 0);
signal inverse : std_logic_vector (7 downto 0);
signal check : std_logic_vector (7 downto 0);
component twoscomplement
port(
input : in std_logic_vector(7 downto 0);
output : out std_logic_vector(7 downto 0)
);
end component;
for all : twoscomplement use entity
work.twoscomplement ;
begin
CUT1: twoscomplement port map(input => count,
output => inverse);
CUT2: twoscomplement port map(input => inverse,
output => check);-- clock and reset process
clk <= not clk after 1 us;
process
begin
rst <= ‘0’,’1’ after 2.5 us;
wait;
end process;-- generate data
process(clk, rst)
variable tempcount : unsigned(7 downto 0);
begin
if rst = ‘0’ then
tempcount := (others => ‘0’);
elsif rising_edge(clk) then
tempcount := tempcount + 1;
end if;
count <= std_logic_vector(tempcount);
end process;
end;
Whether the designer needs to implement a complete ALU from scratch, or if it is purely to understand the behavior of an existing architecture, these functions are very useful in analyzing the behavior of ALUs and processors.


Loading comments... Write a comment