--
--  File Name:         TbUtilPkg.vhd
--  Design Unit Name:  TbUtilPkg
--  Revision:          STANDARD VERSION
--
--  Maintainer:        Jim Lewis      email:  jim@SynthWorks.com 
--  Contributor(s):            
--     Jim Lewis      email:  jim@SynthWorks.com   
--
--  Package Defines
--    
--  Developed for: 
--        SynthWorks Design Inc. 
--        VHDL Training Classes
--        11898 SW 128th Ave.  Tigard, Or  97223
--        http://www.SynthWorks.com
--
--  Revision History:
--    Date      Version    Description
--    11/1999:  0.1        Initial revision
--                         Numerous revisions for VHDL Testbenches and Verification
--    10/2013   2013.10    Split out Text Utilities
--    11/2016   2016.11    First Public Release Version
--                         Updated naming for consistency.
--
--
--  Copyright (c) 1999 - 2016 by SynthWorks Design Inc.  All rights reserved.
--
--  Verbatim copies of this source file may be used and 
--  distributed without restriction.   
-- 								 
--  This source file is free software; you can redistribute it  
--  and/or modify it under the terms of the ARTISTIC License 
--  as published by The Perl Foundation; either version 2.0 of 
--  the License, or (at your option) any later version. 						 
-- 								 
--  This source is distributed in the hope that it will be 	 
--  useful, but WITHOUT ANY WARRANTY; without even the implied  
--  warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR 	 
--  PURPOSE. See the Artistic License for details. 							 
-- 								 
--  You should have received a copy of the license with this source.
--  If not download it from, 
--     http://www.perlfoundation.org/artistic_license_2_0
--
library ieee ;
  use ieee.std_logic_1164.all ;
  
library osvvm ; 
  use osvvm.AlertLogPkg.all ;
  use osvvm.TranscriptPkg.all ; 

package TbUtilPkg is

  constant CLK_ACTIVE : std_logic := '1' ; 

  constant t_sim_resolution : time := std.env.resolution_limit ;  -- VHDL-2008
  -- constant t_sim_resolution : time := 1 ns ;  -- for non VHDL-2008 simulators

  ------------------------------------------------------------
  -- ZeroOneHot, OneHot
  --   OneHot:      return true if exactly one value is 1
  --   ZeroOneHot:  return false when more than one value is a 1
  ------------------------------------------------------------
  function OneHot ( constant A : in std_logic_vector ) return boolean ;  
  function ZeroOneHot ( constant A : in std_logic_vector ) return boolean ;  


  ------------------------------------------------------------
  -- RequestTransaction
  --   Transaction initiation side of handshaking
  --   Pairs with WaitForTransaction or one of its variations
  ------------------------------------------------------------
  procedure RequestTransaction (
    signal Rdy  : Out std_logic ;
    signal Ack  : In  std_logic 
  ) ;
  
  ------------------------------------------------------------
  -- WaitForTransaction
  --   Model side of handshaking
  --   Pairs with RequestTransaction 
  ------------------------------------------------------------
  procedure WaitForTransaction (
    signal Clk  : In  std_logic ;
    signal Rdy  : In  std_logic ;
    signal Ack  : Out std_logic 
  ) ;

  -- Variation for model that stops waiting when TimeOut is asserted
  -- Intended for models that need to switch between instruction streams
  -- such as a CPU when interrupt is pending
  procedure WaitForTransaction (
    signal   Clk       : In  std_logic ;
    signal   Rdy       : In  std_logic ;
    signal   Ack       : Out std_logic ;
    signal   TimeOut   : In  std_logic ;
    constant Polarity  : In  std_logic := '1' 
  ) ;

  -- Set Ack to Model starting value
  procedure StartTransaction  ( signal Ack : Out std_logic ) ; 
  -- Set Ack to Model finishing value
  procedure FinishTransaction ( signal Ack : Out std_logic ) ; 
  -- If a transaction is pending, return true
  function TransactionPending ( signal Rdy : In  std_logic ) return boolean ;

  -- Variation for clockless models
  procedure WaitForTransaction ( 
    signal Rdy : In  std_logic ; 
    signal Ack : Out std_logic 
  ) ;


  ------------------------------------------------------------
  -- Toggle, WaitForToggle
  --   Used for communicating between processes
  ------------------------------------------------------------
  procedure Toggle (
    signal Sig        : InOut std_logic ;
    constant DelayVal : time  
  ) ;
  procedure Toggle ( signal Sig : InOut std_logic ) ;
  procedure ToggleHS ( signal Sig : InOut std_logic ) ;
  function  IsToggle ( signal Sig : In std_logic ) return boolean ; 
  procedure WaitForToggle ( signal Sig : In std_logic ) ;
  
  -- Bit type versions
  procedure Toggle ( signal Sig : InOut bit ; constant DelayVal : time ) ;
  procedure Toggle ( signal Sig : InOut bit ) ;
  procedure ToggleHS ( signal Sig : InOut bit ) ;
  function  IsToggle ( signal Sig : In bit ) return boolean ; 
  procedure WaitForToggle ( signal Sig : In bit ) ;


  ------------------------------------------------------------
  -- WaitForBarrier
  --   Barrier Synchronization
  --   Multiple processes call it, it finishes when all have called it
  ------------------------------------------------------------
  procedure WaitForBarrier ( signal Sig : InOut std_logic ) ;
  procedure WaitForBarrier ( signal Sig : InOut std_logic ; signal TimeOut : std_logic ; constant Polarity : in std_logic := '1') ;
  procedure WaitForBarrier ( signal Sig : InOut std_logic ; constant TimeOut : time ) ;
  -- resolved_barrier : summing resolution used in conjunction with integer based barriers
  function resolved_barrier ( s : integer_vector ) return integer ; 
  subtype  integer_barrier is resolved_barrier integer ; 
  -- Usage of integer barriers requires resolved_barrier. Initialization to 1 recommended, but not required
  --   signal barrier1 : resolved_barrier integer := 1 ;     -- using the resolution function
  --   signal barrier2 : integer_barrier := 1 ;              -- using the subtype that already applies the resolution function
  procedure WaitForBarrier ( signal Sig : InOut integer ) ;  
  procedure WaitForBarrier ( signal Sig : InOut integer ; signal TimeOut : std_logic ; constant Polarity : in std_logic := '1') ;
  procedure WaitForBarrier ( signal Sig : InOut integer ; constant TimeOut : time ) ;
  -- Using separate signals
  procedure WaitForBarrier2 ( signal SyncOut : out std_logic ; signal SyncIn : in  std_logic ) ;
  procedure WaitForBarrier2 ( signal SyncOut : out std_logic ; signal SyncInV : in  std_logic_vector ) ;

  
  ------------------------------------------------------------
  -- WaitForClock
  --   Sync to Clock - after a delay, after a number of clocks
  ------------------------------------------------------------
  procedure WaitForClock ( signal Clk : in std_logic ;  constant Delay : in time ) ; 
  procedure WaitForClock ( signal Clk : in std_logic ;  constant NumberOfClocks : in integer := 1) ; 
  procedure WaitForClock ( signal Clk : in std_logic ;  signal Enable : in boolean ) ; 
  procedure WaitForClock ( signal Clk : in std_logic ;  signal Enable : in std_logic ; constant Polarity : std_logic := '1' ) ;


  ------------------------------------------------------------
  -- WaitForLevel
  --   Find a signal at a level
  ------------------------------------------------------------
  procedure WaitForLevel ( signal A : in boolean ) ;
  procedure WaitForLevel ( signal A : in std_logic ; Polarity : std_logic := '1' ) ; 

  ------------------------------------------------------------
  -- CreateClock,  CreateReset
  --   Note these do not exit
  ------------------------------------------------------------
  procedure CreateClock ( 
    signal   Clk        : inout std_logic ; 
    constant Period     : time ; 
    constant DutyCycle  : real := 0.5 
  )  ; 
  
  procedure CheckClockPeriod ( 
    constant AlertLogID : AlertLogIDType ; 
    signal   Clk        : in  std_logic ; 
    constant Period     : time ;
    constant ClkName    : string := "Clock" ;
    constant HowMany    : integer := 5
  ) ; 
  
  procedure CheckClockPeriod ( 
    signal   Clk        : in  std_logic ; 
    constant Period     : time ;
    constant ClkName    : string := "Clock" ;
    constant HowMany    : integer := 5
  ) ; 

  procedure CreateReset ( 
    signal   Reset       : out std_logic ; 
    constant ResetActive : in  std_logic ; 
    signal   Clk         : in  std_logic ; 
    constant Period      :     time ; 
    constant tpd         :     time 
  ) ;
  
  procedure LogReset ( 
    constant AlertLogID  : AlertLogIDType ; 
    signal   Reset       : in  std_logic ; 
    constant ResetActive : in  std_logic ;
    constant ResetName   : in  string := "Reset" ;
    constant LogLevel    : in  LogType := ALWAYS
  ) ;

  procedure LogReset ( 
    signal   Reset       : in  std_logic ; 
    constant ResetActive : in  std_logic ;
    constant ResetName   : in  string := "Reset" ;
    constant LogLevel    : in  LogType := ALWAYS
  ) ;
  
  ------------------------------------------------------------
  --  Deprecated subprogram names
  --    Maintaining backward compatibility using aliases
  ------------------------------------------------------------
  -- History of RequestTransaction / WaitForTransaction
  alias RequestAction is RequestTransaction [std_logic, std_logic] ;
  alias WaitForRequest is WaitForTransaction [std_logic, std_logic, std_logic] ;
  -- History of WaitForToggle
  alias WaitOnToggle is WaitForToggle [std_logic] ;
  -- History of WaitForBarrier 
  alias WayPointBlock is WaitForBarrier [std_logic] ;
  alias SyncTo is WaitForBarrier2[std_logic, std_logic] ;
  alias SyncTo is WaitForBarrier2[std_logic, std_logic_vector] ;
  -- Backward compatible name  
  alias SyncToClk is WaitForClock [std_logic, time] ;

  ------------------------------------------------------------
  -- Deprecated
  -- subsumed by WaitForTransaction with Ack and TimeOut.
  -- TimeOut works exactly like IntReq
  ------------------------------------------------------------
  procedure WaitForTransactionOrIrq (
    signal Clk     : In  std_logic ;
    signal Rdy     : In  std_logic ;
    signal IntReq  : In  std_logic 
  ) ;

  ------------------------------------------------------------
  -- Deprecated
  -- WaitForAck, StrobeAck
  --   Replaced by WaitForToggle and Toggle
  ------------------------------------------------------------
  procedure WaitForAck ( signal Ack : In  std_logic ) ;
  procedure StrobeAck  ( signal Ack : Out std_logic ) ;
  
end TbUtilPkg ;

-- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
package body TbUtilPkg is 

  ------------------------------------------------------------
  -- ZeroOneHot, OneHot
  --   OneHot:      return true if exactly one value is 1
  --   ZeroOneHot:  return false when more than one value is a 1
  ------------------------------------------------------------
  function OneHot ( constant A : in std_logic_vector ) return boolean is
    variable found_one : boolean := FALSE ;
  begin
    for i in A'range loop 
      if A(i) = '1' or A(i) = 'H' then 
        if found_one then 
          return FALSE ; 
        end if ; 
        found_one := TRUE ;
      end if ; 
    end loop ;
    return found_one ; -- found a one
  end function OneHot ; 
  
  function ZeroOneHot ( constant A : in std_logic_vector ) return boolean is  
    variable found_one : boolean := FALSE ;
  begin
    for i in A'range loop 
      if A(i) = '1' or A(i) = 'H' then 
        if found_one then 
          return FALSE ; 
        end if ; 
        found_one := TRUE ;
      end if ; 
    end loop ;
    return TRUE ;  -- all zero or found a one
  end function ZeroOneHot ; 


  ------------------------------------------------------------
  -- RequestTransaction
  --   Transaction initiation side of handshaking
  --   Pairs with WaitForTransaction or one of its variations
  ------------------------------------------------------------
  procedure RequestTransaction (
    signal Rdy  : Out std_logic ;
    signal Ack  : In  std_logic 
  ) is
  begin
    -- Record contains new transaction
    Rdy        <= '1' ;
    -- Find Ack low = '0' 
    wait until Ack = '0' ;
    -- Prepare for Next Transaction
    Rdy        <= '0' ;
    -- Transaction Done
    wait until Ack = '1' ;        
  end procedure ;


  ------------------------------------------------------------
  -- WaitForTransaction
  --   Model side of handshaking
  --   Pairs with RequestTransaction 
  ------------------------------------------------------------
  procedure WaitForTransaction (
    signal Clk  : In  std_logic ;
    signal Rdy  : In  std_logic ;
    signal Ack  : Out std_logic 
  ) is
    variable AckTime : time ; 
  begin
    -- End of Previous Cycle.  Signal Done
    Ack        <= '1' ;               --  #6
    AckTime    := NOW ; 
    -- Find Start of Transaction
    if Rdy /= '1' then                --   #2
      wait until Rdy = '1' ; 
    else
      wait for 0 ns ; -- allow Ack to update
    end if ; 
    -- align to clock if needed (not back-to-back transactions)
    if NOW /= AckTime then 
      wait until Clk = CLK_ACTIVE ;
    end if ; 
    -- Model active and owns the record
    Ack        <= '0' ;               --  #3
  end procedure ;

  -- Variation for model that stops waiting when TimeOut is asserted
  -- Intended for models that need to switch between instruction streams
  -- such as a CPU when interrupt is pending
  procedure WaitForTransaction (
    signal   Clk       : In  std_logic ;
    signal   Rdy       : In  std_logic ;
    signal   Ack       : Out std_logic ;
    signal   TimeOut   : In  std_logic ;
    constant Polarity  : In std_logic := '1' 
  ) is
    variable AckTime : time ; 
    variable FoundRdy : boolean ; 
  begin
    -- End of Previous Cycle.  Signal Done
    Ack        <= '1' ;               --  #6
    AckTime    := NOW ; 
    -- Find Ready or Time out
    if (Rdy /= '1' and TimeOut /= Polarity) then 
      wait until Rdy = '1' or TimeOut = Polarity ; 
    else
      wait for 0 ns ; -- allow Ack to update
    end if ; 
    FoundRdy := Rdy = '1' ; 
    -- align to clock if Rdy or TimeOut does not happen within delta cycles from Ack
    if NOW /= AckTime then 
      wait until Clk = CLK_ACTIVE ;
    end if ; 
    if FoundRdy then 
      -- Model active and owns the record
      Ack        <= '0' ;             --  #3
    end if ;
  end procedure ;
  
  -- Set Ack to Model starting value
  -- Pairs with WaitForTransactionOrIrq above
  procedure StartTransaction  ( signal Ack : Out std_logic ) is
  begin
    Ack        <= '0' ;
  end procedure ; 

  -- Set Ack to Model finishing value
  -- Pairs with WaitForTransactionOrIrq above
  procedure FinishTransaction ( signal Ack : Out std_logic ) is
  begin
    -- End of Cycle
    Ack        <= '1' ;
  end procedure ; 

  -- If a transaction is pending, return true
  --   Used to detect presence of transaction stream, 
  --   such as an interrupt handler
  function TransactionPending (
    signal Rdy     : In  std_logic 
  ) return boolean is
  begin
    return Rdy = '1' ; 
  end function ;

  -- Variation for clockless models
  procedure WaitForTransaction (
    signal Rdy  : In  std_logic ;
    signal Ack  : Out std_logic 
  ) is
    variable AckTime : time ; 
  begin
    -- End of Previous Cycle.  Signal Done
    Ack        <= '1' ;               --  #6
    -- Find Start of Transaction
    if Rdy /= '1' then                --   #2
      wait until Rdy = '1' ; 
    else
      wait for 0 ns ; -- allow Ack to update
    end if ; 
    -- Model active and owns the record
    Ack        <= '0' ;               --  #3
  end procedure ;


  ------------------------------------------------------------
  -- Toggle, WaitForToggle
  --   Used for communicating between processes
  ------------------------------------------------------------
  type stdulogic_indexby_stdulogic is array (std_ulogic) of std_ulogic;
  constant toggle_sl_table : stdulogic_indexby_stdulogic := (
      '0'     => '1', 
      'L'     => '1', 
      others  => '0' 
  ); 

  procedure Toggle (
    signal Sig        : InOut std_logic ;
    constant DelayVal : time  
  ) is
    variable iDelayVal : time ;
  begin
    iDelayVal := DelayVal ; 
    if iDelayVal > t_sim_resolution then 
      iDelayVal := iDelayVal - t_sim_resolution ; 
    end if ;
    Sig <= toggle_sl_table(Sig) after iDelayVal ;
  end procedure ;

  procedure Toggle ( signal Sig : InOut std_logic ) is
  begin
    Sig <= toggle_sl_table(Sig) ;
  end procedure ;
  
  procedure ToggleHS ( signal Sig : InOut std_logic ) is
  begin
    Sig    <= toggle_sl_table(Sig) ;
    wait for 0 ns ;  -- Sig toggles
    wait for 0 ns ;  -- new values updated into record
  end procedure ;

  function IsToggle ( signal Sig : In std_logic ) return boolean is 
  begin
    return Sig'event ; 
  end function ;

  procedure WaitForToggle ( signal Sig : In std_logic ) is
  begin
    wait on Sig ;
  end procedure ;

  -- Bit type versions
  procedure Toggle ( signal Sig : InOut bit ; constant DelayVal : time ) is
    variable iDelayVal : time ;
  begin
    iDelayVal := DelayVal ; 
    if iDelayVal > t_sim_resolution then 
      iDelayVal := iDelayVal - t_sim_resolution ; 
    end if ;
    Sig <= not Sig after iDelayVal ;
  end procedure ;

  procedure Toggle ( signal Sig : InOut bit ) is
  begin
    Sig <= not Sig ;
  end procedure ;
  
  procedure ToggleHS ( signal Sig : InOut bit ) is
  begin
    Sig    <= not Sig ;
    wait for 0 ns ;  -- Sig toggles
    wait for 0 ns ;  -- new values updated into record
  end procedure ;

  function IsToggle ( signal Sig : In bit ) return boolean is 
  begin
    return Sig'event ; 
  end function ;

  procedure WaitForToggle ( signal Sig : In bit ) is
  begin
    wait on Sig ;
  end procedure ;
  
  
  ------------------------------------------------------------
  -- WaitForBarrier
  --   Barrier Synchronization
  --   Multiple processes call it, it finishes when all have called it
  ------------------------------------------------------------
  procedure WaitForBarrier ( signal Sig : InOut std_logic ) is
  begin
    Sig <= 'H' ; 
    -- Wait until all processes set Sig to H  
    -- Level check not necessary since last value /= H yet
    wait until Sig = 'H' ;
    -- Deactivate and propagate to allow back to back calls
    Sig <= '0' ;
    wait for 0 ns ; 
  end procedure WaitForBarrier ; 

  procedure WaitForBarrier ( signal Sig : InOut std_logic ; signal TimeOut : std_logic ; constant Polarity : in std_logic := '1') is
  begin
    Sig <= 'H' ; 
    -- Wait until all processes set Sig to H  
    -- Level check not necessary since last value /= H yet
    wait until Sig = 'H' or TimeOut = Polarity ;
    -- Deactivate and propagate to allow back to back calls
    Sig <= '0' ;
    wait for 0 ns ; 
  end procedure WaitForBarrier ; 

  procedure WaitForBarrier ( signal Sig : InOut std_logic ; constant TimeOut : time ) is
  begin
    Sig <= 'H' ; 
    -- Wait until all processes set Sig to H  
    -- Level check not necessary since last value /= H yet
    wait until Sig = 'H' for TimeOut ;
    -- Deactivate and propagate to allow back to back calls
    Sig <= '0' ;
    wait for 0 ns ; 
  end procedure WaitForBarrier ; 

  ------------------------------------------------------------
  -- resolved_barrier 
  -- summing resolution used in conjunction with integer based barriers
  function resolved_barrier ( s : integer_vector ) return integer is 
    variable result : integer := 0 ; 
  begin
    for i in s'RANGE loop
      if s(i) /= integer'left then 
        result := s(i) + result;
      else 
        return integer'left ;  -- removes the initialization requirement
      end if ;
    end loop ;
    return result ; 
  end function resolved_barrier ; 

  -- Usage of integer barriers requires resolved_barrier. Initialization to 1 recommended, but not required
  --   signal barrier1 : resolved_barrier integer := 1 ;     -- using the resolution function
  --   signal barrier2 : integer_barrier := 1 ;              -- using the subtype that already applies the resolution function
  procedure WaitForBarrier ( signal Sig : InOut integer ) is
  begin
    Sig <= 0 ; 
    -- Wait until all processes set Sig to 0  
    -- Level check not necessary since last value /= 0 yet
    wait until Sig = 0  ;
    -- Deactivate and propagate to allow back to back calls
    Sig <= 1 ;
    wait for 0 ns ; 
  end procedure WaitForBarrier ; 

  procedure WaitForBarrier ( signal Sig : InOut integer ; signal TimeOut : std_logic ; constant Polarity : in std_logic := '1') is
  begin
    Sig <= 0 ; 
    -- Wait until all processes set Sig to 0  
    -- Level check not necessary since last value /= 0 yet
    wait until Sig = 0 or TimeOut = Polarity ;
    -- Deactivate and propagate to allow back to back calls
    Sig <= 1 ;
    wait for 0 ns ; 
  end procedure WaitForBarrier ; 

  procedure WaitForBarrier ( signal Sig : InOut integer ; constant TimeOut : time ) is
  begin
    Sig <= 0 ; 
    -- Wait until all processes set Sig to 0  
    -- Level check not necessary since last value /= 0 yet
    wait until Sig = 0 for TimeOut ;
    -- Deactivate and propagate to allow back to back calls
    Sig <= 1 ;
    wait for 0 ns ; 
  end procedure WaitForBarrier ; 
  
  -- Using separate signals
  procedure WaitForBarrier2 ( signal SyncOut : out std_logic ; signal SyncIn : in  std_logic ) is
  begin
    -- Activate Rdy 
    SyncOut <= '1' ; 
    -- Make sure our Rdy is seen
    wait for 0 ns ; 
    -- Wait until other process' Rdy is at level 1
    if SyncIn /= '1' then 
       wait until SyncIn = '1' ;
    end if ;
    -- Deactivate Rdy
    SyncOut <= '0' ;
  end procedure WaitForBarrier2 ;

  procedure WaitForBarrier2 ( signal SyncOut : out std_logic ; signal SyncInV : in  std_logic_vector ) is
    constant ALL_ONE : std_logic_vector(SyncInV'Range) := (others => '1');
  begin
    -- Activate Rdy 
    SyncOut <= '1' ; 
    -- Make sure our Rdy is seen
    wait for 0 ns ; 
    -- Wait until all other process' Rdy is at level 1
    if SyncInV /= ALL_ONE then 
       wait until SyncInV = ALL_ONE ;
    end if ;
    -- Deactivate Rdy
    SyncOut <= '0' ;
  end procedure WaitForBarrier2 ;

  
  ------------------------------------------------------------
  -- WaitForClock
  --   Sync to Clock - after a delay, after a number of clocks
  ------------------------------------------------------------
  procedure WaitForClock ( signal Clk : in std_logic ;  constant Delay : in time ) is
  begin
    if delay > t_sim_resolution then
      wait for delay - t_sim_resolution ; 
    end if ; 
    wait until Clk = CLK_ACTIVE ;
  end procedure WaitForClock ;  

  procedure WaitForClock ( signal Clk : in std_logic ;  constant NumberOfClocks : in integer := 1) is
  begin
    for i in 1 to NumberOfClocks loop 
      wait until Clk = CLK_ACTIVE ;
    end loop ;
  end procedure WaitForClock ;  
  
  procedure WaitForClock ( signal Clk : in std_logic ;  signal Enable : in boolean ) is 
  begin
    wait on Clk until Clk = CLK_ACTIVE and Enable ;
  end procedure WaitForClock ;   

  procedure WaitForClock ( signal Clk : in std_logic ;  signal Enable : in std_logic ; constant Polarity : std_logic := '1' ) is
  begin
    wait on Clk until Clk = CLK_ACTIVE and Enable = Polarity ;
  end procedure WaitForClock ;   

  
  ------------------------------------------------------------
  -- WaitForLevel
  --   Find a signal at a level
  ------------------------------------------------------------
  procedure WaitForLevel ( signal A : in boolean ) is 
  begin
    if not A then 
      wait until A ;
    end if ; 
  end procedure WaitForLevel ; 
  
  procedure WaitForLevel ( signal A : in std_logic ; Polarity : std_logic := '1' ) is 
  begin
    if A /= Polarity then 
      -- wait on A until A = Polarity ;
      if Polarity = '1' then 
        wait until A = '1' ; 
      else
        wait until A = '0' ;
      end if ; 
    end if ; 
  end procedure WaitForLevel ; 

  
  ------------------------------------------------------------
  -- CreateClock,  CreateReset
  --   Note these do not exit
  ------------------------------------------------------------
  procedure CreateClock ( 
    signal   Clk        : inout std_logic ; 
    constant Period     : time ; 
    constant DutyCycle  : real := 0.5 
  ) is 
    constant HIGH_TIME : time := Period * DutyCycle ; 
    constant LOW_TIME  : time := Period - HIGH_TIME ; 
  begin
    if HIGH_TIME = LOW_TIME then 
      loop 
        Clk <= toggle_sl_table(Clk) after HIGH_TIME ; 
        wait on Clk ; 
      end loop ; 
    else
      -- Schedule s.t. all assignments after the first occur on delta cycle 0   
      Clk <= '0', '1' after LOW_TIME ; 
      wait for period - 1 ns ; -- allows after on future Clk <= '0'
      loop 
        Clk <= '0' after 1 ns, '1' after LOW_TIME + 1 ns ; 
        wait for period ; 
      end loop ; 
    end if ; 
  end procedure CreateClock ; 
  
  procedure CheckClockPeriod ( 
    constant AlertLogID : AlertLogIDType ; 
    signal   Clk        : in  std_logic ; 
    constant Period     : time ;
    constant ClkName    : string := "Clock" ;
    constant HowMany    : integer := 5
  ) is 
    variable LastLogTime, ObservedPeriod : time ; 
  begin
    wait until Clk = CLK_ACTIVE ; 
    LastLogTime := now ;
    -- Check First HowMany clocks
    for i in 1 to HowMany loop 
      wait until Clk = CLK_ACTIVE ; 
      ObservedPeriod := now - LastLogTime ; 
      AffirmIf(AlertLogID, ObservedPeriod = Period, 
         "CheckClockPeriod: " & ClkName & " Period: " & to_string(ObservedPeriod) & 
         " = Expected " & to_string(Period)) ;
      LastLogTime := now ; 
     end loop ; 
     wait ; 
  end procedure CheckClockPeriod ; 
  
  procedure CheckClockPeriod ( 
    signal   Clk        : in  std_logic ; 
    constant Period     : time ;
    constant ClkName    : string := "Clock" ;
    constant HowMany    : integer := 5
  ) is 
  begin
    CheckClockPeriod ( 
      AlertLogID => ALERTLOG_DEFAULT_ID,
      Clk        => Clk, 
      Period     => Period,
      ClkName    => ClkName,
      HowMany    => HowMany
    ) ; 
  end procedure CheckClockPeriod ; 

  procedure CreateReset ( 
    signal   Reset       : out std_logic ; 
    constant ResetActive : in  std_logic ; 
    signal   Clk         : in  std_logic ; 
    constant Period      :     time ; 
    constant tpd         :     time 
  ) is 
  begin
    wait until Clk = CLK_ACTIVE ; 
    Reset <= ResetActive after tpd ; 
    wait for Period - t_sim_resolution ;
    wait until Clk = CLK_ACTIVE ; 
    Reset <= not ResetActive after tpd ;  
    wait ; 
  end procedure CreateReset ; 

  procedure LogReset ( 
    constant AlertLogID  : AlertLogIDType ; 
    signal   Reset       : in  std_logic ; 
    constant ResetActive : in  std_logic ;
    constant ResetName   : in  string := "Reset" ;
    constant LogLevel    : in  LogType := ALWAYS
  ) is 
  begin
    -- Does not log the value of Reset at time 0.
    for_ever : loop
      wait on Reset ; 
      if Reset = ResetActive then 
        LOG(AlertLogID, ResetName & " now active", INFO) ;
        print("") ;
      elsif Reset = not ResetActive then
        LOG(AlertLogID, ResetName & " now inactive", INFO) ;
        print("") ;
      else
        LOG(AlertLogID, ResetName & " = " & to_string(Reset), INFO) ;
        print("") ;
      end if ; 
    end loop for_ever ; 
  end procedure LogReset ;

  procedure LogReset ( 
    signal   Reset       : in  std_logic ; 
    constant ResetActive : in  std_logic ;
    constant ResetName   : in  string := "Reset" ;
    constant LogLevel    : in  LogType := ALWAYS
  ) is 
  begin
    LogReset ( 
      AlertLogID  => ALERTLOG_DEFAULT_ID, 
      Reset       => Reset,
      ResetActive => ResetActive,
      ResetName   => ResetName,
      LogLevel    => LogLevel
    ) ; 
  end procedure LogReset ;

  ------------------------------------------------------------
  -- Deprecated
  -- subsumed by WaitForTransaction with Ack and TimeOut.
  -- TimeOut works exactly like IntReq
  ------------------------------------------------------------
  procedure WaitForTransactionOrIrq (
    signal Clk     : In  std_logic ;
    signal Rdy     : In  std_logic ;
    signal IntReq  : In  std_logic 
  ) is
    variable AckTime : time ; 
    constant POLARITY : std_logic := '1' ;
  begin
    AckTime    := NOW ; 
    -- Find Ready or Time out
    if (Rdy /= '1' and IntReq /= POLARITY) then 
      wait until Rdy = '1' or IntReq = POLARITY ; 
    else
      wait for 0 ns ; -- allow Ack to update
   end if ; 
    -- align to clock if Rdy or IntReq does not happen within delta cycles from Ack
    if NOW /= AckTime then 
      wait until Clk = CLK_ACTIVE ;
    end if ; 
  end procedure ;
  
  ------------------------------------------------------------
  -- Deprecated
  -- WaitForAck, StrobeAck
  --   Replaced by WaitForToggle and Toggle
  ------------------------------------------------------------
  procedure WaitForAck ( signal Ack : In  std_logic ) is
  begin
    -- Wait for Model to be done
    wait until Ack = '1' ;  
  end procedure ;

  procedure StrobeAck  ( signal Ack  : Out std_logic ) is
  begin
    -- Model done, drive rising edge on Ack
    Ack        <= '0' ;
    wait for 0 ns ;
    Ack        <= '1' ;
  end procedure ;


end TbUtilPkg ;

