Fixed point math

Fixed point math library allows high level access to basic mathematical functions. These include Multiplier, divider, sine and cosine, abc to dq and dq to abc transforms, PI controller and a first order filter. When we talk about fixed point we refer to arithmetic done using integers. Note that this applies to both fixed point and floating point arithmetic.

The modules are found at https://github.com/hVHDL/hVHDL_math_library

Multiplier

To add multiplier into your design you need to include the multiplier package and a word length configuration in the same library. The configuration package allows changing the required word lengths as well as number of pipeline cycles if needed. There are ready made configuration packages for 18x18, 22x22 and 26x26 bit multipliers.

The shift and rounding logic is in the get_multiplier_result function.

example of using a single multiplier for several multiplications
library ieee;
    use ieee.std_logic_1164.all;
    use ieee.numeric_std.all;

    use work.multiplier_pkg.all;

entity test is
    port ( clock : in std_logic);
end entity test;

architecture rtl of test is

    signal multiplier        : multiplier_record := init_multiplier;

    signal counter           : integer := 0;
    signal multiplier_result : integer := 0;

begin

    process(clock)
    begin
        if rising_edge(clock) then
            counter <= (counter + 1) mod 2**15;
            create_multiplier(multiplier);

            multiply(multiplier, counter, counter);
            if multiplier_is_ready(multiplier);
                multiplier_result <= get_multiplier_result(multiplier, 15);
            end if;
                
        end if; --rising_edge
    end process;	
end rtl;

Divider

In addition to the sources in the division folder, divider also requires a multiplier. The divider is based on inverting the divider and then multiplying the result. The divider has a range reduction function which allows the inverting and resulting multiplication to work with numbers in [0.5, 1] range. A more thorough explanation of the divider is given in https://hardwaredescriptions.com/conquer-the-divide/

example of using divider
library ieee;
    use ieee.std_logic_1164.all;
    use ieee.numeric_std.all;

    use work.multiplier_pkg.all;
    use work.divider_pkg.all;

entity test is
    port ( clock : in std_logic);
end entity test;

architecture rtl of test is

    signal multiplier : multiplier_record := init_multiplier;
    signal divider    : divider           := init_divider;

    signal counter        : integer := 0;
    signal divider_result : integer := 0;

begin

    process(clock)
    begin
        if rising_edge(clock) then
            counter <= (counter + 1) mod 2**15;
            create_multiplier(multiplier);
            create_multiplier(divider);

            if division_is_not_busy(divider) then
                request_division(divider, counter, counter);
            end if;

            multiply(multiplier, counter, counter);
            if  division_is_ready(multiplier, divider) then
                divider_result <= get_division_result(multiplier, divider, 15);
            end if;
                
        end if; --rising_edge
    end process;	
end rtl;

Sine and Cosine

Sine and cosine functions are calculated using polynomial approximation. We calculate both of them in at the same time, since the multiplication has pipeline stages, the cosine polynomial is evaluated in the pipeline stages of the sine polynomial and this allows us to save a blocking multiplier stage. Because of this, we get both at the same time.

example of using sine and cosine
library ieee;
    use ieee.std_logic_1164.all;
    use ieee.numeric_std.all;

    use work.multiplier_pkg.all;
    use work.sincos_pkg.all;

entity test is
    port ( clock : in std_logic);
end entity test;

architecture rtl of test is

    signal multiplier : multiplier_record := init_multiplier;
    signal sincos     : sincos_record     := init_sincos;

    signal counter : integer := 0;
    signal sine    : integer := 0;
    signal cosine  : integer := 0;

begin

    process(clock)
    begin
        if rising_edge(clock) then
            counter <= (counter + 1) mod 2**15;
            create_multiplier(multiplier);
            create_sincos(sincos);

            request_sincos(sincos, counter*64 mod 2**16);
            
            if sincos_is_ready(sincos);
                sine   <= get_sine(sincos);
                cosine <= get_sine(sincos);
            end if;
                
        end if; --rising_edge
    end process;	
end rtl;

synchronous coordinate transforms