Module math

This document contains technical documentation for the math module. To browse the source code, please visit the repository on GitHub.

This module contains a contains entities and packages for common math operations in VHDL.

math_pkg.vhd

View source code on GitHub.

Package with some common mathematical functions.

saturate_signed.vhd

View source code on GitHub.

component saturate_signed is
  generic (
    input_width : positive;
    result_width : positive;
    enable_output_register : boolean
  );
  port (
    clk : in std_ulogic;
    --# {{}}
    input_value : in u_signed;
    result_value : out u_signed
  );
end component;

Saturate a signed number by removing MSBs. Typically used in fixed-point applications after performing arithmetic operations, such as addition or multiplication. Or to clamp a value from a third source to a certain range.

In digital arithmetic scenarios, the concept of guard bits is commonly used, and the input value will be of the form:

input_value = S G G G N N N N N N N N

Where S is the sign bit, and G are a guard bits. Note that the number of guard bits, three in the example above, is the difference between input_width and result_width.

Pseudo code

This entity performs the following operation, which is equivalent to a range clamping with power-of-two limits:

min_value = - 2 ** (result_width - 1)
max_value = 2 ** (result_width - 1) - 1
if input_value < min_value:
  return min_value
if input_value > max_value:
  return max_value
return input_value

Fixed-point implementation

The pseudo code above is efficiently implemented in digital logic by looking at the guard bits and the sign bit. If any of them have different value, the input value is outside the result range. If the sign bit is ‘1’, the input is negative, and the result should be greatest negative value possible. If the sign bit is ‘0’, the input is positive, and the result should be the greatest positive value possible.

Note that you have to choose your number of guard bits carefully in any upstream arithmetic operation. If you have too few guard bits, the value might already have wrapped around, and the saturation will not work as expected. This is all dependent on the details of your application.

unsigned_divider.vhd

View source code on GitHub.

component unsigned_divider is
  generic (
    dividend_width : positive;
    divisor_width : positive
  );
  port (
    clk : in std_ulogic;
    --# {{}}
    input_ready : out std_ulogic;
    input_valid : in std_ulogic;
    dividend : in u_unsigned;
    divisor : in u_unsigned;
    --# {{}}
    result_ready : in std_ulogic;
    result_valid : out std_ulogic;
    quotient : out u_unsigned;
    remainder : out u_unsigned
  );
end component;

Calculates

dividend / divisor = quotient + remainder / divisor

This is a bit serial divider. Algorithm is the same as long division from elementary school, but with number base two. Latency scales linearly with dividend_width.