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

Library UNISIM;
use UNISIM.vcomponents.all;

Library UNIMACRO;
use UNIMACRO.vcomponents.all;

-- Add/Sub 
-- This entity adds or subtracts inputs 'A' and 'B', depending on 'mode' (1 = add, 0 = sub).
-- If 'cap' is high, on Overfolw/Underflow conditions the result is capped at max/min value.

entity addsub is
    generic (
        PIPELINE_STAGES : integer := 1;
        DATA_WIDTH      : integer := 16
    );
    port (
        clk     : in std_logic;
        reset   : in std_logic;
        mode    : in std_logic;
        cap     : in std_logic;
        A       : in std_logic_vector(DATA_WIDTH-1 downto 0);
        B       : in std_logic_vector(DATA_WIDTH-1 downto 0);
        RES     : out std_logic_vector(DATA_WIDTH-1 downto 0)
    );
end entity;

architecture arch of addsub is
    
    --*****SIGNAl DECLARATION
    signal result : std_logic_vector(DATA_WIDTH-1 downto 0) := (others => '0');
    signal carry : std_logic := '0';
    
begin

    ADDSUB_MACRO_inst : ADDSUB_MACRO
        generic map (
            DEVICE  => "7SERIES",       -- Target Device: "VIRTEX5", "7SERIES", "SPARTAN6" 
            LATENCY => PIPELINE_STAGES, -- Desired clock cycle latency, 0-2
            WIDTH   => DATA_WIDTH       -- Input / Output bus width, 1-48
        )
        port map (
            CARRYOUT    => open,    -- 1-bit carry-out output signal
            RESULT      => result,  -- Add/sub result output, width defined by WIDTH generic
            A           => A,       -- Input A bus, width defined by WIDTH generic
            ADD_SUB     => mode,    -- 1-bit add/sub input, high selects add, low selects subtract
            B           => B,       -- Input B bus, width defined by WIDTH generic
            CARRYIN     => '0',     -- 1-bit carry-in input
            CE          => '1',     -- 1-bit clock enable input
            CLK         => clk,     -- 1-bit clock input
            RST         => reset    -- 1-bit active high synchronous reset
        );
    
    clamp : process(all)
    begin
        --DEFAULT VALUE
        RES <= result;
        
        --Overflow/Underflow
        if(carry = '1' and cap = '1') then
            --ADD
            if(mode = '1') then
                --CAP AT MAX VALUE
                RES <= (others => '1');
            --SUB
            else
                --CAP AT ZERO
                RES <= (others => '0');
            end if;    
        end if;
    end process;
    
end architecture;