Timing errors using DSP

When trying to compile a code that uses the ScaleOffset functions, the build fails with an error during the “route” phase, but the bitstream is eventually created. In the timing_summary.rpt it says that timing conditions are not met. However, loading the bitstream to the hardware (Moku:Pro 601) produces the expected results. Should I worry about these timing errors, or should I just ignore them? Below is a minimal working example, I am using this in a larger project.

library IEEE;
use IEEE.Std_Logic_1164.all;
use IEEE.Numeric_Std.all;

library Moku;
use Moku.Support.ScaleOffset;


-- Control0: maximum iterations (13)
-- Control1: precision for the unwrapped phase (20)
-- Control2: phase jump when unwrapping (65535) - bit 16 to reset unwrapped phase to 0
-- Control3: scale I1
-- Control4: offset I1
-- Control5: scale I2
-- Control6: offset I2
-- Control7: scale sin_sig (-65535)
-- Control8: offset sin_sig (2124)
-- Control9: scale cos_sig (65535)
-- Control10: offset cos_sig (0)


architecture Behavioural of CustomWrapper is
	signal i1_sig : signed(15 downto 0); --std_ulogic_vector(15 downto 0);
	signal i2_sig : signed(15 downto 0); --std_ulogic_vector(15 downto 0);
--    signal c_sig : positive;
	signal val_sig : std_ulogic;
	signal ata_sig : std_ulogic_vector(15 downto 0);
    signal phase_sig : std_ulogic_vector(35 downto 0);
    signal precision_sig : positive;
    signal cos_sig : signed(15 downto 0); --std_ulogic_vector(15 downto 0);
    signal sin_sig : signed(15 downto 0); --std_ulogic_vector(15 downto 0);

    component ScaleOffset
        generic (
            NORMAL_SHIFT : integer := 2;
            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;

    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;

begin
    -- Add custom code here

	-- ina_sig <= std_ulogic_vector(InputA);
	-- inb_sig <= std_ulogic_vector(InputB);
    -- c_sig <= positive(to_integer(unsigned(Control0(3 downto 0))));
    precision_sig <= positive(to_integer(unsigned(Control1(5 downto 0))));
	-- outputA <= signed(ata_sig); 
	-- outputB(0) <= val_sig;
    -- outputB <= signed(phase_sig(precision_sig-1 downto precision_sig-16));
    outputC <= cos_sig;
    outputD <= sin_sig;

    -- Z = X * Scale + Offset
    -- Offset is units of bits, scale by default runs from -1 to 1 across whatever signal width is given
    I1: ScaleOffset
        port map (
            Clk => Clk,
            Reset => Reset,
            X => InputA,
            Scale => signed(Control3(17 downto 0)),
            Offset => signed(Control4(17 downto 0)),
            Z => i1_sig,
            Valid => '1',
            OutValid => open
        );

    I2: ScaleOffset
        port map (
            Clk => Clk,
            Reset => Reset,
            X => InputB,
            Scale => signed(Control5(17 downto 0)),
            Offset => signed(Control6(17 downto 0)),
            Z => i2_sig,
            Valid => '1',
            OutValid => open
        );

    sin: ScaleOffset2
        port map (
            Clk => Clk,
            Reset => Reset,
            X => i1_sig,
            Y => i2_sig,
            Scale => signed(Control7(17 downto 0)), --"-1", --to_signed(196608, 18),
            Offset => signed(Control8(17 downto 0)),
            Z => sin_sig,
            Valid => '1',
            OutValid => open
        );
      
    cos: ScaleOffset2
        port map (
            Clk => Clk,
            Reset => Reset,
            X => i1_sig,
            Y => -i2_sig,
            Scale => signed(Control9(17 downto 0)), --to_signed(65536, 18),
            Offset => signed(Control10(17 downto 0)), --"0",
            Z => cos_sig,
            Valid => '1',
            OutValid => open
        );

end architecture;

The build artifacts are at the following link:

https://ncloud.eli-np.ro/index.php/s/KgsfeTuaRrdiyrJ

I just realized that the timing errors occur because the inputs of the ScaleOffset2 blocks need to be the outputs from the two ScaleOffest. I need to apply different scaling to InputA and InputB before adding / subtracting them, is there a way to do this on the FPGA?

Sorry, I should have gone once more through the documentation before posting. It says right there that “The calculation can be registered at different locations to meet timing constraints.”, so I set OUT_REG to 1 for ScaleOffset and IN_REG 1 for ScaleOffset2, and everything is now green.

2 Likes