Guide to VHDL for embedded software developers: Part 3 - ALU logic & FSMs

Peter Wilson

July 25, 2011

Peter Wilson

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.

< Previous
Page 4 of 5
Next >

Loading comments...

Most Commented

Parts Search Datasheets.com

KNOWLEDGE CENTER