Industrial manufacturing
Industrial Internet of Things | Industrial materials | Equipment Maintenance and Repair | Industrial programming |
home  MfgRobots >> Industrial manufacturing >  >> Industrial programming >> VHDL

Mastering Signed vs. Unsigned Types in VHDL: Practical Insights

Digital designers rely on precise arithmetic inside FPGAs and ASICs. A core concept is the distinction between signed and unsigned data types in VHDL.

Both types are defined in the numeric_std package from the IEEE library. While many designers still use std_logic_arith, this package is not IEEE‑standard and is not recommended for production designs.

In VHDL, a signed signal is interpreted as a two’s‑complement number, allowing both positive and negative values. An unsigned signal represents only non‑negative values. Internally, the FPGA always uses two’s‑complement for all bit‑vectors, regardless of the type you declare.

BitsUnsigned ValueSigned Value
01133
01022
00111
00000
1117-1
1106-2
1015-3
1004-4

At first glance this mapping can seem counterintuitive, but once you understand two’s‑complement, the behavior is predictable.

It’s important to note that the binary result of an arithmetic operation is identical whether the operands are declared signed or unsigned. What changes is the interpretation of that result.

For example, adding two 5‑bit vectors:

library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;

entity signed_unsigned is
  port (
    i_rst_l : in std_logic;
    i_clk   : in std_logic;
    i_a     : in std_logic_vector(4 downto 0);
    i_b     : in std_logic_vector(4 downto 0)
  );
end signed_unsigned;

architecture behave of signed_unsigned is
  signal rs_SUM_RESULT : signed(4 downto 0)   := (others => '0');
  signal ru_SUM_RESULT : unsigned(4 downto 0) := (others => '0');
  signal rs_SUB_RESULT : signed(4 downto 0)   := (others => '0');
  signal ru_SUB_RESULT : unsigned(4 downto 0) := (others => '0');

begin
  p_SUM : process (i_clk, i_rst_l) begin
    if i_rst_l = '0' then
      rs_SUM_RESULT <= (others => '0');
      ru_SUM_RESULT <= (others => '0');
    elsif rising_edge(i_clk) then
      ru_SUM_RESULT <= unsigned(i_a) + unsigned(i_b);
      rs_SUM_RESULT <= signed(i_a) + signed(i_b);
    end if;
  end process p_SUM;

  p_SUB : process (i_clk, i_rst_l) begin
    if i_rst_l = '0' then
      rs_SUB_RESULT <= (others => '0');
      ru_SUB_RESULT <= (others => '0');
    elsif rising_edge(i_clk) then
      ru_SUB_RESULT <= unsigned(i_a) - unsigned(i_b);
      rs_SUB_RESULT <= signed(i_a) - signed(i_b);
    end if;
  end process p_SUB;

end behave;

Testbench

library ieee;
use ieee.std_logic_1164.all;

entity example_signed_unsigned_tb is
end example_signed_unsigned_tb;

architecture behave of example_signed_unsigned_tb is
  signal r_CLK   : std_logic                    := '0';
  signal r_RST_L : std_logic                    := '0';
  signal r_A     : natural                      := 0;
  signal r_B     : natural                      := 0;
  signal r_A_SLV : std_logic_vector(4 downto 0) := (others => '0');
  signal r_B_SLV : std_logic_vector(4 downto 0) := (others => '0');
  constant c_CLK_PERIOD : time := 10 ns;

  component example_signed_unsigned is
    port (
      i_rst_l : in  std_logic;
      i_clk   : in  std_logic;
      i_a     : in  std_logic_vector(4 downto 0);
      i_b     : in  std_logic_vector(4 downto 0)
    );
  end component example_signed_unsigned;

begin
  i_DUT: example_signed_unsigned
    port map (
      i_rst_l => r_RST_L,
      i_clk   => r_CLK,
      i_a     => r_A_SLV,
      i_b     => r_B_SLV
    );

  clk_gen : process is
  begin
    r_CLK <= '0' after c_CLK_PERIOD/2, '1' after c_CLK_PERIOD;
    wait for c_CLK_PERIOD;
  end process clk_gen;

  process
  begin
    r_RST_L <= '0';
    wait for 20 ns;
    r_RST_L <= '1';
    wait for 20 ns;

    r_A_SLV <= "01001";
    r_B_SLV <= "00110";   
    wait for 20 ns;

    r_A_SLV <= "10001";
    r_B_SLV <= "00110";
    wait for 20 ns;

    r_A_SLV <= "10001";
    r_B_SLV <= "00001";
    wait for 20 ns;

    r_A_SLV <= "10001";
    r_B_SLV <= "00010";
    wait for 20 ns;
    
    r_A_SLV <= "11111";
    r_B_SLV <= "00001";
    wait for 20 ns;

    r_A_SLV <= "00000";
    r_B_SLV <= "00001";
    wait for 20 ns;    
    wait;
  end process;

end behave;
Mastering Signed vs. Unsigned Types in VHDL: Practical Insights

Modelsim simulation wave – Hex values

Mastering Signed vs. Unsigned Types in VHDL: Practical Insights

Modelsim simulation wave – Decimal values

The two screenshots demonstrate that the underlying binary results are identical regardless of type. The difference arises only when the simulator displays the values: in hex the same bit pattern appears, while in decimal signed results may appear negative. Careful type handling is essential when designing FPGA logic.

If any part of this topic remains unclear, please email us via the sidebar contact link. We’re happy to help you master signed and unsigned arithmetic.

Most Popular Nandland Pages

VHDL

  1. Introduction to VHDL: Building Your First AND Gate
  2. Leveraging VHDL Records for Clean, Reusable FIFO Interfaces
  3. VHDL Variables Explained: Practical Examples & Rules for Reliable Design
  4. Mastering VHDL Functions: A Practical Guide to Efficient Design
  5. Using Procedures in VHDL: Simplify Your Design with Reusable Code
  6. Build a Reliable Timer in VHDL: Counting Clock Cycles to Hours
  7. Mastering Signed and Unsigned Types in VHDL: A Practical Guide
  8. Mastering std_logic_vector: Creating Signal Vectors in VHDL
  9. Understanding the Difference Between Signals and Variables in VHDL
  10. What Is VHDL? A Practical Guide to Hardware Description Language