OpenCores
URL https://opencores.org/ocsvn/viirf/viirf/trunk

Subversion Repositories viirf

[/] [viirf/] [trunk/] [src/] [sos_core_df1_reuse.vhd] - Rev 2

Compare with Previous | Blame | View Log

--------------------------------------------------------------------------------------------------------------
-- MIT License
--  
-- Copyright (c) 2017 Mario Mauerer
--  
-- Permission is hereby granted, free of charge, to any person obtaining a copy
-- of this software and associated documentation files (the "Software"), to deal
-- in the Software without restriction, including without limitation the rights
-- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-- copies of the Software, and to permit persons to whom the Software is
-- furnished to do so, subject to the following conditions:
-- 
-- The above copyright notice and this permission notice shall be included in all
-- copies or substantial portions of the Software.
-- 
-- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-- SOFTWARE.
--
--------------------------------------------------------------------------------------------------------------
-- 
-- VHDL Naming Convention:
-- http://dz.ee.ethz.ch/en/information/hdl-help/vhdl-naming-conventions.html
--------------------------------------------------------------------------------------------------------------
--
-- VIIRF - Versatile IIR Filter
--
-- sos_core_df1_reuse.vhd
--
-- This is the implementation of a direct-form 1 SOS/biquad.
-- It implements the following difference equation:
-- y[n] = g*(b0*x[n] + b1*x[n-1] + b2*x[n-2] - a1*y[n-1] - a2*y[n-2])
--
-- The core utilizes a single multiplier (it is reused), and a state-machine
-- takes care that the multiplier gets supplied with the correct data. This
-- saves resources, but due to the increased logic complexity to feed the
-- multiplier with data, the clock rate is more limited than in the pipelined
-- version of this core (sos_core_df1.vhd).
--
-- The coefficients are provided as signed vectors to this unit.
--
-- Generics/Configuration:
--
-- W_DAT:
--      Data width (input and output) of the core
-- W_COEF:
--      Overall coefficient width.
-- W_FRAC:
--      Fraction length of the coefficients. Together with W_COEF, this defines
--      the Q-notation of the quantized coefficients.
--      E.g., for Q1.15, set W_COEF=16 and W_FRAC=15.
-- SOSGAIN_EN:
--      Boolean that indicates whether the output-gain of the section is
--      enabled/generated, or not.
 
library IEEE;
use IEEE.STD_LOGIC_1164.all;
use IEEE.NUMERIC_STD.all;
 
entity sos_core_df1_reuse is
  generic(
    W_DAT      : integer := 25;
    W_COEF     : integer := 18;
    W_FRAC     : integer := 16;
    SOSGAIN_EN : boolean := true
    );
  port (
    ClkxCI  : in  std_logic;
    RstxRI  : in  std_logic;
    DatxDI  : in  signed(W_DAT-1 downto 0);
    StrbxSI : in  std_logic;
    DatxDO  : out signed(W_DAT-1 downto 0);
    StrbxSO : out std_logic;
    b0xDI   : in  signed(W_COEF-1 downto 0);
    b1xDI   : in  signed(W_COEF-1 downto 0);
    b2xDI   : in  signed(W_COEF-1 downto 0);
    a1xDI   : in  signed(W_COEF-1 downto 0);
    a2xDI   : in  signed(W_COEF-1 downto 0);
    gxDI    : in  signed(W_COEF-1 downto 0)
    );
end sos_core_df1_reuse;
 
architecture Behavioral of sos_core_df1_reuse is
 
-----------------------------------------------------------------------------
-- Components
-----------------------------------------------------------------------------
-- No additional components.
 
-----------------------------------------------------------------------------
-- Signal Declarations
-----------------------------------------------------------------------------
  -------------
  -- Constants:
  -------------
 
  -- Data width after multipliers. This is also the width of the adders:
  constant W_MUL : integer := W_COEF + W_DAT;
 
  -- Values for output saturation. Two's complement min and max values, defined
  -- using bit-masks instead of integers, as W_DAT can be > 32 bit.
  constant SAT_OUT_POS : signed(W_DAT-1 downto 0) := (W_DAT-1 => '0', others => '1');
  constant SAT_OUT_NEG : signed(W_DAT-1 downto 0) := (W_DAT-1 => '1', others => '0');
 
  -----------
  -- Signals:
  -----------
  type STATES is (IDLE, MULACC1, MULACC2, MULACC3, MULACC4, MULACC5, MULACC6, DIVFRAC, SATOUT, GAINOUT, GAINOUT_DIVFRAC, SATGAINOUT);
  signal StatexSN, StatexSP : STATES := IDLE;
 
  -- Main registers for the direct-form 1 implementation:
  signal b0RegxDN, b0RegxDP : signed(W_DAT-1 downto 0) := (others => '0');
  signal b1RegxDN, b1RegxDP : signed(W_DAT-1 downto 0) := (others => '0');
  signal b2RegxDN, b2RegxDP : signed(W_DAT-1 downto 0) := (others => '0');
  signal a1RegxDN, a1RegxDP : signed(W_DAT-1 downto 0) := (others => '0');
  signal a2RegxDN, a2RegxDP : signed(W_DAT-1 downto 0) := (others => '0');
 
  -- The Multiply-Accumulator and right-shifts:
  signal MulxDN, MulxDP                   : signed(W_MUL-1 downto 0) := (others => '0');
  signal AccuxDN, AccuxDP                 : signed(W_MUL-1 downto 0) := (others => '0');
  signal ShiftOutxDN, ShiftOutxDP         : signed(W_MUL-1 downto 0) := (others => '0');
  signal ShiftOutGainxDN, ShiftOutGainxDP : signed(W_MUL-1 downto 0) := (others => '0');
 
  -- Output and output-gain register/signals:
  signal SatRegxDN, SatRegxDP : signed(W_DAT-1 downto 0) := (others => '0');
  signal OutRegxDN, OutRegxDP : signed(W_DAT-1 downto 0) := (others => '0');
  signal StrbxSN, StrbxSP     : std_logic                := '0';
 
  -- Coefficient buffer registers: For reducing routing delay. These registers
  -- are ''static'', i.e., don't change during the core's operation.
  signal b0xDN, b0xDP : signed(W_COEF-1 downto 0) := (others => '0');
  signal b1xDN, b1xDP : signed(W_COEF-1 downto 0) := (others => '0');
  signal b2xDN, b2xDP : signed(W_COEF-1 downto 0) := (others => '0');
  signal a1xDN, a1xDP : signed(W_COEF-1 downto 0) := (others => '0');
  signal a2xDN, a2xDP : signed(W_COEF-1 downto 0) := (others => '0');
  signal gxDN, gxDP   : signed(W_COEF-1 downto 0) := (others => '0');
 
-----------------------------------------------------------------------------
begin  -- Behavioral
-----------------------------------------------------------------------------
 
-----------------------------------------------------------------------------
-- Wiring
-----------------------------------------------------------------------------
  -- Buffer registers for timing relaxation (routing delay):
  b0xDN   <= b0xDI;
  b1xDN   <= b1xDI;
  b2xDN   <= b2xDI;
  a1xDN   <= a1xDI;
  a2xDN   <= a2xDI;
  gxDN    <= gxDI;
  -- Outputs:
  DatxDO  <= OutRegxDP;
  StrbxSO <= StrbxSP;
 
-----------------------------------------------------------------------------
-- Processes
-----------------------------------------------------------------------------
 
  -- The main registers of the direct-form 1 are strobed/forwarded here:
  RegStrb : process(DatxDI, StrbxSI, a1RegxDP, a2RegxDP, b0RegxDP, b1RegxDP,
                    b2RegxDP, satRegxDP)
  begin
    -- Default assignments:
    b0RegxDN <= b0RegxDP;
    b1RegxDN <= b1RegxDP;
    b2RegxDN <= b2RegxDP;
    a1RegxDN <= a1RegxDP;
    a2RegxDN <= a2RegxDP;
    -- These registers are only updated upon a new input strobe:
    if StrbxSI = '1' then
      b0RegxDN <= DatxDI;
      b1RegxDN <= b0RegxDP;
      b2RegxDN <= b1RegxDP;
      a1RegxDN <= satRegxDP;
      a2RegxDN <= a1RegxDP;
    end if;
  end process RegStrb;
 
  -- This calculates the section's new states. It also saturates the output of
  -- the core.
  -- In this implementation, it is implemented as a FSM; the
  -- multiply-accumulator is reused. This saves pyhsical multipliers. 
  DataCalc : process(AccuxDP, MulxDP, OutRegxDP, SatRegxDP, ShiftOutGainxDP,
                     ShiftOutxDP, StatexSP, StrbxSI, a1RegxDP, a1xDP, a2RegxDP,
                     a2xDP, b0RegxDP, b0xDP, b1RegxDP, b1xDP, b2RegxDP, b2xDP,
                     gxDP)
  begin
    -- Default Assignments:
    StatexSN        <= StatexSP;
    StrbxSN         <= '0';
    MulxDN          <= MulxDP;
    AccuxDN         <= AccuxDP;
    ShiftOutxDN     <= ShiftOutxDP;
    ShiftOutGainxDN <= ShiftOutGainxDP;
    SatRegxDN       <= SatRegxDP;
    OutRegxDN       <= OutRegxDP;
 
    case StatexSP is
      when IDLE =>
        AccuxDN <= (others => '0');
        if StrbxSI = '1' then
          StatexSN <= MULACC1;
        end if;
 
      when MULACC1 =>
        MulxDN   <= b0RegxDP * b0xDP;
        StatexSN <= MULACC2;
 
      when MULACC2 =>
        AccuxDN  <= AccuxDP + MulxDP;
        MulxDN   <= b1RegxDP * b1xDP;
        StatexSN <= MULACC3;
 
      when MULACC3 =>
        AccuxDN  <= AccuxDP + MulxDP;
        MulxDN   <= b2RegxDP * b2xDP;
        StatexSN <= MULACC4;
 
      when MULACC4 =>
        AccuxDN  <= AccuxDP + MulxDP;
        MulxDN   <= a1RegxDP * a1xDP;
        StatexSN <= MULACC5;
 
      when MULACC5 =>
        AccuxDN  <= AccuxDP - MulxDP;   -- Negative sign
        MulxDN   <= a2RegxDP * a2xDP;
        StatexSN <= MULACC6;
 
      when MULACC6 =>
        AccuxDN  <= AccuxDP - MulxDP;   -- Negative sign
        StatexSN <= DIVFRAC;
 
      when DIVFRAC =>
        -- Output divider: Get rid of the gain due to the integer algebra (2^W_FRAC):
        ShiftOutxDN <= shift_right(AccuxDP, W_FRAC);
        StatexSN    <= SATOUT;
 
      when SATOUT =>
        -- Check for output overflow and saturate, if necessary:
        -- Set the output-register already here too, in case SOSGAIN_EN is false
        if ShiftOutxDP > SAT_OUT_POS then
          SatRegxDN <= SAT_OUT_POS;
          OutRegxDN <= SAT_OUT_POS;
        elsif ShiftOutxDP < SAT_OUT_NEG then
          SatRegxDN <= SAT_OUT_NEG;
          OutRegxDN <= SAT_OUT_NEG;
        else
          SatRegxDN <= resize(ShiftOutxDP, W_DAT);
          OutRegxDN <= resize(ShiftOutxDP, W_DAT);
        end if;
        -- Check if the output gain has to be applied:
        if SOSGAIN_EN = true then
          StatexSN <= GAINOUT;
        else
          -- Done, OutReg can be output / strobed valid: 
          StatexSN <= IDLE;
          StrbxSN  <= '1';
        end if;
 
      when GAINOUT =>
        -- Reuse the MulAcc from above also here. If timing is a bit more
        -- critical, this could be replaced by an additional multiplier.
        MulxDN   <= SatRegxDP * gxDP;
        StatexSN <= GAINOUT_DIVFRAC;
 
      when GAINOUT_DIVFRAC =>
        ShiftOutGainxDN <= shift_right(MulxDP, W_FRAC);
        StatexSN        <= SATGAINOUT;
 
      when SATGAINOUT =>
        -- Check for output overflow and saturate, if necessary:
        if ShiftOutGainxDP > SAT_OUT_POS then
          OutRegxDN <= SAT_OUT_POS;
        elsif ShiftOutGainxDP < SAT_OUT_NEG then
          OutRegxDN <= SAT_OUT_NEG;
        else
          OutRegxDN <= resize(ShiftOutGainxDP, W_DAT);
        end if;
        -- Done, OutReg can be output / strobed valid: 
        StatexSN <= IDLE;
        StrbxSN  <= '1';
 
      when others =>
        StatexSN <= IDLE;
    end case;
 
  end process DataCalc;
 
  -- The flip-flops for the direct-form 1 implementation:
  FF : process (ClkxCI)
  begin
    if ClkxCI'event and ClkxCI = '1' then
      if RstxRI = '1' then
        StatexSP        <= IDLE;
        b0RegxDP        <= (others => '0');
        b1RegxDP        <= (others => '0');
        b2RegxDP        <= (others => '0');
        a1RegxDP        <= (others => '0');
        a2RegxDP        <= (others => '0');
        MulxDP          <= (others => '0');
        AccuxDP         <= (others => '0');
        ShiftOutxDP     <= (others => '0');
        ShiftOutGainxDP <= (others => '0');
        SatRegxDP       <= (others => '0');
        OutRegxDP       <= (others => '0');
        StrbxSP         <= '0';
        b0xDP           <= (others => '0');
        b1xDP           <= (others => '0');
        b2xDP           <= (others => '0');
        a1xDP           <= (others => '0');
        a2xDP           <= (others => '0');
        gxDP            <= (others => '0');
      else
        StatexSP        <= StatexSN;
        b0RegxDP        <= b0RegxDN;
        b1RegxDP        <= b1RegxDN;
        b2RegxDP        <= b2RegxDN;
        a1RegxDP        <= a1RegxDN;
        a2RegxDP        <= a2RegxDN;
        MulxDP          <= MulxDN;
        AccuxDP         <= AccuxDN;
        ShiftOutxDP     <= ShiftOutxDN;
        ShiftOutGainxDP <= ShiftOutGainxDN;
        SatRegxDP       <= SatRegxDN;
        OutRegxDP       <= OutRegxDN;
        StrbxSP         <= StrbxSN;
        b0xDP           <= b0xDN;
        b1xDP           <= b1xDN;
        b2xDP           <= b2xDN;
        a1xDP           <= a1xDN;
        a2xDP           <= a2xDN;
        gxDP            <= gxDN;
      end if;
    end if;
  end process FF;
 
 
-----------------------------------------------------------------------------
-- Instances
-----------------------------------------------------------------------------
-- No additional instances.
 
 
end Behavioral;
 

Compare with Previous | Blame | View Log

powered by: WebSVN 2.1.0

© copyright 1999-2024 OpenCores.org, equivalent to Oliscience, all rights reserved. OpenCores®, registered trademark.