# Moku Library

The Moku library is available to provide some useful types, functions and components to use in your designs. These design elements are defined in the Support package, and can be used in your design like this:

library Moku;
use Moku.Support.clip;

architecture Behavioural of CustomWrapper is
    signal X : signed(31 downto 0);
begin
    -- Multiply results in bit growth to 32 bits
    X <= InputA * InputB;

    -- Use library function to "clip" the value to 16 bits
    OutputA <= clip(X, 15, 0);
end architecture;

# Moku.Support Functions

# sum_no_overflow

sum A and B and clip the result to the range of A instead of wrapping

function sum_no_overflow(A : signed; B : signed) return signed;
function sum_no_overflow(A : signed; B : integer) return signed;
function sum_no_overflow(A : unsigned; B : signed) return unsigned;
function sum_no_overflow(A : unsigned; B : integer) return unsigned;

# clip

Clip A from NewLeft to NewRight and saturate the result if original value exceeds the resulting range

function clip(A : signed; NewLeft, NewRight : integer) return signed;

# clip_val

Clip A between MinVal and MaxVal (inclusive) without resizing the vector.

function clip_val(A : signed; MinVal, MaxVal : integer) return signed;

# or_reduce

Return the result of or'ing all bits in X

function or_reduce(X: std_logic_vector) return std_logic;

# Moku.Support Components

# ScaleOffset

ScaleOffset and ScaleOffset2 are wrappers for a DSP block to compute Z = X * Scale + Offset and Z = (X + Y) * Scale + Offset respectively.

Scale can be up to 18 bits long and covers the range -2^NORMAL_SHIFT -> 2^NORMAL_SHIFT; i.e., ±1 with the default value of NORMAL_SHIFT=0. This means that if you need this block to scale up, then NORMAL_SHIFT must be set greater than 0; for example, if you need to scale up by a factor of 10x then NORMAL_SHIFT=4 gives a range of ±16 then Scale=0.625, or 0x4FFF in signed 16-bits, gives the required scale overall.

OFFSET_SHIFT gives the relative shift between the Offset field and the X argument. This is useful to get the Offset field to the same order as the X * Scale value.

ROUNDING is on by default. Set to 0 to floor the result.

The calculation can be registered at different locations to meet timing constraints. By default, the calculation is registered in the middle only, for a one clock cycle latency. This can be disabled by setting MID_REG=0. On the other hand, long timing paths to and from the block can be registered right at the input and/or output (IN_REG=1 and/or OUT_REG=1) if required. Note that IN_REG and MID_REG are mutually exclusive and trying to set both at once will fail synthesis.

-- Z = X * Scale + Offset
component ScaleOffset
    generic (
        NORMAL_SHIFT : integer := 0;
        OFFSET_SHIFT : integer := 0;
        ROUNDING : boolean := true;
        IN_REG : integer range 0 to 1 := 0;
        OUT_REG : integer range 0 to 1 := 0;
        MID_REG : integer range 0 to 1 := 1
    );
    port (
        Clk : in std_logic;
        Reset : in std_logic;
        X : in signed;
        Scale : in signed;
        Offset : in signed;
        Z : out signed;
        Valid : in std_logic;
        OutValid : out std_logic
    );
end component;

-- Z = (X + Y) * Scale + Offset
component ScaleOffset2
    generic (
        NORMAL_SHIFT : integer := 0;
        OFFSET_SHIFT : integer := 0;
        ROUNDING : boolean := true;
        IN_REG : integer range 0 to 1 := 0;
        OUT_REG : integer range 0 to 1 := 0;
        MID_REG : integer range 0 to 1 := 1
    );
    port (
        Clk : in std_logic;
        Reset : in std_logic;
        X : in signed;
        Y : in signed;
        Scale : in signed;
        Offset : in signed;
        Z : out signed;
        Valid : in std_logic;
        OutValid : out std_logic
    );
end component;

# Interpolator

Linearly interpolate between A and B a distance of N N is normalized between 0 and 1

component Interpolator
    generic (
        OUT_REG : integer range 0 to 1 := 0  -- optional output register
    );
    port (
        Clk : in std_logic;
        Reset : in std_logic;
        A : in signed;  --Point A
        B : in signed; --Point B
        N : in unsigned;  --Distance from A to B to resolve Z
        Z : out signed  --Result
    );
end component;

# Example

library Moku;
use Moku.Support.Interpolator;

architecture Behavioural of CustomWrapper is
    signal A, B : signed(15 downto 0);
    signal N : unsigned(7 downto 0);
    signal Z : signed(15 downto 0);
begin
    -- These can be any dynamic signals
    A <= to_signed(0, 16);
    B <= to_signed(1000, 16);

    -- N is normalized to N'range, so 128 ~= 128 / 2^8 = 0.5
    N <= to_unsigned(128, 8);

    INTERP0: Interpolator
        port map (
            Clk => Clk,
            Reset => Reset,
            A => A,
            B => B,
            N => N,
            Z => Z
        );

    -- Z = (B - A) * 0.5 + A = 500
end architecture;