---------------------------------------------------------------------
--
-- Package body fix_std
-- Fixed point arithmetic based on numeric_std
-- Compile using VHDL-93 (required to support 'ASCENDING
-- array attribute).
--
---------------------------------------------------------------------
--
-- This document describes a VHDL package of definitions and subprograms
-- intended to support the use of fixed-point arithmetic in test benches
-- and synthesisable designs.  This document and the package it describes
-- are made freely available to the HDL design community without warranty
-- of any kind.  You are welcome to make use of this information for your
-- own private study and in your own designs.  If you make use of the
-- package, you accept sole responsibility for its fitness for purpose
-- and its suitability in your application.  You are free to modify the
-- package in any way, and to record your contribution to it, but this
-- notice must appear in a prominent place in any modified or derived
-- version of the package or its supporting documentation.
--
-- The author wishes to thank his employer Doulos Ltd for making
-- available the facilities used to develop, test and distribute this
-- package.  However, Doulos Ltd accepts no responsibility for the
-- package's contents and cannot support it in any way.
--
---------------------------------------------------------------------
-- Revision information:
--
-- Version 0.1  06-May-2003
--   Alpha version, for review
--   No division operators are provided, pending detailed
--   consultation on their functionality.
--   Some subprogram implementations are unnecessarily inefficient.
-- Version 0.2  11-Jun-2003
--   Fixes aiming to make the package portable across all synthesis tools:
--   * Removed deferred constants, made them package constants
--   * Removed all variable initialisations in procedures, made them
--     procedural initialisations in the code
--   * Fixed definition of extract functions to return just one bit
--     if their results would otherwise be null
---------------------------------------------------------------------

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

package body fix_std is

  --------------------------------------------------------------------
  -- DEFERRED CONSTANTS
  --------------------------------------------------------------------
  -- THESE CONSTANTS MAY BE ADJUSTED TO SUIT THE NEEDS OF SPECIFIC
  -- IMPLEMENTATIONS OR APPLICATIONS.
  --------------------------------------------------------------------

  -- Fixed-point offset, the bit number (subscript) used for
  -- the bit in a UFix or SFix vector having binary weight 2^0
  -- (the units bit).
  --
  -- constant FixP : Fix_Subscr := 100;

  -- Default values for overflow and rounding modes:
  --
  -- constant Fix_Default_Rounding : Fix_Rounding_Mode := clip_LS;
  -- constant Fix_Default_Overflow : Fix_Overflow_Mode := clip_MS;

  --------------------------------------------------------------------
  -- END OF DEFERRED CONSTANTS
  --------------------------------------------------------------------

  --------------------------------------------------------------------
  -- IMPLEMENTATION CONSTANTS
  --------------------------------------------------------------------
  -- THESE CONSTANTS MAY BE ADJUSTED TO SUIT THE NEEDS OF SPECIFIC
  -- IMPLEMENTATIONS OR APPLICATIONS.
  --------------------------------------------------------------------

  -- Severity levels for various errors

  -- Concatenation errors - bad sign extension, discontiguous range etc
  -- These are only warnings - they just give the wrong answer.
  --
  constant concat_severity    : severity_level := WARNING;

  -- Subtype direction errors - the present implementation can't
  -- handle fixed-point objects with ascending ranges.  These messages
  -- are intended to give early warning of problems that would
  -- definitely cause other fatal errors at elaboration or run time.
  --
  constant direction_severity : severity_level := FAILURE;

  -- Assertion error message constants

  constant bad_direction_msg : string :=
           " has ascending range";

  constant concat_range_msg  : string :=
           "Concatenation bounds should have contiguous ranges";

  constant concat_sxt_msg    : string :=
           "Concatenation corrupts sign of result";

  constant concat_signLS_msg : string :=
           "RHS of concatenation should be unsigned";

  constant compare_meta_msg  : string :=
           "Metavalue detected in FIX_STD comparison, returning FALSE";

  --------------------------------------------------------------------
  -- END OF IMPLEMENTATION CONSTANTS
  --------------------------------------------------------------------

  --------------------------------------------------------------------
  -- DO NOT MODIFY ANY CODE BELOW THIS POINT.
  --------------------------------------------------------------------

  -- Assumed bit width of INTEGER type in VHDL
  --
  constant INT_WIDTH : positive := 32;

  -- Single-bit zero values
  subtype Izero_UFixT is UFix(FixP downto FixP);
  subtype Izero_SFixT is SFix(FixP downto FixP);
  subtype Fzero_UFixT is UFix(FixP-1 downto FixP-1);
  constant Izero_UFix: Izero_UFixT := "0";
  constant Izero_SFix: Izero_SFixT := "0";
  constant Fzero_UFix: Fzero_UFixT := "0";

  --------------------------------------------------------------------
  -- LOCAL PRIVATE SUBPROGRAMS
  --------------------------------------------------------------------

  -- Min, Max: Find minimum and maximum of two integers.
  -- Useful for determining appropriate range for operation results.
  --
  function Max(L, R: Fix_Subscr) return Fix_Subscr is
  begin
    if L>R then
      return L;
    else
      return R;
    end if;
  end;
  --
  function Min(L, R: Fix_Subscr) return Fix_Subscr is
  begin
    if L<R then
      return L;
    else
      return R;
    end if;
  end;

  -- concat:
  --
  -- This helper function automates the calculation of return
  -- subtype range in concatenation operators "&".
  --
  function concat(L, R: std_logic_vector) return std_logic_vector is
    variable F: std_logic_vector(L'LEFT downto R'RIGHT);
  begin
    if L'LENGTH = 0 then
      return R;
    elsif R'LENGTH = 0 then
      return L;
    else
      assert L'RIGHT = R'LEFT+1
        report concat_range_msg
        severity concat_severity;
      F := L & R;
      return F;
    end if;
  end;
  --
  function concat(L: std_logic; R: std_logic_vector)
  return std_logic_vector is
    variable F: std_logic_vector(R'LEFT+1 downto R'RIGHT);
  begin
    F := L & R;
    return F;
  end;
  --
  function concat(L: std_logic_vector; R: std_ulogic)
  return std_logic_vector is
    variable F: std_logic_vector(L'LEFT downto L'RIGHT-1);
  begin
    F := L & R;
    return F;
  end;

  --------------------------------------------------------------------
  -- IMPLEMENTATIONS OF PUBLIC SUBPROGRAMS
  --------------------------------------------------------------------

  ---------------------------
  -- E.  Extraction functions
  ---------------------------

  -- E.1.  Return the integral part of a UFix value
  function Int(U: UFix) return UFix is
  begin
    if U'LEFT < FixP then
      return Izero_UFix;
    elsif U'RIGHT > FixP then
      return Span(U);
    else
      return U(U'LEFT downto FixP);
    end if;
  end;

  -- E.2.  Return the fractional part of a UFix value
  function Frac(U: UFix) return UFix is
  begin
    if U'LEFT < FixP-1 then
      return Span(U);
    elsif U'RIGHT >= FixP then
      return Fzero_UFix;
    else
      return U(FixP-1 downto U'RIGHT);
    end if;
  end;

  -- E.3.  Return the integral part of a SFix value
  function Int(S: SFix) return SFix is
  begin
    if S'LENGTH = 0 then
      return Izero_SFix;
    elsif S'LEFT <= FixP then
      return SFix'(FixP => S(S'LEFT));
    elsif S'RIGHT > FixP then
      return Span(S);
    else
      return S(S'LEFT downto FixP);
    end if;
  end;

  -- E.4.  Return the fractional part of a SFix value
  function Frac(S: SFix) return UFix is
    variable P: SFix(Max(S'LEFT, FixP) downto Min(S'RIGHT, FixP));
  begin
    if S'LEFT < FixP then
      for I in FixP-1 downto S'LEFT+1 loop
        P(I) := S(S'LEFT);
      end loop;
      P(S'RANGE) := S;
      return UFix(S(FixP-1 downto S'RIGHT));
    elsif S'RIGHT >= FixP then
      return Fzero_UFix;
    else
      return UFix(S(FixP-1 downto S'RIGHT));
    end if;
  end;

  -- E.5.  Widen a SFix value if necessary so that
  --       its range adjoins or spans the binary point
  function Span(S: SFix) return SFix is
    variable F: SFix(Max(S'LEFT, FixP) downto Min(S'RIGHT, FixP));
  begin
    F(S'RANGE) := S;
    for I in F'LEFT downto S'LEFT+1 loop
      F(I) := S(S'LEFT);
    end loop;
    for I in S'RIGHT-1 downto F'RIGHT loop
      F(I) := '0';
    end loop;
    return F;
  end;

  -- E.6.  Widen a UFix value if necessary so that
  --       its range adjoins or spans the binary point
  function Span(U: UFix) return UFix is
    variable F: UFix(Max(U'LEFT, FixP-1) downto Min(U'RIGHT, FixP));
  begin
    F(U'RANGE) := U;
    for I in F'LEFT downto U'LEFT+1 loop
      F(I) := '0';
    end loop;
    for I in U'RIGHT-1 downto F'RIGHT loop
      F(I) := '0';
    end loop;
    return F;
  end;

  ---------------------------
  -- T.  Conversion functions
  ---------------------------

  -- T.1 to T.8: Unsigned conversions

  -- T.1.  Resizing an existing UFix.  Provide number of
  --       integer bits and number of fraction bits.
  --
  function to_UFix(
    N         : UFix;
    MSB_index : Fix_Subscr;
    LSB_index : Fix_Subscr        := FixP;
    overflow  : Fix_Overflow_Mode := Fix_Default_Overflow;
    rounding  : Fix_Rounding_Mode := Fix_Default_Rounding
  ) return UFix is
    variable F: UFix(MSB_index downto LSB_index);
  begin
    Copy_V(F, N, overflow, rounding);
    return F;
  end;

  -- T.2.  Resizing an existing SFix.  Provide number of
  --       integer bits and number of fraction bits.
  --
  function to_UFix(
    N         : SFix;
    MSB_index : Fix_Subscr;
    LSB_index : Fix_Subscr        := FixP;
    overflow  : Fix_Overflow_Mode := Fix_Default_Overflow;
    rounding  : Fix_Rounding_Mode := Fix_Default_Rounding
  ) return UFix is
    variable F: UFix(MSB_index downto LSB_index);
  begin
    Copy_V(F, N, overflow, rounding);
    return F;
  end;

  -- T.3.  Conversion from integer to UFix.  Provide number of
  --       integer bits and number of fraction bits.  Note that
  --       negative input is tolerated!
  --
  function to_UFix(
    N         : integer;
    MSB_index : Fix_Subscr;
    LSB_index : Fix_Subscr        := FixP;
    overflow  : Fix_Overflow_Mode := Fix_Default_Overflow;
    rounding  : Fix_Rounding_Mode := Fix_Default_Rounding
  ) return UFix is
    variable F: UFix(MSB_index downto LSB_index);
  begin
    Copy_V(F, N, overflow, rounding);
    return F;
  end;

  -- T.4.  Conversion from unsigned to UFix.  Provide subscripts of
  --       MS and (optionally) LS bit of the resulting UFix.
  --
  function to_UFix(
    N         : unsigned;
    MSB_index : Fix_Subscr;
    LSB_index : Fix_Subscr        := FixP;
    overflow  : Fix_Overflow_Mode := Fix_Default_Overflow;
    rounding  : Fix_Rounding_Mode := Fix_Default_Rounding
  ) return UFix is
    variable F: UFix(MSB_index downto LSB_index);
  begin
    Copy_V(F, N, overflow, rounding);
    return F;
  end;

  -- T.5.  Conversion from  unsigned to UFix.  Automatically set the
  --       range of the resulting UFix to match the unsigned value,
  --       as an integral UFix value with no fraction bits.
  --
  function to_UFix( N : unsigned ) return UFix is
    variable F: UFix(FixP+N'LENGTH-1 downto FixP);
  begin
    F := UFix(N);
    return F;
  end;

  -- T.6.  Conversion from UFix to unsigned.  All integer bits are
  --       preserved (including adding LS scale bits if necessary).
  --
  function to_unsigned(
    N        : UFix;
    rounding : Fix_Rounding_Mode   := Fix_Default_Rounding
  ) return unsigned is
    variable F_UFix: UFix(N'LEFT downto FixP);
    variable F: unsigned(F_UFix'LENGTH-1 downto 0);
  begin
    Copy_V(F_UFix, N, rounding=>rounding);
    F := unsigned(F_UFix);
    return F;
  end;

  -- T.7.  Conversion from UFix to unsigned.  Caller specifies
  --       required number of bits in unsigned result.
  --
  function to_unsigned(
    N        : UFix;
    bits     : positive;
    overflow : Fix_Overflow_Mode := Fix_Default_Overflow;
    rounding : Fix_Rounding_Mode := Fix_Default_Rounding
  ) return unsigned is
    variable F_UFix: UFix(FixP+bits-1 downto FixP);
    variable F: unsigned(F_UFix'LENGTH-1 downto 0);
  begin
    Copy_V(F_UFix, N, overflow, rounding);
    F := unsigned(F_UFix);
    return F;
  end;

  -- T.8.  Conversion from UFix to integer. Up to 31 integer bits are
  --       preserved (including adding LS scale bits if necessary).
  --
  function to_integer(
    N        : UFix;
    overflow : Fix_Overflow_Mode := Fix_Default_Overflow;
    rounding : Fix_Rounding_Mode := Fix_Default_Rounding
  ) return integer is
    variable F_UFix: UFix(Min(FixP+INT_WIDTH-2, N'LEFT) downto FixP);
  begin
    Copy_V(F_UFix, N, overflow, rounding);
    return to_integer(unsigned(F_UFix));
  end;

  -- T.9 to T.16: Signed conversions

  -- T.9.  Resizing an existing UFix.  Provide number of
  --       integer bits and number of fraction bits.
  --
  function to_SFix(
    N         : UFix;
    MSB_index : Fix_Subscr;
    LSB_index : Fix_Subscr        := FixP;
    overflow  : Fix_Overflow_Mode := Fix_Default_Overflow;
    rounding  : Fix_Rounding_Mode := Fix_Default_Rounding
  ) return SFix is
    variable F: SFix(MSB_index downto LSB_index);
  begin
    Copy_V(F, N, overflow, rounding);
    return F;
  end;

  -- T.10. Resizing an existing SFix.  Provide number of
  --       integer bits and number of fraction bits.
  --
  function to_SFix(
    N         : SFix;
    MSB_index : Fix_Subscr;
    LSB_index : Fix_Subscr        := FixP;
    overflow  : Fix_Overflow_Mode := Fix_Default_Overflow;
    rounding  : Fix_Rounding_Mode := Fix_Default_Rounding
  ) return SFix is
    variable F: SFix(MSB_index downto LSB_index);
  begin
    Copy_V(F, N, overflow, rounding);
    return F;
  end;

  -- T.11. Conversion from integer to SFix.  Provide number of
  --       integer bits and number of fraction bits.
  --
  function to_SFix(
    N         : integer;
    MSB_index : Fix_Subscr;
    LSB_index : Fix_Subscr        := FixP;
    overflow  : Fix_Overflow_Mode := Fix_Default_Overflow;
    rounding  : Fix_Rounding_Mode := Fix_Default_Rounding
  ) return SFix is
    variable F: SFix(MSB_index downto LSB_index);
  begin
    Copy_V(F, N, overflow, rounding);
    return F;
  end;

  -- T.12. Conversion from signed to SFix.  Provide subscripts of
  --       MS and (optionally) LS bit of the resulting SFix.
  --
  function to_SFix(
    N         : signed;
    MSB_index : Fix_Subscr;
    LSB_index : Fix_Subscr        := FixP;
    overflow  : Fix_Overflow_Mode := Fix_Default_Overflow;
    rounding  : Fix_Rounding_Mode := Fix_Default_Rounding
  ) return SFix is
    variable F: SFix(MSB_index downto LSB_index);
  begin
    Copy_V(F, N, overflow, rounding);
    return F;
  end;

  -- T.13. Conversion from signed to SFix.  Automatically set the
  --       range of the resulting SFix to match the signed value,
  --       as an integral SFix value with no fraction bits.
  --
  function to_SFix( N : signed ) return SFix is
    variable F: SFix(FixP+N'LENGTH-1 downto FixP);
  begin
    F := SFix(N);
    return F;
  end;

  -- T.14. Conversion from SFix to signed.  All integer bits are
  --       preserved (including adding LS scale bits if necessary).
  --
  function to_signed(
    N        : SFix;
    rounding : Fix_Rounding_Mode   := Fix_Default_Rounding
  ) return signed is
    variable F_SFix: SFix(N'LEFT downto FixP);
    variable F: signed(F_SFix'LENGTH-1 downto 0);
  begin
    Copy_V(F_SFix, N, rounding=>rounding);
    F := signed(F_SFix);
    return F;
  end;

  -- T.15. Conversion from SFix to signed.  Caller specifies
  --       required number of bits in signed result.
  --
  function to_signed(
    N        : SFix;
    bits     : positive;
    overflow : Fix_Overflow_Mode := Fix_Default_Overflow;
    rounding : Fix_Rounding_Mode := Fix_Default_Rounding
  ) return signed is
    variable F_SFix: UFix(FixP+bits-1 downto FixP);
    variable F: signed(F_SFix'LENGTH-1 downto 0);
  begin
    Copy_V(F_SFix, N, overflow, rounding);
    F := signed(F_SFix);
    return F;
  end;

  -- T.16. Conversion from SFix to integer. Up to 32 integer bits are
  --       preserved (including adding LS scale bits if necessary).
  --
  function to_integer(
    N        : SFix;
    overflow : Fix_Overflow_Mode := Fix_Default_Overflow;
    rounding : Fix_Rounding_Mode := Fix_Default_Rounding
  ) return integer is
    variable F_SFix: SFix(Min(FixP+INT_WIDTH-1, N'LEFT) downto FixP);
  begin
    Copy_V(F_SFix, N, overflow, rounding);
    return to_integer(signed(F_SFix));
  end;

  -- T.17. Conversion from UFix to SFix.  This simply adds
  --       an extra more significant bit, set to zero.
  --
  function  to_SFix(U: UFix) return SFix is
    variable F: SFix(U'LEFT+1 downto U'RIGHT);
  begin
    F := SFix('0' & U);
    return F;
  end;


  ----------------------------------
  -- V.  Copy-to-variable procedures
  ----------------------------------

  -- Range-aware copying of an expression into a variable.  The
  -- ranges of destination variable and source expression are
  -- used to determine correct rounding, overflow and scaling
  -- behaviour.

  -- V.1 to V.5: Copy an expression into a UFix variable

  -- V.1. Copy UFix to UFix
  --
  procedure Copy_V(
    target   : out UFix;
    source   : UFix;
    overflow : Fix_Overflow_Mode := Fix_Default_Overflow;
    rounding : Fix_Rounding_Mode := Fix_Default_Rounding
  ) is
    subtype target_T is UFix(target'RANGE);
    variable W: UFix(Max(target'LEFT, source'LEFT+1) downto
                     Min(target'RIGHT, source'RIGHT));
    constant HighZero: UFix(W'LEFT downto target'LEFT+1) := (others => '0');
  begin
    assert not target'ASCENDING
      report "target" & bad_direction_msg
      severity direction_severity;
    assert not source'ASCENDING
      report "source" & bad_direction_msg
      severity direction_severity;
    W := (others => '0');
    W(source'RANGE) := source;
    if target'RIGHT > source'RIGHT then
      case rounding is
        when clip_LS | towards_zero =>
          null;
        when to_nearest =>
          W(W'LEFT downto target'RIGHT-1) := UFix(
            unsigned(W(W'LEFT downto target'RIGHT-1)) + 1
          );
      end case;
    end if;
    target := W(target'RANGE);
    if HighZero'LENGTH > 0 then
      if unsigned(W(HighZero'RANGE)) /= unsigned(HighZero) then
        case overflow is
          when clip_MS =>
            null;
          when saturate =>
            target := target_T'(others => '1');
        end case;
      end if;
    end if;
  end;

  -- V.2. Copy SFix to UFix
  --
  procedure Copy_V(
    target   : out UFix;
    source   : SFix;
    overflow : Fix_Overflow_Mode := Fix_Default_Overflow;
    rounding : Fix_Rounding_Mode := Fix_Default_Rounding
  ) is
    variable W: SFix(Max(Source'LEFT, target'LEFT) downto target'RIGHT);
    constant target_zero: UFix(target'RANGE) := (others => '0');
  begin
    assert not target'ASCENDING
      report "target" & bad_direction_msg
      severity direction_severity;
    assert not source'ASCENDING
      report "source" & bad_direction_msg
      severity direction_severity;
    W := (others => '0');
    -- Rounding & sign-extend
    Copy_V(W, source, clip_MS, rounding);
    -- Saturation?
    if W < 0 and overflow = saturate then
      target := target_zero;
    else
      Copy_V(target, UFix(W), overflow, clip_LS);
    end if;
  end;

  -- V.3. Copy UNSIGNED to UFix
  --
  procedure Copy_V(
    target   : out UFix;
    source   : unsigned;
    overflow : Fix_Overflow_Mode := Fix_Default_Overflow;
    rounding : Fix_Rounding_Mode := Fix_Default_Rounding
  ) is
    constant U: UFix(FixP+source'LENGTH-1 downto FixP) := UFix(source);
  begin
    Copy_V(target, U, overflow, rounding);
  end;

  -- V.4. Copy SIGNED to UFix
  --
  procedure Copy_V(
    target   : out UFix;
    source   : signed;
    overflow : Fix_Overflow_Mode := Fix_Default_Overflow;
    rounding : Fix_Rounding_Mode := Fix_Default_Rounding
  ) is
    constant S: SFix(FixP+source'LENGTH-1 downto FixP) := SFix(source);
  begin
    Copy_V(target, S, overflow, rounding);
  end;

  -- V.5. Copy integer to UFix
  --
  procedure Copy_V(
    target   : out UFix;
    source   : integer;
    overflow : Fix_Overflow_Mode := Fix_Default_Overflow;
    rounding : Fix_Rounding_Mode := Fix_Default_Rounding
  ) is
    constant N: signed(FixP+INT_WIDTH-1 downto FixP)
              := to_signed(source, INT_WIDTH);
  begin
    -- Cop-out, this is very inefficient, but will do for now.
    Copy_V(target, N, overflow, rounding);
  end;

  -- V.6 to V.10: Copy an expression into a SFix variable

  -- V.6. Copy UFix to SFix
  --
  procedure Copy_V(
    target   : out SFix;
    source   : UFix;
    overflow : Fix_Overflow_Mode := Fix_Default_Overflow;
    rounding : Fix_Rounding_Mode := Fix_Default_Rounding
  ) is
    constant S: SFix(FixP+source'LENGTH downto FixP) := to_SFix(source);
  begin
    Copy_V(target, S, overflow, rounding);
  end;

  -- V.7. Copy SFix to SFix
  --
  procedure Copy_V(
    target   : out SFix;
    source   : SFix;
    overflow : Fix_Overflow_Mode := Fix_Default_Overflow;
    rounding : Fix_Rounding_Mode := Fix_Default_Rounding
  ) is
    subtype target_T is SFix(target'RANGE);
    variable W: SFix(Max(target'LEFT, source'LEFT+1) downto
                     Min(target'RIGHT, source'RIGHT));
    variable HighSX: SFix(W'LEFT downto target'LEFT+1);
  begin
    for i in HighSX'RANGE loop
      HighSX(i) := source(source'LEFT);
    end loop;
    assert not target'ASCENDING
      report "target" & bad_direction_msg
      severity direction_severity;
    assert not source'ASCENDING
      report "source" & bad_direction_msg
      severity direction_severity;
    -- Make W a copy of source, duly extended at both ends
    W := (others => '0');
    W(source'RANGE) := source;
    W(HighSX'RANGE) := HighSX;
    -- Do any rounding required
    if target'RIGHT > source'RIGHT then
      case rounding is
        when clip_LS =>
          null;
        when towards_zero =>
          if
            (to_UX01(W(W'LEFT)) = '1')   -- it's negative?
          and
            (unsigned(W(target'RIGHT-1 downto W'RIGHT)) /= 0) -- round up?
          then
            W(W'LEFT downto target'RIGHT) := SFix(
              signed(W(W'LEFT downto target'RIGHT)) + 1
            );
          end if;
        when to_nearest =>
          W(W'LEFT downto target'RIGHT-1) := SFix(
            signed(W(W'LEFT downto target'RIGHT-1)) + 1
          );
      end case;
    end if;
    -- Do any overflow required
    target := W(target'RANGE);
    if HighSX'LENGTH > 0 then
      if signed(W(HighSX'RANGE)) /= signed(HighSX) then
        case overflow is
          when clip_MS =>
            null;
          when saturate =>
            target := Target_T'(others => not W(W'LEFT));
            target(target'LEFT) := W(W'LEFT);
        end case;
      end if;
    end if;
  end;

  -- V.8. Copy UNSIGNED to SFix
  --
  procedure Copy_V(
    target   : out SFix;
    source   : unsigned;
    overflow : Fix_Overflow_Mode := Fix_Default_Overflow;
    rounding : Fix_Rounding_Mode := Fix_Default_Rounding
  ) is
    constant S: SFix(FixP+source'LENGTH downto FixP) := SFix('0' & source);
  begin
    Copy_V(target, S, overflow, rounding);
  end;

  -- V.9. Copy SIGNED to SFix
  --
  procedure Copy_V(
    target   : out SFix;
    source   : signed;
    overflow : Fix_Overflow_Mode := Fix_Default_Overflow;
    rounding : Fix_Rounding_Mode := Fix_Default_Rounding
  ) is
    constant S: SFix(FixP+source'LENGTH-1 downto FixP) := SFix(source);
  begin
    Copy_V(target, S, overflow, rounding);
  end;

  -- V.10. Copy integer to SFix
  --
  procedure Copy_V(
    target   : out SFix;
    source   : integer;
    overflow : Fix_Overflow_Mode := Fix_Default_Overflow;
    rounding : Fix_Rounding_Mode := Fix_Default_Rounding
  ) is
    constant N: signed(FixP+INT_WIDTH-1 downto FixP)
              := to_signed(source, INT_WIDTH);
  begin
    -- Cop-out, this is very inefficient, but will do for now.
    Copy_V(target, N, overflow, rounding);
  end;


  --------------------------------
  -- S.  Copy-to-signal procedures
  --------------------------------
  --
  -- Range-aware copying of an expression into a signal.  The
  -- ranges of destination signal and source expression are
  -- used to determine correct rounding, overflow and scaling
  -- behaviour.

  -- S.1 to S.5: Copy an expression into a UFix signal
  --

  -- S.1. Copy UFix to UFix
  --
  procedure Copy_S(
    signal target   : out UFix;
           source   : UFix;
           overflow : Fix_Overflow_Mode := Fix_Default_Overflow;
           rounding : Fix_Rounding_Mode := Fix_Default_Rounding
  ) is
    variable V: UFix(target'RANGE);
  begin
    Copy_V(V, source, overflow, rounding);
    target <= V;
  end;

  -- S.1. Copy SFix to UFix
  --
  procedure Copy_S(
    signal target   : out UFix;
           source   : SFix;
           overflow : Fix_Overflow_Mode := Fix_Default_Overflow;
           rounding : Fix_Rounding_Mode := Fix_Default_Rounding
  ) is
    variable V: UFix(target'RANGE);
  begin
    Copy_V(V, source, overflow, rounding);
    target <= V;
  end;

  -- S.3. Copy UNSIGNED to UFix
  --
  procedure Copy_S(
    signal target   : out UFix;
           source   : unsigned;
           overflow : Fix_Overflow_Mode := Fix_Default_Overflow;
           rounding : Fix_Rounding_Mode := Fix_Default_Rounding
  ) is
    variable V: UFix(target'RANGE);
  begin
    Copy_V(V, source, overflow, rounding);
    target <= V;
  end;

  -- S.4. Copy SIGNED to UFix
  --
  procedure Copy_S(
    signal target   : out UFix;
           source   : signed;
           overflow : Fix_Overflow_Mode := Fix_Default_Overflow;
           rounding : Fix_Rounding_Mode := Fix_Default_Rounding
  ) is
    variable V: UFix(target'RANGE);
  begin
    Copy_V(V, source, overflow, rounding);
    target <= V;
  end;

  -- S.5. Copy integer to UFix
  --
  procedure Copy_S(
    signal target   : out UFix;
           source   : integer;
           overflow : Fix_Overflow_Mode := Fix_Default_Overflow;
           rounding : Fix_Rounding_Mode := Fix_Default_Rounding
  ) is
    variable V: UFix(target'RANGE);
  begin
    Copy_V(V, source, overflow, rounding);
    target <= V;
  end;

  -- S.6 to S.10: Copy an expression into a SFix signal
  --

  -- S.6. Copy UFix to SFix
  --
  procedure Copy_S(
    signal target   : out SFix;
           source   : UFix;
           overflow : Fix_Overflow_Mode := Fix_Default_Overflow;
           rounding : Fix_Rounding_Mode := Fix_Default_Rounding
  ) is
    variable V: SFix(target'RANGE);
  begin
    Copy_V(V, source, overflow, rounding);
    target <= V;
  end;

  -- S.7. Copy SFix to SFix
  --
  procedure Copy_S(
    signal target   : out SFix;
           source   : SFix;
           overflow : Fix_Overflow_Mode := Fix_Default_Overflow;
           rounding : Fix_Rounding_Mode := Fix_Default_Rounding
  ) is
    variable V: SFix(target'RANGE);
  begin
    Copy_V(V, source, overflow, rounding);
    target <= V;
  end;

  -- S.8. Copy UNSIGNED to SFix
  --
  procedure Copy_S(
    signal target   : out SFix;
           source   : unsigned;
           overflow : Fix_Overflow_Mode := Fix_Default_Overflow;
           rounding : Fix_Rounding_Mode := Fix_Default_Rounding
  ) is
    variable V: SFix(target'RANGE);
  begin
    Copy_V(V, source, overflow, rounding);
    target <= V;
  end;

  -- S.9. Copy SIGNED to SFix
  --
  procedure Copy_S(
    signal target   : out SFix;
           source   : signed;
           overflow : Fix_Overflow_Mode := Fix_Default_Overflow;
           rounding : Fix_Rounding_Mode := Fix_Default_Rounding
  ) is
    variable V: SFix(target'RANGE);
  begin
    Copy_V(V, source, overflow, rounding);
    target <= V;
  end;

  -- S.10. Copy integer to SFix
  --
  procedure Copy_S(
    signal target   : out SFix;
           source   : integer;
           overflow : Fix_Overflow_Mode := Fix_Default_Overflow;
           rounding : Fix_Rounding_Mode := Fix_Default_Rounding
  ) is
    variable V: SFix(target'RANGE);
  begin
    Copy_V(V, source, overflow, rounding);
    target <= V;
  end;


  ------------------------------------------------------------------
  -- A.  Arithmetic operations involving UFix, SFix and other
  --     numeric types.
  --     The operators are overloaded for most of the sensible
  --     combinations of SFix, UFix, UNSIGNED, SIGNED and INTEGER.
  --
  -- IMPORTANT NOTE:
  --   Arithmetic operators return a result that is wide enough
  --   and precise enough so that no information is lost.  THIS IS
  --   SIGNIFICANTLY DIFFERENT FROM THE BEHAVIOUR OF numeric_std.
  --   In the detailed description that follow, "scaling" means
  --   the numerical value (weight) of the least significant bit
  --   in a fixed-point value.
  --
  -- ADDITION returns a result whose least significant bit
  -- is the same as the least significant bit in either operand,
  -- and whose most significant bit is one position higher than
  -- the most significant bit in either operand.  Addition of
  -- two operands having the same bounds therefore results in
  -- a growth of one bit at the most significant end.  Addition
  -- of an integer to a UFix or SFix is accomplished by first
  -- converting the integer to the same subtype as the other
  -- operand, thereby giving rise to one-bit growth;  default
  -- truncation and rounding modes are used for the conversion.
  --
  -- SUBTRACTION where one or both operands are signed behaves
  -- in the same way as addition.  Subtraction where both operands
  -- are unsigned returns a result whose most significant bit
  -- position is the same as the most significant bit in either
  -- operand, and whose least significant bit is the same as the
  -- least significant bit in either operand.
  --
  -- MULTIPLICATION returns a result whose width is the sum of
  -- the widths of its two operands, and whose scaling is the
  -- product of its two operands' scalings.
  --
  -- UNARY NEGATION always gives a result having one more
  -- significant bit than the operand.
  --
  -- ABSOLUTE VALUE always gives a result having exactly the
  -- same subtype as its operand.
  --
  -- In binary operations where one operand is a fixed-point type
  -- and the other is an integer numeric type, the integer operand
  -- is first converted to a fixed-point type of the same subtype
  -- as the other operand using Copy_V with default rounding and
  -- overflow modes.  The operation is then performed between two
  -- fixed-point operands in the normal way.
  --
  ------------------------------------------------------------------


  ------------------------------------
  -- A.1.  Unary "-" negation operator
  ------------------------------------

  -- A.1.1.  SFix := - UFix
  function "-" (R: UFix) return SFix is
    variable F: SFix(R'LEFT+1 downto R'RIGHT);
  begin
    F := SFix(-(signed('0' & R)));
    return F;
  end;

  -- A.1.2.  SFix := - SFix
  function "-" (R: SFix) return SFix is
    variable F: SFix(R'LEFT+1 downto R'RIGHT);
  begin
    F := SFix(-(signed(R(R'LEFT) & R)));
    return F;
  end;


  --------------------------------------------
  -- A.2.  Unary "abs" absolute-value operator
  --------------------------------------------

  -- A.2.1.  SFix := abs SFix
  function "abs" (R: SFix) return SFix is
    variable F: SFix(R'RANGE);
  begin
    F := R;
    if to_X01(R(R'LEFT)) = '1' then
      F := SFix(-(signed(R)));
    end if;
    return F;
  end;


  -------------------------------------
  -- A.3.  Binary "+" addition operator
  -------------------------------------

  -- A.3.1.  UFix := UFix + UFix
  function "+" (L: UFix; R: UFix) return UFix is
    variable F: UFix(Max(L'LEFT, R'LEFT)+1 downto Min(L'Right, R'Right));
    variable LU, RU: unsigned(F'RANGE);
  begin
    LU := (others => '0');
    RU := (others => '0');
    LU(L'RANGE) := unsigned(L);
    RU(R'RANGE) := unsigned(R);
    F := UFix(LU + RU);
    return F;
  end;

  -- A.3.2.  UFix := UFix + unsigned
  function "+" (L: UFix; R: unsigned) return UFix is
  begin
    return L + to_UFix(R);
  end;

  -- A.3.3.  UFix := unsigned + UFix
  function "+" (L: unsigned; R: UFix) return UFix is
  begin
    return R+L;
  end;

  -- A.3.4.  UFix := UFix + natural
  function "+" (L: UFix; R: natural) return UFix is
    variable R_fp: UFix(L'RANGE);
  begin
    Copy_V(R_fp, R);
    return L + R_fp;
  end;

  -- A.3.5.  UFix := natural + UFix
  function "+" (L: natural; R: UFix) return UFix is
  begin
    return R+L;
  end;

  -- A.3.6.  SFix := SFix + SFix
  function "+" (L: SFix; R: SFix) return SFix is
    variable F: SFix(Max(L'LEFT, R'LEFT)+1 downto Min(L'Right, R'Right));
    variable LS, RS: signed(F'RANGE);
  begin
    LS := (others => '0');
    RS := (others => '0');
    LS(L'RANGE) := signed(L);
    RS(R'RANGE) := signed(R);
    for i in LS'LEFT downto L'LEFT+1 loop LS(i) := L(L'LEFT); end loop;
    for i in RS'LEFT downto R'LEFT+1 loop RS(i) := R(R'LEFT); end loop;
    F := SFix(LS + RS);
    return F;
  end;

  -- A.3.7.  SFix := SFix + UFix
  function "+" (L: SFix; R: UFix) return SFix is
  begin
    return L + SFix('0' & R);
  end;

  -- A.3.8.  SFix := UFix + SFix
  function "+" (L: UFix; R: SFix) return SFix is
  begin
    return SFix('0' & L) + R;
  end;

  -- A.3.9.  SFix := SFix + signed
  function "+" (L: SFix; R: signed) return SFix is
  begin
    return L + to_SFix(R);
  end;

  -- A.3.10. SFix := signed + SFix
  function "+" (L: signed; R: SFix) return SFix is
  begin
    return to_SFix(L) + R;
  end;

  -- A.3.11. SFix := SFix + integer
  function "+" (L: SFix; R: integer) return SFix is
    variable R_fp: SFix(L'RANGE);
  begin
    Copy_V(R_fp, R);
    return L + R_fp;
  end;

  -- A.3.12. SFix := integer + SFix
  function "+" (L: integer; R: SFix) return SFix is
  begin
    return R + L;
  end;


  ----------------------------------------
  -- A.4.  Binary "-" subtraction operator
  ----------------------------------------
  --
  -- NOTE: There is a special problem with subtraction
  -- operators that return a UFix result if that result
  -- goes negative.  Our arbitrary decision is that we
  -- truncate most-significant bits in this case.  If you
  -- wish to avoid the risk of this arbitrarily-defined
  -- truncation behaviour, change one or more of your
  -- operands to SFix and get a SFix result using this
  -- idiom:
  --   SFix := UFix + (-UFix)
  -- or copy one of the UFix operands into a SFix that's
  -- one bit wider, before performing the operation.
  --
  -- Subtractions returning a UFix result take unsigned
  -- operands and do not exhibit bit growth.
  -- Subtractions returning a SFix result take signed or
  -- unsigned operands and exhibit one-bit growth like addition.

  -- A.4.1.  UFix := UFix - UFix
  function "-" (L: UFix; R: UFix) return UFix is
    variable F, LW, RW: UFix(Max(L'LEFT, R'LEFT) downto
                             Min(L'RIGHT, R'RIGHT));
  begin
    LW := (others => '0');
    RW := (others => '0');
    LW(L'RANGE) := L;
    RW(R'RANGE) := R;
    F := UFix(unsigned(LW) - unsigned(RW));
    return F;
  end;

  -- A.4.2.  UFix := UFix - unsigned
  function "-" (L: UFix; R: unsigned) return UFix is
  begin
    return L - to_UFix(R);
  end;

  -- A.4.3.  UFix := unsigned - UFix
  function "-" (L: unsigned; R: UFix) return UFix is
  begin
    return to_UFix(L) - R;
  end;

  -- A.4.4.  UFix := UFix - integer
  function "-" (L: UFix; R: natural) return UFix is
    variable R_fp: UFix(L'RANGE);
  begin
    Copy_V(R_fp, R);
    return L - R_fp;
  end;

  -- A.4.5.  UFix := natural - UFix
  function "-" (L: natural; R: UFix) return UFix is
    variable L_fp: UFix(R'RANGE);
  begin
    Copy_V(L_fp, L);
    return L_fp - R;
  end;

  -- A.4.6.  SFix := SFix - SFix
  function "-" (L: SFix; R: SFix) return SFix is
  begin
    return L + (-R);
  end;

  -- A.4.7.  SFix := SFix - UFix
  function "-" (L: SFix; R: UFix) return SFix is
  begin
    return L + (-R);
  end;

  -- A.4.8.  SFix := UFix - SFix
  function "-" (L: UFix; R: SFix) return SFix is
  begin
    return To_SFix(L) - R;
  end;

  -- A.4.9.  SFix := SFix - signed
  function "-" (L: SFix; R: signed) return SFix is
  begin
    return L - to_SFix(R);
  end;

  -- A.4.10. SFix := signed - SFix
  function "-" (L: signed; R: SFix) return SFix is
  begin
    return to_SFix(L) - R;
  end;

  -- A.4.11. SFix := SFix - integer
  function "-" (L: SFix; R: integer) return SFix is
    variable R_fp: SFix(L'RANGE);
  begin
    Copy_V(R_fp, R);
    return L - R_fp;
  end;

  -- A.4.12. SFix := integer - SFix
  function "-" (L: integer; R: SFix) return SFix is
    variable L_fp: SFix(R'RANGE);
  begin
    Copy_V(L_fp, L);
    return L_fp - R;
  end;


  -------------------------------------------
  -- A.5.  Binary "*" multiplication operator
  -------------------------------------------

  -- A.5.1.  UFix := UFix * UFix
  function "*" (L: UFix; R: UFix) return UFix is
    variable F: UFix(L'LEFT+R'LEFT+1-FixP downto L'RIGHT+R'RIGHT-FixP);
  begin
    F := UFix(unsigned(L) * unsigned(R));
    return F;
  end;

  -- A.5.2.  UFix := UFix * unsigned
  function "*" (L: UFix; R: unsigned) return UFix is
  begin
    return L * to_UFix(R);
  end;

  -- A.5.3.  UFix := unsigned * UFix
  function "*" (L: unsigned; R: UFix) return UFix is
  begin
    return to_UFix(L) * R;
  end;

  -- A.5.4.  UFix := UFix * natural
  function "*" (L: UFix; R: natural) return UFix is
    variable R_fp: UFix(L'RANGE);
  begin
    Copy_V(R_fp, R);
    return L * R_fp;
  end;

  -- A.5.5.  UFix := natural * UFix
  function "*" (L: natural; R: UFix) return UFix is
  begin
    return R * L;
  end;

  -- A.5.6.  SFix := SFix * SFix
  function "*" (L: SFix; R: SFix) return SFix is
    variable F: SFix(L'LEFT+R'LEFT+1-FixP downto L'RIGHT+R'RIGHT-FixP);
  begin
    F := SFix(signed(L) * signed(R));
    return F;
  end;

  -- A.5.7.  SFix := SFix * UFix
  function "*" (L: SFix; R: UFix) return SFix is
    constant RS: SFix := SFix('0' & R);
    variable F: SFix(L'LEFT+R'LEFT+1-FixP downto L'RIGHT+R'RIGHT-FixP);
    variable FW: SFix(F'LEFT+1 downto F'RIGHT);
  begin
    FW := SFix(signed(L) * signed(RS));
    Copy_V(F, FW);  -- never overflows
    return F;
  end;

  -- A.5.8.  SFix := UFix * SFix
  function "*" (L: UFix; R: SFix) return SFix is
  begin
    return R * L;
  end;

  -- A.5.9.  SFix := SFix * signed
  function "*" (L: SFix; R: signed) return SFix is
  begin
    return L * to_SFix(R);
  end;

  -- A.5.10. SFix := signed * SFix
  function "*" (L: signed; R: SFix) return SFix is
  begin
    return to_SFix(L) * R;
  end;

  -- A.5.11. SFix := SFix * integer
  function "*" (L: SFix; R: integer) return SFix is
    variable R_fp: SFix(L'RANGE);
  begin
    Copy_V(R_fp, R);
    return L * R_fp;
  end;

  -- A.5.12. SFix := integer * SFix
  function "*" (L: integer; R: SFix) return SFix is
  begin
    return R * L;
  end;


  ------------------------------------------------------------------
  -- R.  Relational operators - arithmetic comparisons involving
  --     UFix, SFix and other numeric types.
  --     The operators are overloaded for most of the sensible
  --     combinations of SFix, UFix, UNSIGNED, SIGNED and INTEGER.
  --     Note that in all cases comparisons are exact;  if one
  --     operand has more fraction bits than the other, the less
  --     precise operand will have its LSBs zero-extended, and
  --     these zero bits will take part in comparison with whatever
  --     LSBs exist in the more precise operand.
  --
  -- Our implementation is based on only two operators, "=" and "<".
  -- All other operators are derived from these two, either by
  -- logical inversion of the result or by swapping operands.
  ------------------------------------------------------------------

  -------------------------------------
  -- R.1.  Binary "=" equality operator
  -------------------------------------

  -- R.1.1.   UFix = UFix
  function "=" (L: UFix; R: UFix) return boolean is
    variable LW, RW: UFix(Max(L'LEFT, R'LEFT) downto Min(L'RIGHT, R'RIGHT));
  begin
    LW := (others => '0');
    RW := (others => '0');
    LW(L'RANGE) := L;
    RW(R'RANGE) := R;
    return unsigned(LW) = unsigned(RW);
  end;

  -- R.1.2.   UFix = unsigned
  function "=" (L: UFix; R: unsigned) return boolean is
  begin
    return L = to_UFix(R);
  end;

  -- R.1.3.   unsigned = UFix
  function "=" (L: unsigned; R: UFix) return boolean is
  begin
    return to_UFix(L) = R;
  end;

  -- R.1.4.   UFix = integer
  function "=" (L: UFix; R: integer) return boolean is
    variable RS: SFix(FixP+INT_WIDTH-1 downto FixP);
  begin
    Copy_V(RS, R, clip_MS, clip_LS);
    return To_SFix(L) = RS;
  end;

  -- R.1.5.   integer = UFix
  function "=" (L: integer; R: UFix) return boolean is
  begin
    return R = L;
  end;

  -- R.1.6.   SFix = SFix
  function "=" (L: SFix; R: SFix) return boolean is
    variable LW, RW: SFix(Max(L'LEFT, R'LEFT) downto Min(L'RIGHT, R'RIGHT));
  begin
    -- clip modes are OK because we aren't losing any bits
    Copy_V(LW, L, clip_MS, clip_LS);
    Copy_V(RW, R, clip_MS, clip_LS);
    return signed(LW) = signed(RW);
  end;

  -- R.1.7.   SFix = UFix
  function "=" (L: SFix; R: UFix) return boolean is
  begin
    return L = To_SFix(R);
  end;

  -- R.1.8.   UFix = SFix
  function "=" (L: UFix; R: SFix) return boolean is
  begin
    return R = L;
  end;

  -- R.1.9.   SFix = signed
  function "=" (L: SFix; R: signed) return boolean is
  begin
    return L = to_SFix(R);
  end;

  -- R.1.10.  signed = SFix
  function "=" (L: signed; R: SFix) return boolean is
  begin
    return to_SFix(L) = R;
  end;

  -- R.1.11.  SFix = integer
  function "=" (L: SFix; R: integer) return boolean is
    variable RS: SFix(FixP+INT_WIDTH-1 downto FixP);
  begin
    Copy_V(RS, R, clip_MS, clip_LS);
    return L = RS;
  end;

  -- R.1.12.  integer = SFix
  function "=" (L: integer; R: SFix) return boolean is
  begin
    return R = L;
  end;


  --------------------------------------
  -- R.2.  Binary "<" less-than operator
  --------------------------------------

  -- R.2.1.   UFix < UFix
  function "<" (L: UFix; R: UFix) return boolean is
    variable LW, RW: unsigned(Max(L'LEFT, R'LEFT) downto
                              Min(L'RIGHT, R'RIGHT));
  begin
    LW := (others => '0');
    RW := (others => '0');
    LW(L'RANGE) := unsigned(L);
    RW(R'RANGE) := unsigned(R);
    return unsigned(LW) < unsigned(RW);
  end;

  -- R.2.2.   UFix < unsigned
  function "<" (L: UFix; R: unsigned) return boolean is
  begin
    return L < To_UFix(R);
  end;

  -- R.2.3.   unsigned < UFix
  function "<" (L: unsigned; R: UFix) return boolean is
  begin
    return To_UFix(L) < R;
  end;

  -- R.2.4.   UFix < integer
  function "<" (L: UFix; R: integer) return boolean is
    variable S: SFix(FixP+INT_WIDTH-1 downto FixP);
  begin
    Copy_V(S, R, clip_MS, clip_LS);
    return L < S;
  end;

  -- R.2.5.   integer < UFix
  function "<" (L: integer; R: UFix) return boolean is
    variable S: SFix(FixP+INT_WIDTH-1 downto FixP);
  begin
    Copy_V(S, L, clip_MS, clip_LS);
    return S < R;
  end;

  -- R.2.6.   SFix < SFix
  function "<" (L: SFix; R: SFix) return boolean is
    variable LW, RW: SFix(Max(L'LEFT, R'LEFT) downto
                          Min(L'RIGHT, R'RIGHT));
  begin
    Copy_V(LW, L, clip_MS, clip_LS);
    Copy_V(RW, R, clip_MS, clip_LS);
    return signed(LW) < signed(RW);
  end;

  -- R.2.7.   SFix < UFix
  function "<" (L: SFix; R: UFix) return boolean is
  begin
    return L < To_SFix(R);
  end;

  -- R.2.8.   UFix < SFix
  function "<" (L: UFix; R: SFix) return boolean is
  begin
    return To_SFix(L) < R;
  end;

  -- R.2.9.   SFix < signed
  function "<" (L: SFix; R: signed) return boolean is
  begin
    return L < To_SFix(R);
  end;

  -- R.2.10.  signed < SFix
  function "<" (L: signed; R: SFix) return boolean is
  begin
    return To_SFix(L) < R;
  end;

  -- R.2.11.  SFix < integer
  function "<" (L: SFix; R: integer) return boolean is
    variable S: SFix(FixP+INT_WIDTH-1 downto FixP);
  begin
    Copy_V(S, R, clip_MS, clip_LS);
    return L < S;
  end;

  -- R.2.12.  integer < SFix
  function "<" (L: integer; R: SFix) return boolean is
    variable S: SFix(FixP+INT_WIDTH-1 downto FixP);
  begin
    Copy_V(S, L, clip_MS, clip_LS);
    return S < R;
  end;


  ----------------------------------------
  -- R.3.  Binary "/=" inequality operator
  ----------------------------------------

  -- R.3.1.   UFix /= UFix
  function "/=" (L: UFix; R: UFix) return boolean is
  begin
    return not (L = R);
  end;

  -- R.3.2.   UFix /= unsigned
  function "/=" (L: UFix; R: unsigned) return boolean is
  begin
    return not (L = R);
  end;

  -- R.3.3.   unsigned /= UFix
  function "/=" (L: unsigned; R: UFix) return boolean is
  begin
    return not (L = R);
  end;

  -- R.3.4.   UFix /= integer
  function "/=" (L: UFix; R: integer) return boolean is
  begin
    return not (L = R);
  end;

  -- R.3.5.   integer /= UFix
  function "/=" (L: integer; R: UFix) return boolean is
  begin
    return not (L = R);
  end;

  -- R.3.6.   SFix /= SFix
  function "/=" (L: SFix; R: SFix) return boolean is
  begin
    return not (L = R);
  end;

  -- R.3.7.   SFix /= UFix
  function "/=" (L: SFix; R: UFix) return boolean is
  begin
    return not (L = R);
  end;

  -- R.3.8.   UFix /= SFix
  function "/=" (L: UFix; R: SFix) return boolean is
  begin
    return not (L = R);
  end;

  -- R.3.9.   SFix /= signed
  function "/=" (L: SFix; R: signed) return boolean is
  begin
    return not (L = R);
  end;

  -- R.3.10.  signed /= SFix
  function "/=" (L: signed; R: SFix) return boolean is
  begin
    return not (L = R);
  end;

  -- R.3.11.  SFix /= integer
  function "/=" (L: SFix; R: integer) return boolean is
  begin
    return not (L = R);
  end;

  -- R.3.12.  integer /= SFix
  function "/=" (L: integer; R: SFix) return boolean is
  begin
    return not (L = R);
  end;


  -----------------------------------------
  -- R.4.  Binary ">" greater-than operator
  -----------------------------------------

  -- R.4.1.   UFix > UFix
  function ">" (L: UFix; R: UFix) return boolean is
  begin
    return R < L;
  end;

  -- R.4.2.   UFix > unsigned
  function ">" (L: UFix; R: unsigned) return boolean is
  begin
    return R < L;
  end;

  -- R.4.3.   unsigned > UFix
  function ">" (L: unsigned; R: UFix) return boolean is
  begin
    return R < L;
  end;

  -- R.4.4.   UFix > integer
  function ">" (L: UFix; R: integer) return boolean is
  begin
    return R < L;
  end;

  -- R.4.5.   integer > UFix
  function ">" (L: integer; R: UFix) return boolean is
  begin
    return R < L;
  end;

  -- R.4.6.   SFix > SFix
  function ">" (L: SFix; R: SFix) return boolean is
  begin
    return R < L;
  end;

  -- R.4.7.   SFix > UFix
  function ">" (L: SFix; R: UFix) return boolean is
  begin
    return R < L;
  end;

  -- R.4.8.   UFix > SFix
  function ">" (L: UFix; R: SFix) return boolean is
  begin
    return R < L;
  end;

  -- R.4.9.   SFix > signed
  function ">" (L: SFix; R: signed) return boolean is
  begin
    return R < L;
  end;

  -- R.4.10.  signed > SFix
  function ">" (L: signed; R: SFix) return boolean is
  begin
    return R < L;
  end;

  -- R.4.11.  SFix > integer
  function ">" (L: SFix; R: integer) return boolean is
  begin
    return R < L;
  end;

  -- R.4.12.  integer > SFix
  function ">" (L: integer; R: SFix) return boolean is
  begin
    return R < L;
  end;


  ----------------------------------------------
  -- R.5.  Binary ">=" greater-or-equal operator
  ----------------------------------------------

  -- R.5.1.   UFix >= UFix
  function ">=" (L: UFix; R: UFix) return boolean is
  begin
    return not (L < R);
  end;

  -- R.5.2.   UFix >= unsigned
  function ">=" (L: UFix; R: unsigned) return boolean is
  begin
    return not (L < R);
  end;

  -- R.5.3.   unsigned >= UFix
  function ">=" (L: unsigned; R: UFix) return boolean is
  begin
    return not (L < R);
  end;

  -- R.5.4.   UFix >= integer
  function ">=" (L: UFix; R: integer) return boolean is
  begin
    return not (L < R);
  end;

  -- R.5.5.   integer >= UFix
  function ">=" (L: integer; R: UFix) return boolean is
  begin
    return not (L < R);
  end;

  -- R.5.6.   SFix >= SFix
  function ">=" (L: SFix; R: SFix) return boolean is
  begin
    return not (L < R);
  end;

  -- R.5.7.   SFix >= UFix
  function ">=" (L: SFix; R: UFix) return boolean is
  begin
    return not (L < R);
  end;

  -- R.5.8.   UFix >= SFix
  function ">=" (L: UFix; R: SFix) return boolean is
  begin
    return not (L < R);
  end;

  -- R.5.9.   SFix >= signed
  function ">=" (L: SFix; R: signed) return boolean is
  begin
    return not (L < R);
  end;

  -- R.5.10.  signed >= SFix
  function ">=" (L: signed; R: SFix) return boolean is
  begin
    return not (L < R);
  end;

  -- R.5.11.  SFix >= integer
  function ">=" (L: SFix; R: integer) return boolean is
  begin
    return not (L < R);
  end;

  -- R.5.12.  integer >= SFix
  function ">=" (L: integer; R: SFix) return boolean is
  begin
    return not (L < R);
  end;


  -------------------------------------------
  -- R.6.  Binary "<=" less-or-equal operator
  -------------------------------------------

  -- R.6.1.   UFix <= UFix
  function "<=" (L: UFix; R: UFix) return boolean is
  begin
    return not (R < L);
  end;

  -- R.6.2.   UFix <= unsigned
  function "<=" (L: UFix; R: unsigned) return boolean is
  begin
    return not (R < L);
  end;

  -- R.6.3.   unsigned <= UFix
  function "<=" (L: unsigned; R: UFix) return boolean is
  begin
    return not (R < L);
  end;

  -- R.6.4.   UFix <= integer
  function "<=" (L: UFix; R: integer) return boolean is
  begin
    return not (R < L);
  end;

  -- R.6.5.   integer <= UFix
  function "<=" (L: integer; R: UFix) return boolean is
  begin
    return not (R < L);
  end;

  -- R.6.6.   SFix <= SFix
  function "<=" (L: SFix; R: SFix) return boolean is
  begin
    return not (R < L);
  end;

  -- R.6.7.   SFix <= UFix
  function "<=" (L: SFix; R: UFix) return boolean is
  begin
    return not (R < L);
  end;

  -- R.6.8.   UFix <= SFix
  function "<=" (L: UFix; R: SFix) return boolean is
  begin
    return not (R < L);
  end;

  -- R.6.9.   SFix <= signed
  function "<=" (L: SFix; R: signed) return boolean is
  begin
    return not (R < L);
  end;

  -- R.6.10.  signed <= SFix
  function "<=" (L: signed; R: SFix) return boolean is
  begin
    return not (R < L);
  end;

  -- R.6.11.  SFix <= integer
  function "<=" (L: SFix; R: integer) return boolean is
  begin
    return not (R < L);
  end;

  -- R.6.12.  integer <= SFix
  function "<=" (L: integer; R: SFix) return boolean is
  begin
    return not (R < L);
  end;

  ------------------------------------------------------------------
  -- C.  Concatenation operators between fixed-point operands, and
  --     between fixed-point operands and other reasonable types.
  --     Unlike the implicitly defined "&" operator, they preserve
  --     place value and return an appropriately aligned subtype.
  --     Preservation of place value means that some concatenation
  --     operations are illegal.  In particular, operations
  --     [SFix & SFix] and [UFix & SFix] are illegal, and
  --     the place values of operands in [SFix & UFix] and
  --     [UFix & UFix] must match up - the rightmost subscript of
  --     the left operand must be one higher than the leftmost
  --     subscript of the right operand.
  --     Single bits of type std_ulogic may be concatenated with
  --     fixed-point types where there is no risk of losing
  --     sign-extension information.
  --     Because of the way VHDL defines implicit concatenation,
  --     it is impossible to detect all these errors at compile time.
  --     Instead they are detected by assertion statements at run time.
  --     Tool vendors may choose to implement compile-time checks
  --     so that the run-time checking overhead can be avoided.
  ------------------------------------------------------------------

  -- C.1.1.   UFix := UFix & UFix
  function "&" (L: UFix; R: UFix) return UFix is
  begin
    return UFix(concat(std_logic_vector(L), std_logic_vector(R)));
  end;

  -- C.1.2.   UFix := UFix & std_ulogic
  function "&" (L: UFix; R: std_ulogic) return UFix is
  begin
    return UFix(concat(std_logic_vector(L), R));
  end;

  -- C.1.3.   UFix := std_ulogic & UFix
  function "&" (L: std_ulogic; R: UFix) return UFix is
  begin
    return UFix(concat(L, std_logic_vector(R)));
  end;


  -- C.2.1.   SFix := SFix & UFix
  function "&" (L: SFix; R: UFix) return SFix is
  begin
    return SFix(concat(std_logic_vector(L), std_logic_vector(R)));
  end;

  -- C.2.2.   SFix := SFix & SFix -- ILLEGAL
  function "&" (L: SFix; R: SFix) return SFix is
  begin
    report concat_signLS_msg
      severity concat_severity;
    return SFix(concat(std_logic_vector(L), std_logic_vector(R)));
  end;

  -- C.2.3.   SFix := SFix & std_ulogic
  function "&" (L: SFix; R: std_ulogic) return SFix is
  begin
    return SFix(concat(std_logic_vector(L), R));
  end;

  -- C.2.4.   SFix := std_ulogic & SFix
  function "&" (L: std_ulogic; R: SFix) return SFix is
  begin
    assert std_match(L, R(R'LEFT))
      report concat_sxt_msg
      severity concat_severity;
    return SFix(concat(L, std_logic_vector(R)));
  end;


end; -- package body fix_std;
