-- ----------------------------------------------------------------------------
-- Title        : Standard VITAL Memory Package
--              :
-- Library      : Vital_Memory
--              :
-- Developers   : IEEE DASC Timing Working Group (TWG), PAR 1076.4
--              : Ekambaram Balaji, LSI Logic Corporation
--              : Jose De Castro, Consultant
--              : Prakash Bare, GDA Technologies
--              : William Yam, LSI Logic Corporation
--              : Dennis Brophy, Model Technology
--              :
-- Purpose      : This packages defines standard types, constants, functions
--              : and procedures for use in developing ASIC memory models.
--              :  
-- ----------------------------------------------------------------------------
--
-- ----------------------------------------------------------------------------
-- Modification History : 
-- ----------------------------------------------------------------------------
-- Ver:|Auth:| Date:| Changes Made:
-- 0.1 | eb  |071796| First prototye as part of VITAL memory proposal
-- 0.2 | jdc |012897| Initial prototyping with proposed MTM scheme 
-- 0.3 | jdc |090297| Extensive updates for TAG review (functional)
-- 0.4 | eb  |091597| Changed naming conventions for VitalMemoryTable 
--     |     |      | Added interface of VitalMemoryCrossPorts() & 
--     |     |      | VitalMemoryViolation().
-- 0.5 | jdc |092997| Completed naming changes thoughout package body.
--     |     |      | Testing with simgle port test model looks ok.
-- 0.6 | jdc |121797| Major updates to the packages:
--     |     |      | - Implement VitalMemoryCrossPorts()
--     |     |      |   - Use new VitalAddressValueType
--     |     |      |   - Use new VitalCrossPortModeType enum
--     |     |      |   - Overloading without SamePort args
--     |     |      |   - Honor erroneous address values
--     |     |      |   - Honor ports disabled with 'Z'
--     |     |      | - Implement implicit read 'M' table symbol
--     |     |      | - Cleanup buses to use (H DOWNTO L)
--     |     |      | - Message control via MsgOn,HeaderMsg,PortName
--     |     |      | - Tested with 1P1RW,2P2RW,4P2R2W,4P4RW cases
-- 0.7 | jdc |052698| Bug fixes to the packages:
--     |     |      | - Fix failure with negative Address values
--     |     |      | - Added debug messages for VMT table search
--     |     |      | - Remove 'S' for action column (only 's')
--     |     |      | - Remove 's' for response column (only 'S')
--     |     |      | - Remove 'X' for action and response columns
-- 0.8 | jdc |061298| Implemented VitalMemoryViolation()
--     |     |      | - Minimal functionality violation tables
--     |     |      | - Missing:
--     |     |      |   - Cannot handle wide violation variables
--     |     |      |   - Cannot handle sub-word cases
--     |     |      | Fixed IIC version of MemoryMatch
--     |     |      | Fixed 'M' vs 'm' switched on debug output
--     |     |      | TO BE DONE:
--     |     |      | - Implement 'd' corrupting a single bit
--     |     |      | - Implement 'D' corrupting a single bit
-- 0.9 |eb/sc|080498| Added UNDEF value for VitalPortFlagType
-- 0.10|eb/sc|080798| Added CORRUPT value for VitalPortFlagType
-- 0.11|eb/sc|081798| Added overloaded function interface for
--     |     |      |       VitalDeclareMemory
-- 0.14| jdc |113198| Merging of memory functionality and version
--     |     |      | 1.4 9/17/98 of timing package from Prakash
-- 0.15| jdc |120198| Major development of VMV functionality
-- 0.16| jdc |120298| Complete VMV functionlality for initial testing
--     |     |      | - New ViolationTableCorruptMask() procedure
--     |     |      | - New MemoryTableCorruptMask() procedure
--     |     |      | - HandleMemoryAction():
--     |     |      |   - Removed DataOutBus bogus output
--     |     |      |   - Replaced DataOutTmp with DataInTmp
--     |     |      |   - Added CorruptMask input handling
--     |     |      |   - Implemented 'd','D' using CorruptMask
--     |     |      |   - CorruptMask on 'd','C','L','D','E'
--     |     |      |   - CorruptMask ignored on 'c','l','e'
--     |     |      |   - Changed 'l','d','e' to set PortFlag to CORRUPT
--     |     |      |   - Changed 'L','D','E' to set PortFlag to CORRUPT
--     |     |      |   - Changed 'c','l','d','e' to ignore HighBit, LowBit
--     |     |      |   - Changed 'C','L','D','E' to use HighBit, LowBit
--     |     |      | - HandleDataAction():
--     |     |      |   - Added CorruptMask input handling
--     |     |      |   - Implemented 'd','D' using CorruptMask
--     |     |      |   - CorruptMask on 'd','C','L','D','E'
--     |     |      |   - CorruptMask ignored on 'l','e'
--     |     |      |   - Changed 'l','d','e' to set PortFlag to CORRUPT
--     |     |      |   - Changed 'L','D','E' to set PortFlag to CORRUPT
--     |     |      |   - Changed 'l','d','e' to ignore HighBit, LowBit
--     |     |      |   - Changed 'L','D','E' to use HighBit, LowBit
--     |     |      | - MemoryTableLookUp():
--     |     |      |   - Added MsgOn table debug output
--     |     |      |   - Uses new MemoryTableCorruptMask()
--     |     |      | - ViolationTableLookUp():
--     |     |      |   - Uses new ViolationTableCorruptMask()
-- 0.17| jdc |120898| - Added VitalMemoryViolationSymbolType,
--     |     |      |   VitalMemoryViolationTableType data 
--     |     |      |   types but not used yet (need to discuss)
--     |     |      | - Added overload for VitalMemoryViolation()
--     |     |      |   which does not have array flags
--     |     |      | - Bug fixes for VMV functionality:
--     |     |      |   - ViolationTableLookUp() not handling '-' in
--     |     |      |     scalar violation matching
--     |     |      |   - VitalMemoryViolation() now normalizes
--     |     |      |     VFlagArrayTmp'LEFT as LSB before calling
--     |     |      |     ViolationTableLookUp() for proper scanning
--     |     |      |   - ViolationTableCorruptMask() had to remove 
--     |     |      |     normalization of CorruptMaskTmp and
--     |     |      |     ViolMaskTmp for proper MSB:LSB corruption
--     |     |      |   - HandleMemoryAction(), HandleDataAction() 
--     |     |      |     - Removed 'D','E' since not being used
--     |     |      |     - Use XOR instead of OR for corrupt masks
--     |     |      |     - Now 'd' is sensitive to HighBit, LowBit
--     |     |      |   - Fixed LowBit overflow in bit writeable case
--     |     |      |     - MemoryTableCorruptMask()
--     |     |      |     - ViolationTableCorruptMask()
--     |     |      |     - VitalMemoryTable()
--     |     |      |     - VitalMemoryCrossPorts()
--     |     |      |   - Fixed VitalMemoryViolation() failing on
--     |     |      |     error AddressValue from earlier VMT()
--     |     |      | - Minor cleanup of code formatting
-- 0.18| jdc |032599| - In VitalDeclareMemory()
--     |     |      |   - Added BinaryLoadFile formal arg and 
--     |     |      |     modified LoadMemory() to handle bin
--     |     |      | - Added NOCHANGE to VitalPortFlagType
--     |     |      | - For VitalCrossPortModeType
--     |     |      |   - Added CpContention enum
--     |     |      | - In HandleDataAction()
--     |     |      |   - Set PortFlag := NOCHANGE for 'S' 
--     |     |      | - In HandleMemoryAction()
--     |     |      |   - Set PortFlag := NOCHANGE for 's' 
--     |     |      | - In VitalMemoryTable() and
--     |     |      |   VitalMemoryViolation()
--     |     |      |   - Honor PortFlag = NOCHANGE returned 
--     |     |      |     from HandleMemoryAction()
--     |     |      | - In VitalMemoryCrossPorts()
--     |     |      |   - Fixed Address = AddressJ for all
--     |     |      |     conditions of DoWrCont & DoCpRead 
--     |     |      |   - Handle CpContention like WrContOnly
--     |     |      |     under CpReadOnly conditions, with
--     |     |      |     associated memory message changes
--     |     |      |   - Handle PortFlag = NOCHANGE like
--     |     |      |     PortFlag = READ for actions
--     |     |      | - Modeling change:
--     |     |      |   - Need to init PortFlag every delta
--     |     |      |     PortFlag_A := (OTHES => UNDEF);
--     |     |      | - Updated InternalTimingCheck code
-- 0.19| jdc |042599| - Fixes for bit-writeable cases
--     |     |      | - Check PortFlag after HandleDataAction
--     |     |      |   in VitalMemoryViolation()
-- 0.20| jdc |042599| - Merge PortFlag changes from Prakash
--     |     |      |   and Willian:
--     |     |      |     VitalMemorySchedulePathDelay()
--     |     |      |     VitalMemoryExpandPortFlag()
-- 0.21| jdc |072199| - Changed VitalCrossPortModeType enums,
--     |     |      |   added new CpReadAndReadContention.
--     |     |      | - Fixed VitalMemoryCrossPorts() parameter
--     |     |      |   SamePortFlag to INOUT so that it can
--     |     |      |   set CORRUPT or READ value.
--     |     |      | - Fixed VitalMemoryTable() where PortFlag
--     |     |      |   setting by HandleDataAction() is being
--     |     |      |   ignored when HandleMemoryAction() sets
--     |     |      |   PortFlagTmp to NOCHANGE.
--     |     |      | - Fixed VitalMemoryViolation() to set
--     |     |      |   all bits of PortFlag when violating.
-- 0.22| jdc |072399| - Added HIGHZ to PortFlagType. HandleData
--     |     |      |   checks whether the previous state is HIGHZ.
--     |     |      |   If yes then portFlag should be NOCHANGE
--     |     |      |   for VMPD to ignore IORetain corruption.
--     |     |      |   The idea is that the first Z should be
--     |     |      |   propagated but later ones should be ignored.
--     |     |      |
-- 0.23| jdc |100499| - Took code checked in by Dennis 09/28/99
--     |     |      | - Changed VitalPortFlagType to record of
--     |     |      |   new VitalPortStateType to hold current,
--     |     |      |   previous values and separate disable.
--     |     |      |   Also created VitalDefaultPortFlag const.
--     |     |      |   Removed usage of PortFlag NOCHANGE
--     |     |      | - VitalMemoryTable() changes:
--     |     |      |   Optimized return when all curr = prev
--     |     |      |   AddressValue is now INOUT to optimize
--     |     |      |   Transfer PF.MemoryCurrent to MemoryPrevious
--     |     |      |   Transfer PF.DataCurrent to DataPrevious
--     |     |      |   Reset PF.OutputDisable to FALSE
--     |     |      |   Expects PortFlag init in declaration
--     |     |      |   No need to init PortFlag every delta
--     |     |      | - VitalMemorySchedulePathDelay() changes:
--     |     |      |   Initialize with VitalDefaultPortFlag
--     |     |      |   Check PortFlag.OutputDisable
--     |     |      | - HandleMemoryAction() changes:
--     |     |      |   Set value of PortFlag.MemoryCurrent
--     |     |      |   Never set PortFlag.OutputDisable
--     |     |      | - HandleDataAction() changes:
--     |     |      |   Set value of PortFlag.DataCurrent
--     |     |      |   Set PortFlag.DataCurrent for HIGHZ
--     |     |      | - VitalMemoryCrossPorts() changes:
--     |     |      |   Check/set value of PF.MemoryCurrent
--     |     |      |   Check value of PF.OutputDisable
--     |     |      | - VitalMemoryViolation() changes:
--     |     |      |   Fixed bug - not reading inout PF value
--     |     |      |   Clean up setting of PortFlag
-- 0.24| jdc |100899| - Modified update of PF.OutputDisable
--     |     |      |   to correctly accomodate 2P1W1R case:
--     |     |      |   the read port should not exhibit
--     |     |      |   IO retain corrupt when reading
--     |     |      |   addr unrelated to addr being written.
-- 0.25| jdc |100999| - VitalMemoryViolation() change:
--     |     |      |   Fixed bug with RDNWR mode incorrectly
--     |     |      |   updating the PF.OutputDisable
-- 0.26| jdc |100999| - VitalMemoryCrossPorts() change:
--     |     |      |   Fixed bugs with update of PF
-- 0.27| jdc |101499| - VitalMemoryCrossPorts() change:
--     |     |      |   Added DoRdWrCont message (ErrMcpRdWrCo,
--     |     |      |   Memory cross port read/write data only
--     |     |      |   contention)
--     |     |      | - VitalMemoryTable() change:
--     |     |      |   Set PF.OutputDisable := TRUE for the
--     |     |      |   optimized cases.
-- 0.28| pb  |112399| - Added 8 VMPD procedures for vector 
--     |     |      |   PathCondition support. Now the total
--     |     |      |   number of overloadings for VMPD is 24.
--     |     |      | - Number of overloadings for SetupHold
--     |     |      |   procedures increased to 5. Scalar violations
--     |     |      |   are not supported anymore. Vector checkEnabled
--     |     |      |   support is provided through the new overloading
-- 0.29| jdc |120999| - HandleMemoryAction() HandleDataAction()
--     |     |      |   Reinstated 'D' and 'E' actions but
--     |     |      |   with new PortFlagType
--     |     |      | - Updated file handling syntax, must compile
--     |     |      |   with -93 syntax now.
-- 0.30| jdc |022300| - Formated for 80 column max width
-- ----------------------------------------------------------------------------

LIBRARY IEEE;
USE     IEEE.STD_LOGIC_1164.ALL;
USE     IEEE.Vital_Timing.all;
USE     IEEE.Vital_Primitives.all;

LIBRARY STD;
USE     STD.TEXTIO.ALL;

-- ----------------------------------------------------------------------------
PACKAGE BODY Vital_Memory IS

-- ----------------------------------------------------------------------------
-- Timing Section
-- ----------------------------------------------------------------------------

FILE LogFile : TEXT OPEN write_mode IS "delayLog";
FILE Output  : TEXT OPEN write_mode IS "STD_OUTPUT";

-- Added for turning off the debug msg..
CONSTANT PrintDebugMsg : STD_ULOGIC := '0';
                         -- '0' - don't print in STD OUTPUT
                         -- '1' - print in STD OUTPUT

-- Type and constant definitions for type conversion.
TYPE MVL9_TO_CHAR_TBL IS ARRAY (STD_ULOGIC) OF character;

--constant MVL9_to_char: MVL9_TO_CHAR_TBL := "UX01ZWLH-";
CONSTANT MVL9_to_char: MVL9_TO_CHAR_TBL := "XX01ZX010";

-- ----------------------------------------------------------------------------
-- STD_LOGIC WRITE UTILITIES
-- ----------------------------------------------------------------------------
PROCEDURE WRITE(
  l       : INOUT line;
  val     : IN std_logic_vector;
  justify : IN side := right; 
  field   : IN width := 0
) IS
  VARIABLE invect : std_logic_vector(val'LENGTH DOWNTO 1);
  VARIABLE ins    : STRING(val'LENGTH DOWNTO 1);
BEGIN
  invect := val;
  FOR I IN invect'length DOWNTO 1 LOOP
     ins(I) := MVL9_to_char(invect(I));
  END LOOP;
  WRITE(L, ins, justify, field);
END;

PROCEDURE WRITE(
  l       : INOUT line; 
  val     : IN std_ulogic;
  justify : IN side := right;
  field   : in width := 0
) IS
  VARIABLE ins : CHARACTER;
BEGIN
  ins := MVL9_to_char(val);
  WRITE(L, ins, justify, field);
END;

-- ----------------------------------------------------------------------------
PROCEDURE DelayValue(
  InputTime : IN TIME ;
  outline   : INOUT LINE
) IS
  CONSTANT header : STRING := "TIME'HIGH";
BEGIN
  IF(InputTime = TIME'HIGH) THEN
    WRITE(outline, header);
  ELSE
    WRITE(outline, InputTime);
  END IF;
END DelayValue;

-- ----------------------------------------------------------------------------
PROCEDURE PrintScheduleDataArray (
  ScheduleDataArray : IN VitalMemoryScheduleDataVectorType
) IS
  VARIABLE outline1 : LINE;
  VARIABLE outline2 : LINE;
  VARIABLE value    : TIME;
  CONSTANT empty    : STRING := "      ";
  CONSTANT header1  : STRING := "i    Age     PropDly  RetainDly"; 
  CONSTANT header2  : STRING := "i  Sc.Value  Output  Lastvalue  Sc.Time";
BEGIN
  WRITE (outline1, empty);
  WRITE (outline1, NOW);
  outline2 := outline1; 
  WRITELINE (LogFile, outline1);
  IF (PrintDebugMsg = '1') THEN
    WRITELINE (output, outline2);
  END IF;
  WRITE (outline1, header1);
  outline2 := outline1; 
  WRITELINE (LogFile, outline1);
  IF (PrintDebugMsg = '1') THEN
    WRITELINE (output, outline2);
  END IF;
  FOR i IN ScheduleDataArray'RANGE LOOP
    WRITE (outline1, i );
    WRITE (outline1, empty);
    DelayValue(ScheduleDataArray(i).InputAge, outline1);  
    WRITE (outline1, empty);
    DelayValue(ScheduleDataArray(i).PropDelay, outline1);  
    WRITE (outline1, empty);
    DelayValue(ScheduleDataArray(i).OutputRetainDelay, outline1);  
    outline2 := outline1; 
    WRITELINE (LogFile, outline1);
    IF (PrintDebugMsg = '1') THEN
      WRITELINE (output, outline2);
    END IF;
  END LOOP;
  WRITE (outline1, header2);
  outline2 := outline1; 
  WRITELINE (LogFile, outline1);
  IF (PrintDebugMsg = '1') THEN
    WRITELINE (output, outline2);
  END IF;
  FOR i IN ScheduleDataArray'RANGE LOOP
    WRITE (outline1, i ); 
    WRITE (outline1, empty);
    WRITE (outline1, ScheduleDataArray(i).ScheduleValue); 
    WRITE (outline1, empty);
    WRITE (outline1, ScheduleDataArray(i).OutputData);
    WRITE (outline1, empty);
    WRITE (outline1, ScheduleDataArray(i).LastOutputValue );
    WRITE (outline1, empty);
    DelayValue(ScheduleDataArray(i).ScheduleTime, outline1);
    outline2 := outline1; 
    WRITELINE (LogFile, outline1);
    IF (PrintDebugMsg = '1') THEN
      WRITELINE (output, outline2);
    END IF;
  END LOOP;
  WRITE (outline1, empty);
  WRITE (outline2, empty);
  WRITELINE (LogFile, outline1);
  IF (PrintDebugMsg = '1') THEN
    WRITELINE (Output, outline2);
  END IF;
END PrintScheduleDataArray;

-- ----------------------------------------------------------------------------
PROCEDURE PrintArcType (
  ArcType : IN VitalMemoryArcType
) IS
  VARIABLE outline1, outline2 : LINE;
  CONSTANT empty : STRING := " ";
  CONSTANT cross : STRING := "CrossArc";
  CONSTANT para : STRING := "ParallelArc";
  CONSTANT sub : STRING := "SubWordArc";
  CONSTANT Header1 : STRING := "Path considered @ ";
  CONSTANT Header2 : STRING := " is  ";
BEGIN
  WRITELINE (LogFile, outline1);
  WRITE (outline1, header1);
  WRITE (outline1, NOW);
  WRITE (outline1, empty);
  WRITE (outline1, header2);
  WRITE (outline1, empty);
  case ArcType is
  WHEN CrossArc =>
    WRITE (outline1, cross);
  WHEN ParallelArc =>
    WRITE (outline1, para);
  WHEN SubwordArc =>
    WRITE (outline1, sub);
  END CASE;
  outline2 := outline1 ;
  -- Appears on STD OUT
  IF (PrintDebugMsg = '1') THEN
    WRITELINE (Output, outline1);
  END IF;
  WRITELINE (LogFile, outline2);
END PrintArcType;

-- ----------------------------------------------------------------------------
-- This returns the value picked from the delay array
-- ----------------------------------------------------------------------------
PROCEDURE PrintDelay (
  outbitpos         : IN INTEGER;
  InputArrayLow     : IN INTEGER; 
  InputArrayHigh    : IN INTEGER;
  debugprop         : IN VitalTimeArrayT;
  debugretain       : IN VitalTimeArrayT
) IS
  VARIABLE outline1 : LINE;
  VARIABLE outline2 : LINE;
  VARIABLE outline3 : LINE;
  VARIABLE outline4 : LINE;
  VARIABLE outline5 : LINE;
  VARIABLE outline6 : LINE;
  CONSTANT empty    : STRING := "  ";
  CONSTANT empty5   : STRING := "  ";
  CONSTANT header1  : STRING := "Prop. delays : ";
  CONSTANT header2  : STRING := "Retain delays : ";
  CONSTANT header3  : STRING := "output bit :  ";
BEGIN
  WRITE(outline1, header3);
  WRITE(outline1, outbitpos);
  outline2 := outline1;
  WRITELINE(LogFile, outline1);
  IF (PrintDebugMsg = '1') THEN
    WRITELINE(output, outline2);
  END IF;
  WRITE(outline1, header1);
  WRITE (outline1, empty5);
  FOR i IN InputArrayHigh DOWNTO InputArrayLow LOOP
    DelayValue(debugprop(i), outline1);
    WRITE(outline1, empty);
  END LOOP;
  outline2 := outline1;
  WRITELINE(LogFile, outline1);
  IF (PrintDebugMsg = '1') THEN
    WRITELINE(output, outline2);
  END IF;
  WRITE(outline1, header2);
  WRITE (outline1, empty5);
  FOR i in InputArrayHigh DOWNTO InputArrayLow LOOP
    DelayValue(debugretain(i), outline1);
    WRITE(outline1, empty);
  END LOOP;
  outline2 := outline1;
  WRITELINE(LogFile, outline1);
  IF (PrintDebugMsg = '1') THEN
    WRITELINE(output, outline2);
  END IF;
END PrintDelay;

-- ----------------------------------------------------------------------------
PROCEDURE DebugMsg1 IS
    CONSTANT header1:STRING:= "******************************************";
    CONSTANT header2 :STRING:="Entering the process because of an i/p change";
    variable outline1, outline2 : LINE;
BEGIN
    WRITE(outline1, header1);
    outline2 := outline1;
    WRITELINE (Logfile, outline1); 
    IF (PrintDebugMsg = '1') THEN
       WRITELINE (output, outline2); 
    END IF;
    WRITE(outline1, header2);
    outline2 := outline1;
    WRITELINE (Logfile, outline1); 
    IF (PrintDebugMsg = '1') THEN 
       WRITELINE (output, outline2); 
    END IF;
    WRITE(outline1, header1);
    outline2 := outline1;
    WRITELINE (Logfile, outline1);
    IF (PrintDebugMsg = '1') THEN 
       WRITELINE (output, outline2); 
    END IF;
END DebugMsg1;

-- ----------------------------------------------------------------------------
PROCEDURE ScheduleDebugMsg IS
  CONSTANT header1  : STRING := "******************************************";
  CONSTANT header2  : STRING := "Finished executing all the procedures";
  VARIABLE outline1 : LINE;
  VARIABLE outline2 : LINE;
BEGIN
  WRITE(outline1, header1);
  outline2 := outline1;
  IF (PrintDebugMsg = '1') THEN 
    WRITELINE (output, outline2); 
  END IF;
  WRITELINE (Logfile, outline1); 
  WRITE(outline1, header2);
  outline2 := outline1;
  IF (PrintDebugMsg = '1') THEN 
    WRITELINE (output, outline2);
  END IF; 
  WRITELINE (Logfile, outline1);    
  WRITE(outline1, header1);
  outline2 := outline1;
  IF (PrintDebugMsg = '1') THEN 
    WRITELINE (output, outline2); 
  END IF;
  WRITELINE (Logfile, outline1);
END ScheduleDebugMsg;   

-- ----------------------------------------------------------------------------
PROCEDURE PrintInputName(
  InputSignalName : IN STRING
) IS
  VARIABLE outline1 : LINE;
  VARIABLE outline2 : LINE;
  CONSTANT header1  : STRING := "***Changing input is ";
  CONSTANT header2  : STRING := "(";
  CONSTANT header3  : STRING := ")";
  CONSTANT header4  : STRING :=  "****";
  CONSTANT header5  : STRING := "******************************************";
  CONSTANT header6  : STRING:="Entering the process because of an i/p change";
  CONSTANT empty    : STRING := "  ";
BEGIN
  WRITE(outline1, header5);
  outline2 := outline1;
  WRITELINE (output, outline1); 
  WRITELINE (Logfile, outline2); 
  WRITE(outline1, header6);
  outline2 := outline1;
  WRITELINE (output, outline1); 
  WRITELINE (Logfile, outline2); 
  WRITE(outline1, header5);
  outline2 := outline1;
  WRITELINE (output, outline1); 
  WRITELINE (Logfile, outline2); 
  WRITE(outline1, header1);
  WRITE(outline1, InputSignalName); 
  WRITE(outline1, empty); 
  WRITE(outline1, now);
  WRITE(outline1, empty); 
  WRITE(outline1, header4);
  WRITELINE (output, outline1); 
  WRITELINE (Logfile, outline2);  
END PrintInputName;

-- ----------------------------------------------------------------------------
PROCEDURE PrintInputChangeTime(
  ChangeTimeArray : IN VitalTimeArrayT
) IS
  VARIABLE outline1 : LINE;
  VARIABLE outline2 : LINE;
  CONSTANT header5  : STRING := "*************************************";
  CONSTANT header6  : STRING:="ChangeTime Array : ";
  CONSTANT empty    : STRING := "  ";
BEGIN
  WRITE(outline1, header5);
  outline2 := outline1;
  IF (PrintDebugMsg = '1') THEN 
    WRITELINE (output, outline2); 
  END IF;
  WRITELINE (Logfile, outline1); 
  WRITE(outline1, header6);
  FOR i in ChangeTimeArray'range LOOP
    WRITE(outline1, ChangeTimeArray(i));
    WRITE(outline1, empty);
  END LOOP;
  outline2 := outline1;
  IF (PrintDebugMsg = '1') THEN  
    WRITELINE (output, outline2); 
  END IF;
  WRITELINE (Logfile, outline1); 
  WRITE(outline1, header5);
  outline2 := outline1;
  IF (PrintDebugMsg = '1') THEN  
    WRITELINE (output, outline2); 
  END IF;
  WRITELINE (Logfile, outline1);  
END PrintInputChangeTime;

-- ----------------------------------------------------------------------------
PROCEDURE PrintInputChangeTime(
  ChangeTime : IN Time
) IS
  VARIABLE ChangeTimeArray : VitalTimeArrayT(0 DOWNTO 0);
BEGIN
  ChangeTimeArray(0) := ChangeTime;
  PrintInputChangeTime(ChangeTimeArray);
END PrintInputChangeTime;

-- ----------------------------------------------------------------------------
-- for debug purpose
CONSTANT MaxNoInputBits : INTEGER := 1000;

TYPE VitalMemoryDelayType IS RECORD
  PropDelay         : TIME;
  OutputRetainDelay : TIME;
END RECORD;

-- ----------------------------------------------------------------------------
-- PROCEDURE:   IntToStr
--
-- PARAMETERS:  InputInt     - Integer to be converted to String.
--              ResultStr    - String buffer for converted Integer
--              AppendPos    - Position in buffer to place result
--
-- DESCRIPTION: This procedure is used to convert an input integer
--              into a string representation. The converted string
--              may be placed at a specific position in the result
--              buffer.
--
-- ----------------------------------------------------------------------------

PROCEDURE IntToStr ( 
  InputInt    : IN INTEGER ;
  ResultStr   : INOUT STRING ( 1 TO 256) ;
  AppendPos   : INOUT NATURAL
) IS
  -- Look-up table.  Given an int, we can get the character.
  TYPE     integer_table_type IS  ARRAY (0 TO 9) OF CHARACTER ;
  CONSTANT integer_table : integer_table_type :=
    ('0', '1', '2', '3', '4', '5', '6', '7', '8', '9') ;
  --  Local variables used in this function.
  VARIABLE   inpVal      : INTEGER := inputInt ;
  VARIABLE   divisor     : INTEGER := 10 ;
  VARIABLE   tmpStrIndex : INTEGER := 1 ;
  VARIABLE   tmpStr      : STRING ( 1 TO 256 ) ;
BEGIN
  IF ( inpVal = 0 ) THEN
    tmpStr(tmpStrIndex) := integer_table ( 0 ) ;
    tmpStrIndex := tmpStrIndex + 1 ;
  ELSE
    WHILE ( inpVal > 0 )  LOOP
      tmpStr(tmpStrIndex) := integer_table (inpVal mod divisor);
      tmpStrIndex := tmpStrIndex + 1 ;
      inpVal := inpVal / divisor ;
    END LOOP ;
  END IF ;
  IF (appendPos /= 1 ) THEN
    resultStr(appendPos)  :=  ',' ;
    appendPos := appendPos + 1 ;
  END IF ;

  FOR i IN tmpStrIndex-1 DOWNTO 1  LOOP
    resultStr(appendPos)  :=  tmpStr(i) ;
    appendPos := appendPos + 1 ;
  END LOOP ;
END IntToStr ;

-- ----------------------------------------------------------------------------
TYPE CheckType IS (
  SetupCheck,
  HoldCheck,
  RecoveryCheck,
  RemovalCheck,
  PulseWidCheck,
  PeriodCheck 
);

TYPE CheckInfoType IS RECORD
  Violation : BOOLEAN;
  CheckKind : CheckType;
  ObsTime   : TIME;
  ExpTime   : TIME;
  DetTime   : TIME;
  State     : X01;
END RECORD;

TYPE LogicCvtTableType IS ARRAY (std_ulogic) OF CHARACTER; 
TYPE HiLoStrType IS ARRAY (std_ulogic RANGE 'X' TO '1') OF STRING(1 TO 4); 

CONSTANT LogicCvtTable : LogicCvtTableType 
               := ( 'U', 'X', '0', '1', 'Z', 'W', 'L', 'H', '-'); 
CONSTANT HiLoStr     : HiLoStrType := ("  X ", " Low", "High" ); 

TYPE EdgeSymbolMatchType IS ARRAY (X01,X01,VitalEdgeSymbolType) OF BOOLEAN;

-- last value, present value, edge symbol
CONSTANT EdgeSymbolMatch : EdgeSymbolMatchType :=  
  (
  'X' =>
    ( 'X'=>(                                 OTHERS => FALSE),
      '0'=>('N'|'F'|'v'|'E'|'D'|'*' => TRUE, OTHERS => FALSE ),
      '1'=>('P'|'R'|'^'|'E'|'A'|'*' => TRUE, OTHERS => FALSE ) 
    ),
  '0' =>
    ( 'X'=>(    'r'|'p'|'R'|'A'|'*' => TRUE, OTHERS => FALSE ),
      '0'=>(                                 OTHERS => FALSE ),
      '1'=>(    '/'|'P'|'p'|'R'|'*' => TRUE, OTHERS => FALSE ) 
    ),
  '1' =>
    ( 'X'=>(    'f'|'n'|'F'|'D'|'*' => TRUE, OTHERS => FALSE ),
      '0'=>(    '\'|'N'|'n'|'F'|'*' => TRUE, OTHERS => FALSE ),
      '1'=>(                                 OTHERS => FALSE ) 
    ) 
  );

-- ----------------------------------------------------------------------------
FUNCTION Minimum (
  CONSTANT t1, t2 : IN TIME 
) RETURN TIME IS
BEGIN
  IF (t1 < t2) THEN RETURN (t1); ELSE RETURN (t2); END IF;
END Minimum;

-- ----------------------------------------------------------------------------
FUNCTION Maximum (
  CONSTANT t1, t2 : IN TIME
) RETURN TIME IS
BEGIN
  IF (t1 < t2) THEN RETURN (t2); ELSE RETURN (t1); END IF;
END Maximum;

-- ----------------------------------------------------------------------------
-- FUNCTION:    VitalMemoryCalcDelay
-- Description: Select Transition dependent Delay. 
--              Used internally by VitalMemorySelectDelay.        
-- ----------------------------------------------------------------------------
FUNCTION VitalMemoryCalcDelay (
  CONSTANT NewVal : IN STD_ULOGIC := 'X';
  CONSTANT OldVal : IN STD_ULOGIC := 'X';
  CONSTANT Delay  : IN VitalDelayType01ZX
) RETURN VitalMemoryDelayType IS
  VARIABLE Result : VitalMemoryDelayType;
BEGIN
  CASE Oldval IS
  WHEN '0' | 'L' =>
    CASE Newval IS
      WHEN '0' | 'L' =>
        Result.PropDelay := Delay(tr10);
      WHEN '1' | 'H' =>
        Result.PropDelay := Delay(tr01);
      WHEN 'Z' =>
        Result.PropDelay := Delay(tr0Z);
      WHEN OTHERS =>
        Result.PropDelay := Minimum(Delay(tr01), Delay(tr0Z));
    END CASE;
    Result.OutputRetainDelay := Delay(tr0X);
  WHEN '1' | 'H' =>
    CASE Newval IS
      WHEN '0' | 'L' =>
        Result.PropDelay := Delay(tr10);
      WHEN '1' | 'H' =>
        Result.PropDelay := Delay(tr01);
      WHEN 'Z' =>
        Result.PropDelay := Delay(tr1Z);
      WHEN OTHERS =>
        Result.PropDelay := Minimum(Delay(tr10), Delay(tr1Z));
    END CASE;
    Result.OutputRetainDelay := Delay(tr1X);
  WHEN 'Z' =>
    CASE Newval IS
      WHEN '0' | 'L' =>
        Result.PropDelay := Delay(trZ0);
      WHEN '1' | 'H' =>
        Result.PropDelay := Delay(trZ1);
      WHEN 'Z' =>
        Result.PropDelay := Maximum(Delay(tr1Z), Delay(tr0Z));
      WHEN OTHERS =>
        Result.PropDelay := Minimum(Delay(trZ1), Delay(trZ0));
      END CASE;
      Result.OutputRetainDelay := Delay(trZX);
  WHEN OTHERS =>
    CASE Newval IS
      WHEN '0' | 'L' =>
        Result.PropDelay := Maximum(Delay(tr10), Delay(trZ0));
      WHEN '1' | 'H' =>
        Result.PropDelay := Maximum(Delay(tr01), Delay(trZ1));
      WHEN 'Z' =>
        Result.PropDelay := Maximum(Delay(tr1Z), Delay(tr0Z));
      WHEN OTHERS => 
        Result.PropDelay := Maximum(Delay(tr10), Delay(tr01));
    END CASE;
    Result.OutputRetainDelay := Minimum(Delay(tr1X), Delay(tr0X));
  END CASE;
  RETURN Result;
END VitalMemoryCalcDelay;

-- ----------------------------------------------------------------------------
FUNCTION VitalMemoryCalcDelay (
  CONSTANT NewVal : IN STD_ULOGIC   := 'X';
  CONSTANT OldVal : IN STD_ULOGIC   := 'X';
  CONSTANT Delay  : IN VitalDelayType01Z
) RETURN VitalMemoryDelayType IS
  VARIABLE Result : VitalMemoryDelayType;
BEGIN
CASE Oldval IS
  WHEN '0' | 'L' =>
    CASE Newval IS
      WHEN '0' | 'L' => Result.PropDelay := Delay(tr10);
      WHEN '1' | 'H' => Result.PropDelay := Delay(tr01);
      WHEN OTHERS =>
        Result.PropDelay := Minimum(Delay(tr01), Delay(tr10));
    END CASE;
    Result.OutputRetainDelay := Delay(tr0Z);
  WHEN '1' | 'H' =>
    CASE Newval IS
      WHEN '0' | 'L' => Result.PropDelay := Delay(tr10);
      WHEN '1' | 'H' => Result.PropDelay := Delay(tr01);
      WHEN OTHERS => 
        Result.PropDelay := Minimum(Delay(tr10), Delay(tr01));
    END CASE;
    Result.OutputRetainDelay := Delay(tr1Z);
  WHEN OTHERS =>
    Result.PropDelay := Maximum(Delay(tr10),Delay(tr01));
    Result.OutputRetainDelay := Minimum(Delay(tr1Z),Delay(tr0Z));
  END CASE;
  RETURN Result;
END VitalMemoryCalcDelay;

-- ----------------------------------------------------------------------------
PROCEDURE VitalMemoryUpdateInputChangeTime (
  VARIABLE InputChangeTimeArray : INOUT VitalTimeArrayT;
  SIGNAL   InputSignal          : IN STD_LOGIC_VECTOR;
  VARIABLE NumBitsPerSubword    : INTEGER
) IS
  VARIABLE LastInputValue : STD_LOGIC_VECTOR(InputSignal'LENGTH-1 downto 0);
  VARIABLE InSignalNorm   : STD_LOGIC_VECTOR(InputSignal'LENGTH-1 downto 0);
  VARIABLE ChangeTimeNorm : VitalTimeArrayT(InputSignal'LENGTH-1 downto 0);
  VARIABLE BitsPerWord    : INTEGER;
BEGIN
  LastInputValue := InputSignal'LAST_VALUE;
  IF NumBitsPerSubword = DefaultNumBitsPerSubword THEN
    BitsPerWord := InputSignal'LENGTH;
  ELSE
    BitsPerWord := NumBitsPerSubword;
  END IF;

  FOR i IN InSignalNorm'RANGE LOOP
    IF (InSignalNorm(i) /= LastInputValue(i)) THEN
      ChangeTimeNorm(i/BitsPerWord) := NOW - InputSignal'LAST_EVENT;
    ELSE
      ChangeTimeNorm(i/BitsPerWord) := InputChangeTimeArray(i);
    END IF;
  END LOOP;

  FOR i IN ChangeTimeNorm'RANGE LOOP
    ChangeTimeNorm(i) := ChangeTimeNorm(i/BitsPerword);
  END LOOP;

  InputChangeTimeArray := ChangeTimeNorm;

  -- for debug purpose only    
  PrintInputChangeTime(InputChangeTimeArray);
END VitalMemoryUpdateInputChangeTime;

-- ----------------------------------------------------------------------------
-- Procedure:   VitalMemoryUpdateInputChangeTime
-- Description: Time since previous event for each bit of the input
-- ----------------------------------------------------------------------------
PROCEDURE VitalMemoryUpdateInputChangeTime (
  VARIABLE InputChangeTimeArray : INOUT VitalTimeArrayT;
  SIGNAL   InputSignal          : IN STD_LOGIC_VECTOR
) IS
  VARIABLE LastInputValue  : STD_LOGIC_VECTOR(InputSignal'RANGE) ;
BEGIN
  LastInputValue := InputSignal'LAST_VALUE;        
  FOR i IN InputSignal'RANGE LOOP
    IF (InputSignal(i) /= LastInputValue(i)) THEN
      InputChangeTimeArray(i) := NOW - InputSignal'LAST_EVENT;
    END IF;
  END LOOP;
  -- for debug purpose only    
  PrintInputChangeTime(InputChangeTimeArray);
END VitalMemoryUpdateInputChangeTime;

-- ----------------------------------------------------------------------------
PROCEDURE VitalMemoryUpdateInputChangeTime (
  VARIABLE InputChangeTime      : INOUT TIME;
  SIGNAL   InputSignal          : IN STD_ULOGIC
) IS
BEGIN
  InputChangeTime := NOW - InputSignal'LAST_EVENT;
  -- for debug purpose only    
  PrintInputChangeTime(InputChangeTime);
END VitalMemoryUpdateInputChangeTime;

-- ----------------------------------------------------------------------------
PROCEDURE VitalMemoryExpandPortFlag (
  CONSTANT PortFlag             : IN  VitalPortFlagVectorType;
  CONSTANT NumBitsPerSubword    : IN  INTEGER;
  VARIABLE ExpandedPortFlag       : OUT VitalPortFlagVectorType
) IS
  VARIABLE PortFlagNorm : VitalPortFlagVectorType(
            PortFlag'LENGTH-1 downto 0) := PortFlag;
  VARIABLE ExpandedPortFlagNorm : VitalPortFlagVectorType(
            ExpandedPortFlag'LENGTH-1 downto 0);
  VARIABLE SubwordIndex : INTEGER;
BEGIN
  FOR Index IN INTEGER RANGE 0 to ExpandedPortFlag'LENGTH-1 LOOP
    IF NumBitsPerSubword = DefaultNumBitsPerSubword THEN
      SubwordIndex := 0;
    ELSE
      SubwordIndex := Index / NumBitsPerSubword;
    END IF;
    ExpandedPortFlagNorm(Index) := PortFlagNorm(SubWordIndex);
  END LOOP;
  ExpandedPortFlag := ExpandedPortFlagNorm;                               
END VitalMemoryExpandPortFlag;

-- ----------------------------------------------------------------------------
-- Procedure: VitalMemorySelectDelay
-- Description : Select Propagation Delay. Used internally by 
--               VitalMemoryAddPathDelay.
-- ----------------------------------------------------------------------------

-- ----------------------------------------------------------------------------
-- VitalDelayArrayType01ZX
-- ----------------------------------------------------------------------------
PROCEDURE VitalMemorySelectDelay (
  VARIABLE ScheduleDataArray : INOUT VitalMemoryScheduleDataVectorType;
  VARIABLE InputChangeTimeArray : IN VitalTimeArrayT;
  CONSTANT OutputSignalName     : IN STRING :="";
  CONSTANT PathDelayArray       : IN VitalDelayArrayType01ZX;
  CONSTANT ArcType              : IN VitalMemoryArcType;
  CONSTANT PathConditionArray   : IN VitalBoolArrayT;
  CONSTANT OutputRetainFlag     : IN BOOLEAN
) IS
  VARIABLE InputArrayLow      : INTEGER := 0;
  VARIABLE InputArrayHigh     : INTEGER := 0;
  VARIABLE DelayArrayIndex    : INTEGER := 0;
  VARIABLE NumBitsPerSubWord  : INTEGER := DefaultNumBitsPerSubword;
  VARIABLE NewValue           : STD_ULOGIC;
  VARIABLE OldValue           : STD_ULOGIC;
  VARIABLE OutputLength       : INTEGER := 0;
  VARIABLE OutArrayIndex      : INTEGER;
  VARIABLE PropDelay          : TIME;
  VARIABLE RetainDelay        : TIME;
  VARIABLE CurPropDelay       : TIME;
  VARIABLE CurRetainDelay     : TIME;
  VARIABLE InputAge           : TIME;
  VARIABLE CurInputAge        : TIME;
  VARIABLE InputChangeTimeNorm  : VitalTimeArrayT(
     InputChangeTimeArray'LENGTH-1 downto 0):=InputChangeTimeArray;
  VARIABLE DelayArrayNorm  : VitalDelayArrayType01ZX(
     PathDelayArray'LENGTH-1 downto 0):= PathDelayArray;
  VARIABLE ScheduleDataArrayNorm : VitalMemoryScheduleDatavectorType
     (ScheduleDataArray'LENGTH-1 downto 0):= ScheduleDataArray;
   
  -- for debug purpose
  VARIABLE debugprop : VitalTimeArrayT(MaxNoInputBits-1 downto 0);
  VARIABLE debugretain : VitalTimeArrayT(MaxNoInputBits-1 downto 0);

BEGIN

  -- for debug purpose
  PrintArcType(ArcType);

  OutputLength := ScheduleDataArray'LENGTH;        
  FOR OutBitPos IN 0 to (OutputLength -1) LOOP
    NEXT WHEN PathConditionArray(OutBitPos) = FALSE;

    NEXT WHEN ((ScheduleDataArrayNorm(OutBitPos).ScheduleValue 
      = ScheduleDataArrayNorm(OutBitPos).OutputData) AND 
      (ScheduleDataArrayNorm(OutBitPos).ScheduleTime <= NOW) AND
       (OutputRetainFlag = FALSE ));

    NewValue := ScheduleDataArrayNorm(OutBitPos).OutputData;
    OldValue := ScheduleDataArrayNorm(OutBitPos).LastOutputValue;
    PropDelay :=ScheduleDataArrayNorm(OutBitPos).PropDelay;
    InputAge := ScheduleDataArrayNorm(OutBitPos).InputAge;
    RetainDelay:=ScheduleDataArrayNorm(OutBitPos).OutputRetainDelay;
    NumBitsPerSubWord:=ScheduleDataArrayNorm(OutBitPos).NumBitsPerSubWord;

    CASE ArcType IS
      WHEN ParallelArc =>
        InputArrayLow := OutBitPos;
        InputArrayHigh :=  OutBitPos;
        DelayArrayIndex :=  OutBitPos;
      WHEN CrossArc =>
        InputArrayLow := 0;
        InputArrayHigh := InputChangeTimeArray'LENGTH - 1 ;              
        DelayArrayIndex := OutBitPos; 
      WHEN SubwordArc =>
        InputArrayLow := OutBitPos / NumBitsPerSubWord;
        InputArrayHigh := OutBitPos / NumBitsPerSubWord;
        DelayArrayIndex := OutBitPos + 
            (OutputLength * (OutBitPos / NumBitsPerSubWord));  
    END CASE;

    FOR i IN InputArrayLow TO InputArrayHigh LOOP
      (CurPropDelay,CurRetainDelay) := 
        VitalMemoryCalcDelay (
          NewValue, OldValue, DelayArrayNorm(DelayArrayIndex)
        );
      IF (OutputRetainFlag = FALSE) THEN
        CurRetainDelay := TIME'HIGH;
      END IF;              

      -- for debug purpose
      debugprop(i) := CurPropDelay;
      debugretain(i) := CurRetainDelay;

      IF ArcType = CrossArc THEN
        DelayArrayIndex := DelayArrayIndex + OutputLength;
      END IF;

      -- If there is one input change at a time, then choose the
      -- delay from that input. If there is simultaneous input 
      -- change, then choose the minimum of propagation delays 
 
      IF (InputChangeTimeNorm(i) < 0 ns)THEN
        CurInputAge := TIME'HIGH;
      ELSE
        CurInputAge := NOW - InputChangeTimeNorm(i);
      END IF;            

      IF (CurInputAge  < InputAge)THEN
        PropDelay := CurPropDelay;
        RetainDelay := CurRetainDelay;
        InputAge := CurInputAge;
      ELSIF (CurInputAge = InputAge)THEN
        IF (CurPropDelay < PropDelay) THEN
          PropDelay := CurPropDelay;
        END IF;
        IF (OutputRetainFlag = TRUE) THEN
          IF (CurRetainDelay < RetainDelay) THEN
            RetainDelay := CurRetainDelay;
          END IF;
        END IF;
      END IF;
    END LOOP; 

    -- Store it back to data strucutre
    ScheduleDataArrayNorm(OutBitPos).PropDelay := PropDelay;
    ScheduleDataArrayNorm(OutBitPos).OutputRetainDelay:= RetainDelay;
    ScheduleDataArrayNorm(OutBitPos).InputAge := InputAge;

    -- for debug purpose 
    PrintDelay(outbitPos,InputArrayLow, InputArrayHigh,
      debugprop, debugretain);
  END LOOP;

  ScheduleDataArray := ScheduleDataArrayNorm;

END VitalMemorySelectDelay;

-- ----------------------------------------------------------------------------
-- VitalDelayArrayType01Z
-- ----------------------------------------------------------------------------
PROCEDURE VitalMemorySelectDelay (
  VARIABLE ScheduleDataArray    : INOUT VitalMemoryScheduleDataVectorType;
  VARIABLE InputChangeTimeArray : IN VitalTimeArrayT;
  CONSTANT OutputSignalName     : IN STRING :="";
  CONSTANT PathDelayArray       : IN VitalDelayArrayType01Z;
  CONSTANT ArcType              : IN VitalMemoryArcType;
  CONSTANT PathConditionArray   : IN VitalBoolArrayT;
  CONSTANT OutputRetainFlag     : IN BOOLEAN
) IS
  VARIABLE InputArrayLow      : INTEGER := 0;
  VARIABLE InputArrayHigh     : INTEGER := 0;
  VARIABLE DelayArrayIndex    : INTEGER := 0;
  VARIABLE NumBitsPerSubWord  : INTEGER := DefaultNumBitsPerSubword;
  VARIABLE NewValue           : STD_ULOGIC;
  VARIABLE OldValue           : STD_ULOGIC;
  VARIABLE OutputLength       : INTEGER := 0;
  VARIABLE OutArrayIndex      : INTEGER;
  VARIABLE PropDelay          : TIME;
  VARIABLE RetainDelay        : TIME;
  VARIABLE CurPropDelay       : TIME;
  VARIABLE CurRetainDelay     : TIME;
  VARIABLE InputAge           : TIME;
  VARIABLE CurInputAge        : TIME;
  VARIABLE InputChangeTimeNorm  : VitalTimeArrayT(
     InputChangeTimeArray'LENGTH-1 downto 0):=InputChangeTimeArray;
  VARIABLE DelayArrayNorm  : VitalDelayArrayType01Z(
     PathDelayArray'LENGTH-1 downto 0):= PathDelayArray;
  VARIABLE ScheduleDataArrayNorm : VitalMemoryScheduleDatavectorType
     (ScheduleDataArray'LENGTH-1 downto 0):=ScheduleDataArray;
   
  -- for debug purpose
  VARIABLE debugprop : VitalTimeArrayT(MaxNoInputBits-1 downto 0);
  VARIABLE debugretain : VitalTimeArrayT(MaxNoInputBits-1 downto 0);
BEGIN

  -- for debug purpose
  PrintArcType(ArcType);

  OutputLength := ScheduleDataArray'LENGTH;  
  FOR OutBitPos IN 0 to (OutputLength -1) LOOP
    NEXT WHEN PathConditionArray(OutBitPos) = FALSE;

    NEXT WHEN ((ScheduleDataArrayNorm(OutBitPos).ScheduleValue 
      = ScheduleDataArrayNorm(OutBitPos).OutputData) AND 
        (ScheduleDataArrayNorm(OutBitPos).ScheduleTime <= NOW) AND
        (OutputRetainFlag = FALSE));

    NewValue := ScheduleDataArrayNorm(OutBitPos).OutputData;
    OldValue := ScheduleDataArrayNorm(OutBitPos).LastOutputValue;
    PropDelay :=ScheduleDataArrayNorm(OutBitPos).PropDelay;
    InputAge := ScheduleDataArrayNorm(OutBitPos).InputAge;
    RetainDelay:=ScheduleDataArrayNorm(OutBitPos).OutputRetainDelay;
    NumBitsPerSubWord:=ScheduleDataArrayNorm(OutBitPos).NumBitsPerSubWord;

    CASE ArcType IS
      WHEN ParallelArc =>
        InputArrayLow := OutBitPos;
        InputArrayHigh :=  OutBitPos;
        DelayArrayIndex :=  OutBitPos;
      WHEN CrossArc =>
        InputArrayLow := 0;
        InputArrayHigh := InputChangeTimeArray'LENGTH-1;
        DelayArrayIndex := OutBitPos; 
      WHEN SubwordArc =>
        InputArrayLow := OutBitPos / NumBitsPerSubWord;
        InputArrayHigh := OutBitPos / NumBitsPerSubWord;
        DelayArrayIndex := OutBitPos + 
          (OutputLength * (OutBitPos / NumBitsPerSubWord));  
    END CASE;

    FOR i IN InputArrayLow TO InputArrayHigh LOOP
      (CurPropDelay, CurRetainDelay) := 
        VitalMemoryCalcDelay (
          NewValue, OldValue, DelayArrayNorm(DelayArrayIndex)
        );
      IF (OutputRetainFlag = FALSE) THEN
        CurRetainDelay := TIME'HIGH;
      END IF;
        
      -- for debug purpose
      debugprop(i) := CurPropDelay;
      debugretain(i) := CurRetainDelay;
                       
      IF (ArcType = CrossArc) THEN
        DelayArrayIndex := DelayArrayIndex + OutputLength;
      END IF;

      -- If there is one input change at a time, then choose the
      -- delay from that input. If there is simultaneous input 
      -- change, then choose the minimum of propagation delays 

      IF (InputChangeTimeNorm(i) < 0 ns) THEN
        CurInputAge := TIME'HIGH;
      ELSE
        CurInputAge := NOW - InputChangeTimeNorm(i);
      END IF;            

      IF (CurInputAge  < InputAge) THEN
        PropDelay := CurPropDelay;
        RetainDelay := CurRetainDelay;
        InputAge := CurInputAge;
      ELSIF (CurInputAge = InputAge) THEN
        IF (CurPropDelay < PropDelay) THEN
          PropDelay := CurPropDelay;
        END IF;
        IF (OutputRetainFlag = TRUE) THEN
          IF (CurRetainDelay < RetainDelay) THEN
            RetainDelay := CurRetainDelay;
          END IF;
        END IF;
      END IF;
    END LOOP; 

    -- Store it back to data strucutre
    ScheduleDataArrayNorm(OutBitPos).PropDelay := PropDelay;
    ScheduleDataArrayNorm(OutBitPos).OutputRetainDelay:= RetainDelay;
    ScheduleDataArrayNorm(OutBitPos).InputAge := InputAge;

    -- for debug purpose 
    PrintDelay(outbitPos, InputArrayLow, InputArrayHigh,
       debugprop, debugretain);
  END LOOP;

  ScheduleDataArray := ScheduleDataArrayNorm;

END VitalMemorySelectDelay;

-- ----------------------------------------------------------------------------
-- VitalDelayArrayType01
-- ----------------------------------------------------------------------------
PROCEDURE VitalMemorySelectDelay (
  VARIABLE ScheduleDataArray    : INOUT VitalMemoryScheduleDataVectorType;
  VARIABLE InputChangeTimeArray : IN    VitalTimeArrayT;
  CONSTANT OutputSignalName     : IN    STRING :="";
  CONSTANT PathDelayArray       : IN    VitalDelayArrayType01;
  CONSTANT ArcType              : IN    VitalMemoryArcType;
  CONSTANT PathConditionArray   : IN VitalBoolArrayT
) IS
  VARIABLE CurPathDelay       : VitalMemoryDelayType;
  VARIABLE InputArrayLow      : INTEGER := 0;
  VARIABLE InputArrayHigh     : INTEGER := 0;
  VARIABLE DelayArrayIndex    : INTEGER := 0;
  VARIABLE NumBitsPerSubWord  : INTEGER := DefaultNumBitsPerSubword;
  VARIABLE NewValue           : STD_ULOGIC;
  VARIABLE OldValue           : STD_ULOGIC;
  VARIABLE OutputLength       : INTEGER := 0;
  VARIABLE OutArrayIndex      : INTEGER;
  VARIABLE PropDelay          : TIME;
  VARIABLE CurPropDelay       : TIME;
  VARIABLE InputAge           : TIME;
  VARIABLE CurInputAge        : TIME;
  VARIABLE InputChangeTimeNorm  : VitalTimeArrayT(
     InputChangeTimeArray'LENGTH-1 downto 0):= InputChangeTimeArray;
  VARIABLE DelayArrayNorm  : VitalDelayArrayType01(
     PathDelayArray'LENGTH-1 downto 0):= PathDelayArray;
  VARIABLE ScheduleDataArrayNorm : VitalMemoryScheduleDatavectorType
     (ScheduleDataArray'LENGTH-1 downto 0):=ScheduleDataArray;
   
  -- for debug purpose
  VARIABLE debugprop : VitalTimeArrayT(MaxNoInputBits-1 downto 0);
  VARIABLE debugretain : VitalTimeArrayT(MaxNoInputBits-1 downto 0);
BEGIN

  -- for debug purpose
  PrintArcType(ArcType);

  OutputLength := ScheduleDataArray'LENGTH;
  FOR OutBitPos IN 0 to (OutputLength -1) LOOP
    NEXT WHEN PathConditionArray(OutBitPos) = FALSE;

    NEXT WHEN ((ScheduleDataArrayNorm(OutBitPos).ScheduleValue 
    = ScheduleDataArrayNorm(OutBitPos).OutputData) AND 
    (ScheduleDataArrayNorm(OutBitPos).ScheduleTime <= NOW));

    NewValue := ScheduleDataArrayNorm(OutBitPos).OutputData;
    OldValue := ScheduleDataArrayNorm(OutBitPos).LastOutputValue;
    PropDelay :=ScheduleDataArrayNorm(OutBitPos).PropDelay;
    InputAge := ScheduleDataArrayNorm(OutBitPos).InputAge;
    NumBitsPerSubWord:=ScheduleDataArrayNorm(OutBitPos).NumBitsPerSubWord;

    CASE ArcType IS
      WHEN ParallelArc =>
        InputArrayLow := OutBitPos;
        InputArrayHigh :=  OutBitPos;
        DelayArrayIndex :=  OutBitPos;
      WHEN CrossArc =>
        InputArrayLow := 0;
        InputArrayHigh := InputChangeTimeArray'LENGTH-1;
        DelayArrayIndex := OutBitPos; 
      WHEN SubwordArc =>
        InputArrayLow := OutBitPos / NumBitsPerSubWord;
        InputArrayHigh := OutBitPos / NumBitsPerSubWord;
        DelayArrayIndex := OutBitPos + 
          (OutputLength * (OutBitPos / NumBitsPerSubWord));  
    END CASE;

    FOR i IN InputArrayLow TO InputArrayHigh LOOP
      CurPropDelay:= VitalCalcDelay (NewValue, 
                  OldValue, DelayArrayNorm(DelayArrayIndex));

      -- for debug purpose
      debugprop(i) := CurPropDelay;
      debugretain(i) := TIME'HIGH;
                       
      IF (ArcType = CrossArc) THEN
        DelayArrayIndex := DelayArrayIndex + OutputLength;
      END IF;

      -- If there is one input change at a time, then choose the
      -- delay from that input. If there is simultaneous input 
      -- change, then choose the minimum of propagation delays 

      IF (InputChangeTimeNorm(i) < 0 ns) THEN
        CurInputAge := TIME'HIGH;
      ELSE
        CurInputAge := NOW - InputChangeTimeNorm(i);
      END IF;            
      IF (CurInputAge  < InputAge) THEN
        PropDelay := CurPropDelay;
        InputAge := CurInputAge;
      ELSIF (CurInputAge = InputAge) THEN
        IF (CurPropDelay < PropDelay) THEN
          PropDelay := CurPropDelay;
        END IF;
      END IF;
    END LOOP;  

    -- Store it back to data strucutre
    ScheduleDataArrayNorm(OutBitPos).PropDelay := PropDelay;
    ScheduleDataArrayNorm(OutBitPos).InputAge := InputAge;

    -- for debug purpose 
    PrintDelay(outbitPos, InputArrayLow, InputArrayHigh,
      debugprop, debugretain);
  END LOOP;

  ScheduleDataArray := ScheduleDataArrayNorm;

END VitalMemorySelectDelay;

-- ----------------------------------------------------------------------------
-- VitalDelayArrayType
-- ----------------------------------------------------------------------------
PROCEDURE VitalMemorySelectDelay (
  VARIABLE ScheduleDataArray : INOUT VitalMemoryScheduleDataVectorType;
  VARIABLE InputChangeTimeArray : IN VitalTimeArrayT;
  CONSTANT OutputSignalName     : IN STRING :="";
  CONSTANT PathDelayArray       : IN VitalDelayArrayType;
  CONSTANT ArcType              : IN VitalMemoryArcType;
  CONSTANT PathConditionArray   : IN VitalBoolArrayT
) IS
  VARIABLE InputArrayLow      : INTEGER := 0;
  VARIABLE InputArrayHigh     : INTEGER := 0;
  VARIABLE DelayArrayIndex    : INTEGER := 0;
  VARIABLE NumBitsPerSubWord  : INTEGER := DefaultNumBitsPerSubword;
  VARIABLE NewValue           : STD_ULOGIC;
  VARIABLE OldValue           : STD_ULOGIC;
  VARIABLE OutputLength       : INTEGER := 0;
  VARIABLE OutArrayIndex      : INTEGER;
  VARIABLE PropDelay          : TIME;
  VARIABLE CurPropDelay       : TIME;
  VARIABLE InputAge           : TIME;
  VARIABLE CurInputAge        : TIME;
  VARIABLE InputChangeTimeNorm  : VitalTimeArrayT(
     InputChangeTimeArray'LENGTH-1 downto 0) := InputChangeTimeArray;
  VARIABLE DelayArrayNorm  : VitalDelayArrayType(
     PathDelayArray'LENGTH-1 downto 0) := PathDelayArray;
  VARIABLE ScheduleDataArrayNorm : VitalMemoryScheduleDatavectorType
     (ScheduleDataArray'LENGTH-1 downto 0) := ScheduleDataArray;
   
  -- for debug purpose
  VARIABLE debugprop : VitalTimeArrayT(MaxNoInputBits-1 downto 0);
  VARIABLE debugretain : VitalTimeArrayT(MaxNoInputBits-1 downto 0);
BEGIN
  
  -- for debug purpose
  PrintArcType(ArcType);

  OutputLength := ScheduleDataArray'LENGTH;
  FOR OutBitPos IN 0 to (OutputLength -1) LOOP
    NEXT WHEN PathConditionArray(OutBitPos) = FALSE;

    NEXT WHEN ((ScheduleDataArrayNorm(OutBitPos).ScheduleValue 
      = ScheduleDataArrayNorm(OutBitPos).OutputData) AND 
        (ScheduleDataArrayNorm(OutBitPos).ScheduleTime <= NOW));

    NewValue := ScheduleDataArrayNorm(OutBitPos).OutputData;
    OldValue := ScheduleDataArrayNorm(OutBitPos).LastOutputValue;
    PropDelay :=ScheduleDataArrayNorm(OutBitPos).PropDelay;
    InputAge := ScheduleDataArrayNorm(OutBitPos).InputAge;
    NumBitsPerSubWord:=ScheduleDataArrayNorm(OutBitPos).NumBitsPerSubWord;

    CASE ArcType IS
      WHEN ParallelArc =>
        InputArrayLow := OutBitPos;
        InputArrayHigh :=  OutBitPos;
        DelayArrayIndex :=  OutBitPos;
      WHEN CrossArc =>
        InputArrayLow := 0;
        InputArrayHigh := InputChangeTimeArray'LENGTH-1;
        DelayArrayIndex := OutBitPos; 
      WHEN SubwordArc =>
        InputArrayLow := OutBitPos / NumBitsPerSubWord;
        InputArrayHigh := OutBitPos / NumBitsPerSubWord;
        DelayArrayIndex := OutBitPos + 
          (OutputLength * (OutBitPos / NumBitsPerSubWord));  
    END CASE;

    FOR i IN InputArrayLow TO InputArrayHigh LOOP
      CurPropDelay := VitalCalcDelay (NewValue, 
        OldValue, DelayArrayNorm(DelayArrayIndex)); 

      -- for debug purpose
      debugprop(i) := CurPropDelay;
      debugretain(i) := TIME'HIGH;
                   
      IF (ArcType = CrossArc) THEN
        DelayArrayIndex := DelayArrayIndex + OutputLength;
      END IF;

      -- If there is one input change at a time, then choose the
      -- delay from that input. If there is simultaneous input 
      -- change, then choose the minimum of propagation delays 

      IF (InputChangeTimeNorm(i) < 0 ns) THEN
        CurInputAge := TIME'HIGH;
      ELSE
        CurInputAge := NOW - InputChangeTimeNorm(i);
      END IF;            

      IF (CurInputAge  < InputAge) THEN
        PropDelay := CurPropDelay;
        InputAge := CurInputAge;
      ELSIF (CurInputAge = InputAge) THEN
        IF (CurPropDelay < PropDelay) THEN
          PropDelay := CurPropDelay;
        END IF;
      END IF;
    END LOOP;  

    -- Store it back to data strucutre
    ScheduleDataArrayNorm(OutBitPos).PropDelay := PropDelay;
    ScheduleDataArrayNorm(OutBitPos).InputAge := InputAge;

    -- for debug purpose 
    PrintDelay(outbitPos, InputArrayLow, InputArrayHigh,
      debugprop, debugretain);
  END LOOP;

  ScheduleDataArray := ScheduleDataArrayNorm;

END VitalMemorySelectDelay;

-- ----------------------------------------------------------------------------
-- Procedure:   VitalMemoryInitPathDelay
-- Description: To initialize Schedule Data structure for an
--              output.
-- ----------------------------------------------------------------------------
PROCEDURE VitalMemoryInitPathDelay (
  VARIABLE ScheduleDataArray : INOUT VitalMemoryScheduleDataVectorType;
  VARIABLE OutputDataArray   : IN STD_LOGIC_VECTOR;
  CONSTANT NumBitsPerSubWord : IN INTEGER := DefaultNumBitsPerSubword
) IS
BEGIN
  -- Initialize the ScheduleData Structure.
  FOR i IN OutputDataArray'RANGE LOOP        
    ScheduleDataArray(i).OutputData    := OutputDataArray(i); 
    ScheduleDataArray(i).PropDelay     := TIME'HIGH;
    ScheduleDataArray(i).OutputRetainDelay := TIME'HIGH;
    ScheduleDataArray(i).InputAge      := TIME'HIGH;
    ScheduleDataArray(i).NumBitsPerSubWord := NumBitsPerSubWord;

    -- Update LastOutputValue of Output if the Output has 
    -- already been scheduled.
    IF ((ScheduleDataArray(i).ScheduleValue /= OutputDataArray(i)) AND
        (ScheduleDataArray(i).ScheduleTime <= NOW)) THEN
       ScheduleDataArray(i).LastOutputValue 
                := ScheduleDataArray(i).ScheduleValue;
    END IF;                                   
  END LOOP; 

  -- for debug purpose
  DebugMsg1;
  PrintScheduleDataArray(ScheduleDataArray);

END VitalMemoryInitPathDelay;

-- ----------------------------------------------------------------------------
PROCEDURE VitalMemoryInitPathDelay (
  VARIABLE ScheduleData : INOUT VitalMemoryScheduleDataType;
  VARIABLE OutputData   : IN STD_ULOGIC
) IS
  VARIABLE ScheduledataArray: VitalMemoryScheduleDataVectorType
                                        (0 downto 0);
  VARIABLE OutputDataArray  : STD_LOGIC_VECTOR(0 downto 0);
BEGIN
  ScheduledataArray(0) := ScheduleData;
  OutputDataArray(0)   := OutputData;
  VitalMemoryInitPathDelay (
    ScheduleDataArray => ScheduleDataArray,
    OutputDataArray   => OutputDataArray,
    NumBitsPerSubWord => DefaultNumBitsPerSubword
  );

  -- for debug purpose
  DebugMsg1;
  PrintScheduleDataArray( ScheduleDataArray);

END VitalMemoryInitPathDelay;

-- ----------------------------------------------------------------------------
-- Procedure:   VitalMemoryAddPathDelay
-- Description: Declare a path for one scalar/vector input to 
--              the output for which Schedule Data has been
--              initialized previously.
-- ----------------------------------------------------------------------------

-- ----------------------------------------------------------------------------
-- #1
-- DelayType - VitalMemoryDelayType
-- Input     - Scalar 
-- Output    - Scalar 
-- Delay     - Scalar 
-- Condition - Scalar 
PROCEDURE VitalMemoryAddPathDelay (
  VARIABLE ScheduleData      : INOUT VitalMemoryScheduleDataType;
  SIGNAL   InputSignal       : IN STD_ULOGIC;
  CONSTANT OutputSignalName  : IN STRING :="";
  VARIABLE InputChangeTime   : INOUT TIME;
  CONSTANT PathDelay         : IN VitalDelayType;
  CONSTANT ArcType           : IN VitalMemoryArcType := CrossArc;
  CONSTANT PathCondition     : IN BOOLEAN := TRUE
) IS
  VARIABLE ScheduleDataArray : 
              VitalMemoryScheduleDataVectorType(0 downto 0);
  VARIABLE PathDelayArray : VitalDelayArrayType(0 downto 0);
  VARIABLE InputChangeTimeArray : VitalTimeArrayT(0 downto 0); 
  VARIABLE PathConditionArray : VitalBoolArrayT(0 downto 0); 
BEGIN
  PathConditionArray(0) := PathCondition;
  ScheduleDataArray(0) := ScheduleData;
  PathDelayArray(0) := PathDelay;
  VitalMemoryUpdateInputChangeTime(InputChangeTime, InputSignal);
  InputChangeTimeArray(0) := InputChangeTime;          
   
  VitalMemorySelectDelay(
    ScheduleDataArray, InputChangeTimeArray,
    OutputSignalName, PathDelayArray, 
    ArcType, PathConditionArray);
END VitalMemoryAddPathDelay;

-- ----------------------------------------------------------------------------
-- #2
-- DelayType - VitalMemoryDelayType
-- Input     - Scalar 
-- Output    - Vector 
-- Delay     - Vector 
-- Condition - Scalar 
PROCEDURE VitalMemoryAddPathDelay (
  VARIABLE ScheduleDataArray  : INOUT VitalMemoryScheduleDataVectorType;
  SIGNAL   InputSignal        : IN STD_ULOGIC;
  CONSTANT OutputSignalName   : IN STRING :="";
  VARIABLE InputChangeTime    : INOUT TIME;
  CONSTANT PathDelayArray     : IN VitalDelayArrayType;
  CONSTANT ArcType            : IN VitalMemoryArcType := CrossArc;
  CONSTANT PathCondition      : IN BOOLEAN := TRUE
) IS
  VARIABLE InputChangeTimeArray : VitalTimeArrayT(0 downto 0);
  VARIABLE PathConditionArray :
      VitalBoolArrayT(ScheduleDataArray'LENGTH-1 downto 0);
BEGIN
  FOR i IN PathConditionArray'RANGE LOOP
    PathConditionArray(i) := PathCondition;
  END LOOP;

  VitalMemoryUpdateInputChangeTime(InputChangeTime, InputSignal);
  InputChangeTimeArray(0) := InputChangeTime; 

  VitalMemorySelectDelay(
    ScheduleDataArray, InputChangeTimeArray, 
    OutputSignalName, PathDelayArray, 
    ArcType, PathConditionArray
  );
END VitalMemoryAddPathDelay;

-- ----------------------------------------------------------------------------
-- #3
-- DelayType - VitalMemoryDelayType
-- Input     - Scalar
-- Output    - Vector
-- Delay     - Vector
-- Condition - Vector
PROCEDURE VitalMemoryAddPathDelay (
  VARIABLE ScheduleDataArray  : INOUT VitalMemoryScheduleDataVectorType;
  SIGNAL   InputSignal        : IN STD_ULOGIC;
  CONSTANT OutputSignalName   : IN STRING :="";
  VARIABLE InputChangeTime    : INOUT TIME;
  CONSTANT PathDelayArray     : IN VitalDelayArrayType;
  CONSTANT ArcType            : IN VitalMemoryArcType := CrossArc;
  CONSTANT PathConditionArray : IN VitalBoolArrayT
) IS
  VARIABLE InputChangeTimeArray : VitalTimeArrayT(0 downto 0);
  VARIABLE NumBitsPerSubword : INTEGER;
  VARIABLE PathConditionArrayNorm : 
      VitalBoolArrayT(PathConditionArray'LENGTH-1 downto 0) := PathConditionArray; -- IR Mem400 
  VARIABLE PathConditionArrayExp : 
      VitalBoolArrayT(ScheduleDataArray'LENGTH-1 downto 0); 
BEGIN
  NumBitsPerSubword := 
    ScheduleDataArray(ScheduleDataArray'RIGHT).NumBitsPerSubword;
  FOR i IN PathConditionArrayExp'RANGE LOOP
    PathConditionArrayExp(i) := PathConditionArrayNorm(i/NumBitsPerSubword);
  END LOOP;

  VitalMemoryUpdateInputChangeTime(InputChangeTime, InputSignal);
  InputChangeTimeArray(0) := InputChangeTime;

  VitalMemorySelectDelay(
    ScheduleDataArray, InputChangeTimeArray,
    OutputSignalName, PathDelayArray, 
    ArcType, PathConditionArrayExp);
END VitalMemoryAddPathDelay;

-- ----------------------------------------------------------------------------
-- #4
-- DelayType - VitalMemoryDelayType
-- Input     - Vector
-- Output    - Scalar 
-- Delay     - Vector 
-- Condition - Scalar 
PROCEDURE VitalMemoryAddPathDelay (
  VARIABLE ScheduleData         : INOUT VitalMemoryScheduleDataType;
  SIGNAL   InputSignal          : IN STD_LOGIC_VECTOR;
  CONSTANT OutputSignalName     : IN STRING :="";
  VARIABLE InputChangeTimeArray : INOUT VitalTimeArrayT;
  CONSTANT PathDelayArray       : IN VitalDelayArrayType;
  CONSTANT ArcType              : IN VitalMemoryArcType := CrossArc;
  CONSTANT PathCondition        : IN BOOLEAN := TRUE
) IS
  VARIABLE ScheduleDataArray : VitalMemoryScheduleDataVectorType(0 downto 0);
  VARIABLE PathConditionArray : VitalBoolArrayT(0 downto 0); 
BEGIN
  PathConditionArray(0) := PathCondition;

  ScheduleDataArray(0) := ScheduleData;
  VitalMemoryUpdateInputChangeTime(InputChangeTimeArray, InputSignal);
    
  VitalMemorySelectDelay(
    ScheduleDataArray, InputChangeTimeArray, 
    OutputSignalName, PathDelayArray, 
    ArcType, PathConditionArray);
END VitalMemoryAddPathDelay;

-- ----------------------------------------------------------------------------
-- #5
-- DelayType - VitalMemoryDelayType
-- Input     - Vector
-- Output    - Vector
-- Delay     - Vector 
-- Condition - Scalar 
PROCEDURE VitalMemoryAddPathDelay (
  VARIABLE ScheduleDataArray  : INOUT VitalMemoryScheduleDataVectorType;
  SIGNAL   InputSignal        : IN STD_LOGIC_VECTOR;
  CONSTANT OutputSignalName   : IN STRING :="";
  VARIABLE InputChangeTimeArray  : INOUT VitalTimeArrayT;
  CONSTANT PathDelayArray        : IN VitalDelayArrayType;
  CONSTANT ArcType               : IN VitalMemoryArcType := CrossArc; 
  CONSTANT PathCondition         : IN BOOLEAN := TRUE
) IS
  VARIABLE PathConditionArray : 
      VitalBoolArrayT(ScheduleDataArray'LENGTH-1 downto 0); 
BEGIN
  FOR i IN PathConditionArray'RANGE LOOP
    PathConditionArray(i) := PathCondition;
  END LOOP;

  VitalMemoryUpdateInputChangeTime(InputChangeTimeArray, InputSignal); 

  VitalMemorySelectDelay(
    ScheduleDataArray, InputChangeTimeArray, 
    OutputSignalName, PathDelayArray,
    ArcType, PathConditionArray);
END VitalMemoryAddPathDelay;

-- ----------------------------------------------------------------------------
-- #6
-- DelayType - VitalMemoryDelayType
-- Input     - Vector
-- Output    - Vector
-- Delay     - Vector
-- Condition - Vector
PROCEDURE VitalMemoryAddPathDelay (
  VARIABLE ScheduleDataArray  : INOUT VitalMemoryScheduleDataVectorType;
  SIGNAL   InputSignal        : IN STD_LOGIC_VECTOR;
  CONSTANT OutputSignalName   : IN STRING :="";
  VARIABLE InputChangeTimeArray  : INOUT VitalTimeArrayT;
  CONSTANT PathDelayArray        : IN VitalDelayArrayType;
  CONSTANT ArcType               : IN VitalMemoryArcType := CrossArc;
  CONSTANT PathConditionArray    : IN VitalBoolArrayT
) IS
  VARIABLE NumBitsPerSubword : INTEGER;
  VARIABLE PathConditionArrayNorm : 
      VitalBoolArrayT(PathConditionArray'LENGTH-1 downto 0) := PathConditionArray; -- IR MEM400; 
  VARIABLE PathConditionArrayExp : 
      VitalBoolArrayT(ScheduleDataArray'LENGTH-1 downto 0); 
BEGIN
  NumBitsPerSubword := 
      ScheduleDataArray(ScheduleDataArray'RIGHT).NumBitsPerSubword;
  FOR i IN PathConditionArrayExp'RANGE LOOP
    PathConditionArrayExp(i) := PathConditionArrayNorm(i/NumBitsPerSubword);
  END LOOP;

  VitalMemoryUpdateInputChangeTime(InputChangeTimeArray, InputSignal);

  VitalMemorySelectDelay(
    ScheduleDataArray, InputChangeTimeArray,
    OutputSignalName, PathDelayArray, 
    ArcType, PathConditionArrayExp);
END VitalMemoryAddPathDelay;

-- ----------------------------------------------------------------------------
-- #7
-- DelayType - VitalMemoryDelayType01
-- Input     - Scalar
-- Output    - Scalar
-- Delay     - Scalar 
-- Condition - Scalar 
PROCEDURE VitalMemoryAddPathDelay (
  VARIABLE ScheduleData      : INOUT VitalMemoryScheduleDataType;
  SIGNAL   InputSignal       : IN STD_ULOGIC;
  CONSTANT OutputSignalName  : IN STRING :="";
  VARIABLE InputChangeTime   : INOUT TIME;
  CONSTANT PathDelay         : IN VitalDelayType01;
  CONSTANT ArcType           : IN VitalMemoryArcType := CrossArc; 
  CONSTANT PathCondition     : IN BOOLEAN := TRUE
) IS
  VARIABLE ScheduleDataArray : 
           VitalMemoryScheduleDataVectorType(0 downto 0);
  VARIABLE PathDelayArray : VitalDelayArrayType01(0 downto 0);
  VARIABLE InputChangeTimeArray : VitalTimeArrayT(0 downto 0); 
  VARIABLE PathConditionArray : VitalBoolArrayT(0 downto 0); 
BEGIN
  PathConditionArray(0) := PathCondition;
  ScheduleDataArray(0) := ScheduleData;
  PathDelayArray(0) := PathDelay;
  VitalMemoryUpdateInputChangeTime(InputChangeTime, InputSignal);
  InputChangeTimeArray(0) := InputChangeTime;

  VitalMemorySelectDelay(
    ScheduleDataArray, InputChangeTimeArray, 
    OutputSignalName, PathDelayArray, 
    ArcType, PathConditionArray);
END VitalMemoryAddPathDelay;

-- ----------------------------------------------------------------------------
-- #8
-- DelayType - VitalMemoryDelayType01
-- Input     - Scalar
-- Output    - Vector
-- Delay     - Vector
-- Condition - Scalar 
PROCEDURE VitalMemoryAddPathDelay (
  VARIABLE ScheduleDataArray : INOUT VitalMemoryScheduleDataVectorType;
  SIGNAL   InputSignal       : IN STD_ULOGIC;
  CONSTANT OutputSignalName  : IN STRING :="";
  VARIABLE InputChangeTime   : INOUT TIME;
  CONSTANT PathDelayArray    : IN VitalDelayArrayType01;
  CONSTANT ArcType           : IN VitalMemoryArcType := CrossArc;
  CONSTANT PathCondition     : IN BOOLEAN := TRUE
) IS
  VARIABLE InputChangeTimeArray : VitalTimeArrayT(0 downto 0);
  VARIABLE PathConditionArray : 
      VitalBoolArrayT(ScheduleDataArray'LENGTH-1 downto 0); 
BEGIN
  FOR i IN PathConditionArray'RANGE LOOP
    PathConditionArray(i) := PathCondition;
  END LOOP;

  VitalMemoryUpdateInputChangeTime(InputChangeTime, InputSignal);
  InputChangeTimeArray(0) := InputChangeTime;

  VitalMemorySelectDelay(
    ScheduleDataArray, InputChangeTimeArray, 
    OutputSignalName, PathDelayArray, 
    ArcType, PathConditionArray);
END VitalMemoryAddPathDelay;

-- ----------------------------------------------------------------------------
-- #9
-- DelayType - VitalMemoryDelayType01
-- Input     - Scalar
-- Output    - Vector
-- Delay     - Vector
-- Condition - Vector
PROCEDURE VitalMemoryAddPathDelay (
  VARIABLE ScheduleDataArray : INOUT VitalMemoryScheduleDataVectorType;
  SIGNAL   InputSignal       : IN STD_ULOGIC;
  CONSTANT OutputSignalName  : IN STRING :="";
  VARIABLE InputChangeTime   : INOUT TIME;
  CONSTANT PathDelayArray    : IN VitalDelayArrayType01;
  CONSTANT ArcType           : IN VitalMemoryArcType := CrossArc;
  CONSTANT PathConditionArray: IN VitalBoolArrayT
) IS
  VARIABLE InputChangeTimeArray : VitalTimeArrayT(0 downto 0);
  VARIABLE NumBitsPerSubword : INTEGER;
  VARIABLE PathConditionArrayNorm : 
      VitalBoolArrayT(PathConditionArray'LENGTH-1 downto 0) := PathConditionArray; -- IR MEM400; 
  VARIABLE PathConditionArrayExp : 
      VitalBoolArrayT(ScheduleDataArray'LENGTH-1 downto 0); 
BEGIN
  NumBitsPerSubword := 
      ScheduleDataArray(ScheduleDataArray'RIGHT).NumBitsPerSubword;
  FOR i IN PathConditionArrayExp'RANGE LOOP
    PathConditionArrayExp(i) := PathConditionArrayNorm(i/NumBitsPerSubword);
  END LOOP;

  VitalMemoryUpdateInputChangeTime(InputChangeTime, InputSignal);
  InputChangeTimeArray(0) := InputChangeTime;

  VitalMemorySelectDelay(
    ScheduleDataArray, InputChangeTimeArray,
    OutputSignalName, PathDelayArray, 
    ArcType, PathConditionArrayExp);
END VitalMemoryAddPathDelay;

-- ----------------------------------------------------------------------------
-- #10
-- DelayType - VitalMemoryDelayType01
-- Input     - Vector
-- Output    - Scalar
-- Delay     - Vector
-- Condition - Scalar 
PROCEDURE VitalMemoryAddPathDelay (
  VARIABLE ScheduleData        : INOUT VitalMemoryScheduleDataType;
  SIGNAL   InputSignal         : IN STD_LOGIC_VECTOR;
  CONSTANT OutputSignalName    : IN STRING :="";
  VARIABLE InputChangeTimeArray: INOUT VitalTimeArrayT;
  CONSTANT PathDelayArray      : IN VitalDelayArrayType01;
  CONSTANT ArcType             : IN VitalMemoryArcType := CrossArc; 
  CONSTANT PathCondition       : IN BOOLEAN := TRUE
)IS 
  VARIABLE ScheduleDataArray : 
      VitalMemoryScheduleDataVectorType(0 downto 0);
  VARIABLE PathConditionArray : VitalBoolArrayT(0 downto 0); 
BEGIN
  PathConditionArray(0) := PathCondition;
  ScheduleDataArray(0) := ScheduleData;
  VitalMemoryUpdateInputChangeTime(InputChangeTimeArray, InputSignal); 
                                         
  VitalMemorySelectDelay(
    ScheduleDataArray, InputChangeTimeArray, 
    OutputSignalName, PathDelayArray, 
    ArcType, PathConditionArray); 
END VitalMemoryAddPathDelay;

-- ----------------------------------------------------------------------------
-- #11
-- DelayType - VitalMemoryDelayType01
-- Input     - Vector
-- Output    - Vector
-- Delay     - Vector
-- Condition - Scalar 
PROCEDURE VitalMemoryAddPathDelay (
  VARIABLE ScheduleDataArray  : INOUT VitalMemoryScheduleDataVectorType;
  SIGNAL   InputSignal        : IN STD_LOGIC_VECTOR;
  CONSTANT OutputSignalName   : IN STRING :="";
  VARIABLE InputChangeTimeArray : INOUT VitalTimeArrayT;
  CONSTANT PathDelayArray       : IN VitalDelayArrayType01;
  CONSTANT ArcType              : IN VitalMemoryArcType  := CrossArc;
  CONSTANT PathCondition        : IN BOOLEAN := TRUE
) IS
  VARIABLE PathConditionArray : 
      VitalBoolArrayT(ScheduleDataArray'LENGTH-1 downto 0); 
BEGIN
  FOR i IN PathConditionArray'RANGE LOOP
    PathConditionArray(i) := PathCondition;
  END LOOP;

  VitalMemoryUpdateInputChangeTime(InputChangeTimeArray, InputSignal); 

  VitalMemorySelectDelay(
    ScheduleDataArray, InputChangeTimeArray, 
    OutputSignalName, PathDelayArray, 
    ArcType, PathConditionArray);
END VitalMemoryAddPathDelay;

-- ----------------------------------------------------------------------------
-- #12
-- DelayType - VitalMemoryDelayType01
-- Input     - Vector
-- Output    - Vector
-- Delay     - Vector
-- Condition - Vector
PROCEDURE VitalMemoryAddPathDelay (
  VARIABLE ScheduleDataArray  : INOUT VitalMemoryScheduleDataVectorType;
  SIGNAL   InputSignal       : IN STD_LOGIC_VECTOR;
  CONSTANT OutputSignalName  : IN STRING :="";
  VARIABLE InputChangeTimeArray : INOUT VitalTimeArrayT;
  CONSTANT PathDelayArray       : IN VitalDelayArrayType01;
  CONSTANT ArcType              : IN VitalMemoryArcType  := CrossArc;
  CONSTANT PathConditionArray   : IN VitalBoolArrayT
) IS
  VARIABLE NumBitsPerSubword : INTEGER;
  VARIABLE PathConditionArrayNorm : 
      VitalBoolArrayT(PathConditionArray'LENGTH-1 downto 0) := PathConditionArray; -- IR MEM400; 
  VARIABLE PathConditionArrayExp : 
      VitalBoolArrayT(ScheduleDataArray'LENGTH-1 downto 0); 
BEGIN
  NumBitsPerSubword := 
      ScheduleDataArray(ScheduleDataArray'RIGHT).NumBitsPerSubword;
  FOR i IN PathConditionArrayExp'RANGE LOOP
    PathConditionArrayExp(i) := PathConditionArrayNorm(i/NumBitsPerSubword);
  END LOOP;

  VitalMemoryUpdateInputChangeTime(InputChangeTimeArray, InputSignal);

  VitalMemorySelectDelay(
    ScheduleDataArray, InputChangeTimeArray,
    OutputSignalName, PathDelayArray, 
    ArcType, PathConditionArrayExp);
END VitalMemoryAddPathDelay;

-- ----------------------------------------------------------------------------
-- #13
-- DelayType - VitalMemoryDelayType01Z
-- Input     - Scalar
-- Output    - Scalar
-- Delay     - Scalar
-- Condition - Scalar 
PROCEDURE VitalMemoryAddPathDelay (
  VARIABLE ScheduleData     : INOUT VitalMemoryScheduleDataType;
  SIGNAL   InputSignal      : IN STD_ULOGIC;
  CONSTANT OutputSignalName : IN STRING :="";
  VARIABLE InputChangeTime  : INOUT TIME;
  CONSTANT PathDelay        : IN VitalDelayType01Z;
  CONSTANT ArcType          : IN VitalMemoryArcType := CrossArc; 
  CONSTANT PathCondition    : IN BOOLEAN := TRUE;
  CONSTANT OutputRetainFlag : IN BOOLEAN := FALSE
) IS
  VARIABLE ScheduleDataArray : 
           VitalMemoryScheduleDataVectorType(0 downto 0);
  VARIABLE PathDelayArray : VitalDelayArrayType01Z(0 downto 0);
  VARIABLE InputChangeTimeArray : VitalTimeArrayT(0 downto 0);
  VARIABLE PathConditionArray : VitalBoolArrayT(0 downto 0); 
BEGIN
  PathConditionArray(0) := PathCondition;
  ScheduleDataArray(0) := ScheduleData;
  PathDelayArray(0) := PathDelay;
  VitalMemoryUpdateInputChangeTime(InputChangeTime, InputSignal);
  InputChangeTimeArray(0) := InputChangeTime;

  VitalMemorySelectDelay(
    ScheduleDataArray, InputChangeTimeArray,
    OutputSignalName, PathDelayArray, 
    ArcType, PathConditionArray, OutputRetainFlag);
END VitalMemoryAddPathDelay;

-- ----------------------------------------------------------------------------
-- #14
-- DelayType - VitalMemoryDelayType01Z
-- Input     - Scalar
-- Output    - Vector
-- Delay     - Vector
-- Condition - Scalar 
PROCEDURE VitalMemoryAddPathDelay (
  VARIABLE ScheduleDataArray : INOUT VitalMemoryScheduleDataVectorType;
  SIGNAL   InputSignal       : IN STD_ULOGIC;
  CONSTANT OutputSignalName  : IN STRING :="";
  VARIABLE InputChangeTime   : INOUT TIME;
  CONSTANT PathDelayArray    : IN VitalDelayArrayType01Z;
  CONSTANT ArcType           : IN VitalMemoryArcType  := CrossArc;
  CONSTANT PathCondition     : IN BOOLEAN := TRUE;
  CONSTANT OutputRetainFlag  : IN BOOLEAN := FALSE
) IS
  VARIABLE InputChangeTimeArray : VitalTimeArrayT(0 downto 0);
  VARIABLE PathConditionArray : 
      VitalBoolArrayT(ScheduleDataArray'LENGTH-1 downto 0); 
BEGIN
  FOR i IN PathConditionArray'RANGE LOOP
    PathConditionArray(i) := PathCondition;
  END LOOP;

  VitalMemoryUpdateInputChangeTime(InputChangeTime, InputSignal);
  InputChangeTimeArray(0) := InputChangeTime; 

  VitalMemorySelectDelay(
    ScheduleDataArray, InputChangeTimeArray, 
    OutputSignalName, PathDelayArray, 
    ArcType, PathConditionArray, OutputRetainFlag);

END VitalMemoryAddPathDelay;

-- ----------------------------------------------------------------------------
-- #15
-- DelayType - VitalMemoryDelayType01Z
-- Input     - Scalar
-- Output    - Vector
-- Delay     - Vector
-- Condition - Vector
PROCEDURE VitalMemoryAddPathDelay (
  VARIABLE ScheduleDataArray : INOUT VitalMemoryScheduleDataVectorType;
  SIGNAL   InputSignal       : IN STD_ULOGIC;
  CONSTANT OutputSignalName  : IN STRING :="";
  VARIABLE InputChangeTime   : INOUT TIME;
  CONSTANT PathDelayArray    : IN VitalDelayArrayType01Z;
  CONSTANT ArcType           : IN VitalMemoryArcType  := CrossArc;
  CONSTANT PathConditionArray: IN VitalBoolArrayT;
  CONSTANT OutputRetainFlag  : IN BOOLEAN := FALSE
) IS
  VARIABLE InputChangeTimeArray : VitalTimeArrayT(0 downto 0);
  VARIABLE NumBitsPerSubword : INTEGER;
  VARIABLE PathConditionArrayNorm : VitalBoolArrayT(PathConditionArray'LENGTH-1 downto 0); 
  VARIABLE PathConditionArrayExp : VitalBoolArrayT(ScheduleDataArray'LENGTH-1 downto 0); 
BEGIN
  NumBitsPerSubword := ScheduleDataArray(ScheduleDataArray'RIGHT).NumBitsPerSubword;
  FOR i IN PathConditionArrayExp'RANGE LOOP
    PathConditionArrayExp(i) := PathConditionArrayNorm(i/NumBitsPerSubword);
  END LOOP;

  VitalMemoryUpdateInputChangeTime(InputChangeTime, InputSignal);
  InputChangeTimeArray(0) := InputChangeTime;

  VitalMemorySelectDelay(
    ScheduleDataArray, InputChangeTimeArray,
    OutputSignalName, PathDelayArray, 
    ArcType, PathConditionArrayExp, OutputRetainFlag);
END VitalMemoryAddPathDelay;

-- ----------------------------------------------------------------------------
-- #16
-- DelayType - VitalMemoryDelayType01Z
-- Input     - Vector
-- Output    - Scalar
-- Delay     - Vector
-- Condition - Scalar 
PROCEDURE VitalMemoryAddPathDelay (
  VARIABLE ScheduleData         : INOUT VitalMemoryScheduleDataType;
  SIGNAL   InputSignal          : IN STD_LOGIC_VECTOR;
  CONSTANT OutputSignalName     : IN STRING :="";
  VARIABLE InputChangeTimeArray : INOUT VitalTimeArrayT;
  CONSTANT PathDelayArray       : IN VitalDelayArrayType01Z;
  CONSTANT ArcType              : IN VitalMemoryArcType  := CrossArc;
  CONSTANT PathCondition        : IN BOOLEAN := TRUE;
  CONSTANT OutputRetainFlag     : IN BOOLEAN := FALSE;
  CONSTANT OutputRetainBehavior : IN OutputRetainBehaviorType := BitCorrupt
) IS
  VARIABLE ScheduleDataArray : 
              VitalMemoryScheduleDataVectorType(0 downto 0);
  VARIABLE NumBitsPerSubword : INTEGER;
  VARIABLE PathConditionArray : VitalBoolArrayT(0 downto 0); 
BEGIN
  PathConditionArray(0) := PathCondition;
  ScheduleDataArray(0) := ScheduleData;
  NumBitsPerSubword := ScheduleDataArray(0).NumBitsPerSubword;
  IF (OutputRetainBehavior = WordCorrupt AND 
      ArcType = ParallelArc AND
      OutputRetainFlag = TRUE) THEN
    VitalMemoryUpdateInputChangeTime(
      InputChangeTimeArray, 
      InputSignal,
      NumBitsPerSubword
    ); 
  ELSE
    VitalMemoryUpdateInputChangeTime(InputChangeTimeArray, InputSignal); 
  END IF;

  VitalMemorySelectDelay(
    ScheduleDataArray, InputChangeTimeArray, 
    OutputSignalName, PathDelayArray, 
    ArcType, PathConditionArray, OutputRetainFlag);
END VitalMemoryAddPathDelay;

-- ----------------------------------------------------------------------------
-- #17
-- DelayType - VitalMemoryDelayType01Z
-- Input     - Vector
-- Output    - Vector
-- Delay     - Vector  
-- Condition - Scalar 
PROCEDURE VitalMemoryAddPathDelay (
  VARIABLE ScheduleDataArray : INOUT VitalMemoryScheduleDataVectorType;
  SIGNAL   InputSignal       : IN STD_LOGIC_VECTOR;
  CONSTANT OutputSignalName  : IN STRING :="";
  VARIABLE InputChangeTimeArray : INOUT VitalTimeArrayT;
  CONSTANT PathDelayArray       : IN VitalDelayArrayType01Z;
  CONSTANT ArcType              : IN VitalMemoryArcType := CrossArc;
  CONSTANT PathCondition        : IN BOOLEAN := TRUE;
  CONSTANT OutputRetainFlag     : IN BOOLEAN := FALSE;
  CONSTANT OutputRetainBehavior : IN OutputRetainBehaviorType := BitCorrupt
) IS
  VARIABLE NumBitsPerSubword  : INTEGER;
  VARIABLE PathConditionArray : 
      VitalBoolArrayT(ScheduleDataArray'LENGTH-1 downto 0); 
BEGIN
  FOR i IN PathConditionArray'RANGE LOOP
    PathConditionArray(i) := PathCondition;
  END LOOP;

  NumBitsPerSubword := 
      ScheduleDataArray(ScheduleDataArray'LEFT).NumBitsPerSubword;
  IF (OutputRetainBehavior = WordCorrupt AND 
      ArcType = ParallelArc AND
      OutputRetainFlag = TRUE) THEN
    VitalMemoryUpdateInputChangeTime(
      InputChangeTimeArray, 
      InputSignal,
      NumBitsPerSubword
    ); 
  ELSE
    VitalMemoryUpdateInputChangeTime(InputChangeTimeArray, InputSignal); 
  END IF;

  VitalMemorySelectDelay(
    ScheduleDataArray, InputChangeTimeArray, 
    OutputSignalName, PathDelayArray, 
    ArcType, PathConditionArray, OutputRetainFlag);
END VitalMemoryAddPathDelay;

-- ----------------------------------------------------------------------------
-- #18
-- DelayType - VitalMemoryDelayType01Z
-- Input     - Vector
-- Output    - Vector
-- Delay     - Vector 
-- Condition - Vector
PROCEDURE VitalMemoryAddPathDelay (
  VARIABLE ScheduleDataArray : INOUT VitalMemoryScheduleDataVectorType;
  SIGNAL   InputSignal       : IN STD_LOGIC_VECTOR;
  CONSTANT OutputSignalName  : IN STRING :="";
  VARIABLE InputChangeTimeArray : INOUT VitalTimeArrayT;
  CONSTANT PathDelayArray       : IN VitalDelayArrayType01Z;
  CONSTANT ArcType              : IN VitalMemoryArcType := CrossArc;
  CONSTANT PathConditionArray   : IN VitalBoolArrayT;
  CONSTANT OutputRetainFlag     : IN BOOLEAN := FALSE;
  CONSTANT OutputRetainBehavior : IN OutputRetainBehaviorType := BitCorrupt
) IS
VARIABLE NumBitsPerSubword : INTEGER;
VARIABLE PathConditionArrayNorm : 
    VitalBoolArrayT(PathConditionArray'LENGTH-1 downto 0); 
VARIABLE PathConditionArrayExp : 
    VitalBoolArrayT(ScheduleDataArray'LENGTH-1 downto 0); 
BEGIN
  NumBitsPerSubword := ScheduleDataArray(ScheduleDataArray'RIGHT).NumBitsPerSubword;
  FOR i IN PathConditionArrayExp'RANGE LOOP
    PathConditionArrayExp(i) := PathConditionArrayNorm(i/NumBitsPerSubword);
  END LOOP;

  IF (OutputRetainBehavior = WordCorrupt AND 
      ArcType = ParallelArc AND
      OutputRetainFlag = TRUE) THEN
    VitalMemoryUpdateInputChangeTime(
        InputChangeTimeArray, InputSignal,
        NumBitsPerSubword);
  ELSE
    VitalMemoryUpdateInputChangeTime(InputChangeTimeArray, InputSignal);
  END IF;

  VitalMemorySelectDelay(
    ScheduleDataArray, InputChangeTimeArray,
    OutputSignalName, PathDelayArray, 
    ArcType, PathConditionArrayExp, OutputRetainFlag);
END VitalMemoryAddPathDelay;

-- ----------------------------------------------------------------------------
-- #19
-- DelayType - VitalMemoryDelayType01XZ
-- Input     - Scalar
-- Output    - Scalar
-- Delay     - Scalar
-- Condition - Scalar 
PROCEDURE VitalMemoryAddPathDelay (
  VARIABLE ScheduleData      : INOUT  VitalMemoryScheduleDataType;
  SIGNAL   InputSignal       : IN STD_ULOGIC;
  CONSTANT OutputSignalName  : IN STRING :="";
  VARIABLE InputChangeTime   : INOUT TIME;
  CONSTANT PathDelay         : IN VitalDelayType01ZX;
  CONSTANT ArcType           : IN VitalMemoryArcType := CrossArc; 
  CONSTANT PathCondition     : IN BOOLEAN := TRUE;
  CONSTANT OutputRetainFlag  : IN BOOLEAN := FALSE
) IS
  VARIABLE ScheduleDataArray : 
         VitalMemoryScheduleDataVectorType(0 downto 0);
  VARIABLE PathDelayArray : VitalDelayArrayType01ZX(0 downto 0);
  VARIABLE InputChangeTimeArray : VitalTimeArrayT(0 downto 0);
  VARIABLE PathConditionArray : VitalBoolArrayT(0 downto 0); 
BEGIN
  PathConditionArray(0) := PathCondition;
  ScheduleDataArray(0) := ScheduleData;
  PathDelayArray(0) := PathDelay;
  VitalMemoryUpdateInputChangeTime(InputChangeTime, InputSignal);
  InputChangeTimeArray(0) := InputChangeTime;

  VitalMemorySelectDelay(
    ScheduleDataArray, InputChangeTimeArray, 
    OutputSignalName, PathDelayArray, 
    ArcType, PathConditionArray, OutputRetainFlag); 
END VitalMemoryAddPathDelay;

-- ----------------------------------------------------------------------------
-- #20
-- DelayType - VitalMemoryDelayType01XZ
-- Input     - Scalar
-- Output    - Vector
-- Delay     - Vector
-- Condition - Scalar 
PROCEDURE VitalMemoryAddPathDelay (
  VARIABLE ScheduleDataArray :INOUT VitalMemoryScheduleDataVectorType;
  SIGNAL   InputSignal       : IN STD_ULOGIC;
  CONSTANT OutputSignalName  : IN STRING :="";
  VARIABLE InputChangeTime   : INOUT TIME;
  CONSTANT PathDelayArray    : IN VitalDelayArrayType01ZX;
  CONSTANT ArcType           : IN VitalMemoryArcType := CrossArc; 
  CONSTANT PathCondition     : IN BOOLEAN := TRUE;
  CONSTANT OutputRetainFlag  : IN BOOLEAN := FALSE
) IS
  VARIABLE InputChangeTimeArray : VitalTimeArrayT(0 downto 0);
  VARIABLE PathConditionArray : 
      VitalBoolArrayT(ScheduleDataArray'LENGTH-1 downto 0); 
BEGIN
  FOR i IN PathConditionArray'RANGE LOOP
    PathConditionArray(i) := PathCondition;
  END LOOP;

  VitalMemoryUpdateInputChangeTime(InputChangeTime, InputSignal);
  InputChangeTimeArray(0) := InputChangeTime; 

  VitalMemorySelectDelay(
    ScheduleDataArray, InputChangeTimeArray,  
    OutputSignalName, PathDelayArray, 
    ArcType, PathConditionArray, OutputRetainFlag); 
END VitalMemoryAddPathDelay;

-- ----------------------------------------------------------------------------
-- #21
-- DelayType - VitalMemoryDelayType01XZ
-- Input     - Scalar
-- Output    - Vector
-- Delay     - Vector
-- Condition - Vector
PROCEDURE VitalMemoryAddPathDelay (
  VARIABLE ScheduleDataArray :INOUT VitalMemoryScheduleDataVectorType;
  SIGNAL   InputSignal       : IN STD_ULOGIC;
  CONSTANT OutputSignalName  : IN STRING :="";
  VARIABLE InputChangeTime   : INOUT TIME;
  CONSTANT PathDelayArray    : IN VitalDelayArrayType01ZX;
  CONSTANT ArcType           : IN VitalMemoryArcType := CrossArc;
  CONSTANT PathConditionArray: IN VitalBoolArrayT;
  CONSTANT OutputRetainFlag  : IN BOOLEAN := FALSE
) IS
  VARIABLE InputChangeTimeArray : VitalTimeArrayT(0 downto 0);
  VARIABLE NumBitsPerSubword : INTEGER;
  VARIABLE PathConditionArrayNorm : 
      VitalBoolArrayT(PathConditionArray'LENGTH-1 downto 0) := PathConditionArray; -- IR MEM400; 
  VARIABLE PathConditionArrayExp : 
      VitalBoolArrayT(ScheduleDataArray'LENGTH-1 downto 0); 
BEGIN
  NumBitsPerSubword := 
      ScheduleDataArray(ScheduleDataArray'RIGHT).NumBitsPerSubword;
  FOR i IN PathConditionArrayExp'RANGE LOOP
    PathConditionArrayExp(i) := PathConditionArrayNorm(i/NumBitsPerSubword);
  END LOOP;

  VitalMemoryUpdateInputChangeTime(InputChangeTime, InputSignal);
  InputChangeTimeArray(0) := InputChangeTime;

  VitalMemorySelectDelay(
    ScheduleDataArray, InputChangeTimeArray,
    OutputSignalName, PathDelayArray, 
    ArcType, PathConditionArrayExp, OutputRetainFlag);
END VitalMemoryAddPathDelay;

-- ----------------------------------------------------------------------------
-- #22
-- DelayType - VitalMemoryDelayType01XZ
-- Input     - Vector
-- Output    - Scalar
-- Delay     - Vector
-- Condition - Scalar 
PROCEDURE VitalMemoryAddPathDelay (
  VARIABLE ScheduleData         : INOUT  VitalMemoryScheduleDataType;
  SIGNAL   InputSignal          : IN STD_LOGIC_VECTOR;
  CONSTANT OutputSignalName     : IN STRING :="";
  VARIABLE InputChangeTimeArray : INOUT VitalTimeArrayT;
  CONSTANT PathDelayArray       : IN VitalDelayArrayType01ZX;
  CONSTANT ArcType              : IN VitalMemoryArcType := CrossArc; 
  CONSTANT PathCondition        : IN BOOLEAN := TRUE;
  CONSTANT OutputRetainFlag     : IN BOOLEAN := FALSE;
  CONSTANT OutputRetainBehavior : IN OutputRetainBehaviorType := BitCorrupt
) IS 
  VARIABLE ScheduleDataArray : 
    VitalMemoryScheduleDataVectorType(0 downto 0);
  VARIABLE NumBitsPerSubword : INTEGER;
  VARIABLE PathConditionArray : VitalBoolArrayT(0 downto 0); 
BEGIN
  PathConditionArray(0) := PathCondition;
  ScheduleDataArray(0) := ScheduleData;
  NumBitsPerSubword := 
      ScheduleDataArray(ScheduleDataArray'LEFT).NumBitsPerSubword;
  IF (OutputRetainBehavior = WordCorrupt AND 
      ArcType = ParallelArc AND
      OutputRetainFlag = TRUE) THEN
    VitalMemoryUpdateInputChangeTime(
      InputChangeTimeArray, InputSignal,
      NumBitsPerSubword); 
  ELSE
    VitalMemoryUpdateInputChangeTime(InputChangeTimeArray, InputSignal); 
  END IF;

  VitalMemorySelectDelay(
    ScheduleDataArray, InputChangeTimeArray, 
    OutputSignalName, PathDelayArray, 
    ArcType, PathConditionArray, OutputRetainFlag);
END VitalMemoryAddPathDelay;

-- ----------------------------------------------------------------------------
-- #23
-- DelayType - VitalMemoryDelayType01XZ
-- Input     - Vector
-- Output    - Vector
-- Delay     - Vector
-- Condition - Scalar 
PROCEDURE VitalMemoryAddPathDelay (
  VARIABLE ScheduleDataArray : INOUT VitalMemoryScheduleDataVectorType;
  SIGNAL   InputSignal       : IN STD_LOGIC_VECTOR;
  CONSTANT OutputSignalName  : IN STRING :="";
  VARIABLE InputChangeTimeArray : INOUT VitalTimeArrayT;
  CONSTANT PathDelayArray       : IN VitalDelayArrayType01ZX;
  CONSTANT ArcType              : IN VitalMemoryArcType  := CrossArc;
  CONSTANT PathCondition        : IN BOOLEAN := TRUE;
  CONSTANT OutputRetainFlag     : IN BOOLEAN := FALSE;
  CONSTANT OutputRetainBehavior : IN OutputRetainBehaviorType := BitCorrupt
) IS
  VARIABLE NumBitsPerSubword  : INTEGER;
  VARIABLE PathConditionArray : 
      VitalBoolArrayT(ScheduleDataArray'LENGTH-1 downto 0); 
BEGIN
  FOR i IN PathConditionArray'RANGE LOOP
    PathConditionArray(i) := PathCondition;
  END LOOP;

  NumBitsPerSubword := 
      ScheduleDataArray(ScheduleDataArray'LEFT).NumBitsPerSubword;
  IF (OutputRetainBehavior = WordCorrupt AND 
      ArcType = ParallelArc AND
      OutputRetainFlag = TRUE) THEN
    VitalMemoryUpdateInputChangeTime(
      InputChangeTimeArray, InputSignal,
      NumBitsPerSubword); 
  ELSE
    VitalMemoryUpdateInputChangeTime(InputChangeTimeArray, InputSignal); 
  END IF;

  VitalMemorySelectDelay(
    ScheduleDataArray, InputChangeTimeArray,
    OutputSignalName, PathDelayArray, 
    ArcType, PathConditionArray, OutputRetainFlag); 
END VitalMemoryAddPathDelay;

-- ----------------------------------------------------------------------------
-- #24
-- DelayType - VitalMemoryDelayType01XZ
-- Input     - Vector
-- Output    - Vector
-- Delay     - Vector
-- Condition - Vector
PROCEDURE VitalMemoryAddPathDelay (
  VARIABLE ScheduleDataArray : INOUT VitalMemoryScheduleDataVectorType;
  SIGNAL   InputSignal       : IN STD_LOGIC_VECTOR;
  CONSTANT OutputSignalName  : IN STRING :="";
  VARIABLE InputChangeTimeArray : INOUT VitalTimeArrayT;
  CONSTANT PathDelayArray       : IN VitalDelayArrayType01ZX;
  CONSTANT ArcType              : IN VitalMemoryArcType  := CrossArc;
  CONSTANT PathConditionArray   : IN VitalBoolArrayT;
  CONSTANT OutputRetainFlag     : IN BOOLEAN := FALSE;
  CONSTANT OutputRetainBehavior : IN OutputRetainBehaviorType := BitCorrupt
) IS
  VARIABLE NumBitsPerSubword : INTEGER;
  VARIABLE PathConditionArrayNorm : 
      VitalBoolArrayT(PathConditionArray'LENGTH-1 downto 0) := PathConditionArray; -- IR MEM400; 
  VARIABLE PathConditionArrayExp : 
      VitalBoolArrayT(ScheduleDataArray'LENGTH-1 downto 0); 
BEGIN
  NumBitsPerSubword := 
      ScheduleDataArray(ScheduleDataArray'RIGHT).NumBitsPerSubword;
  FOR i IN PathConditionArrayExp'RANGE LOOP
    PathConditionArrayExp(i) := PathConditionArrayNorm(i/NumBitsPerSubword);
  END LOOP;

  IF (OutputRetainBehavior = WordCorrupt AND 
      ArcType = ParallelArc AND
      OutputRetainFlag = TRUE) THEN
    VitalMemoryUpdateInputChangeTime(
      InputChangeTimeArray, InputSignal,
      NumBitsPerSubword);
  ELSE
    VitalMemoryUpdateInputChangeTime(InputChangeTimeArray, InputSignal);
  END IF;

  VitalMemorySelectDelay(
    ScheduleDataArray, InputChangeTimeArray,
    OutputSignalName, PathDelayArray, 
    ArcType, PathConditionArrayExp, OutputRetainFlag);
END VitalMemoryAddPathDelay;

-- ----------------------------------------------------------------------------
-- Procedure:   VitalMemorySchedulePathDelay
-- Description: Schedule Output after Propagation Delay selected
--              by checking all the paths added thru' 
--              VitalMemoryAddPathDelay.
-- ----------------------------------------------------------------------------
PROCEDURE VitalMemorySchedulePathDelay (
  SIGNAL   OutSignal        : OUT STD_LOGIC_VECTOR;
  CONSTANT OutputSignalName : IN STRING :="";
  CONSTANT PortFlag         : IN VitalPortFlagType := VitalDefaultPortFlag;
  CONSTANT OutputMap        : IN VitalOutputMapType:= VitalDefaultOutputMap;
  VARIABLE ScheduleDataArray : INOUT VitalMemoryScheduleDataVectorType
) IS
  VARIABLE Age               : TIME;
  VARIABLE PropDelay         : TIME;
  VARIABLE RetainDelay       : TIME;
  VARIABLE Data              : STD_ULOGIC;
BEGIN
  IF (PortFlag.OutputDisable /= TRUE) THEN
    FOR i IN ScheduleDataArray'RANGE LOOP
      PropDelay := ScheduleDataArray(i).PropDelay;
      RetainDelay := ScheduleDataArray(i).OutputRetainDelay;   

      NEXT WHEN PropDelay = TIME'HIGH;

      Age := ScheduleDataArray(i).InputAge; 
      Data := ScheduleDataArray(i).OutputData;

      IF (Age < RetainDelay and RetainDelay < PropDelay) THEN
        OutSignal(i) <= TRANSPORT 'X' AFTER (RetainDelay - Age);
      END IF;

      IF (Age <= PropDelay) THEN
        OutSignal(i)<= TRANSPORT OutputMap(Data)AFTER (PropDelay-Age);
        ScheduleDataArray(i).ScheduleValue := Data;
        ScheduleDataArray(i).ScheduleTime := NOW + PropDelay - Age;
      END IF;
    END LOOP;
  END IF;

  -- for debug purpose
  PrintScheduleDataArray(ScheduleDataArray);

  -- for debug purpose
  ScheduleDebugMsg;
END VitalMemorySchedulePathDelay;

-- ----------------------------------------------------------------------------
-- Procedure:   VitalMemorySchedulePathDelay
-- Description: Schedule Output after Propagation Delay selected
--              by checking all the paths added thru' 
--              VitalMemoryAddPathDelay.
-- ----------------------------------------------------------------------------
PROCEDURE VitalMemorySchedulePathDelay (
  SIGNAL   OutSignal        : OUT STD_LOGIC_VECTOR;
  CONSTANT OutputSignalName : IN STRING :="";
  CONSTANT PortFlag         : IN VitalPortFlagVectorType;
  CONSTANT OutputMap        : IN VitalOutputMapType:= VitalDefaultOutputMap;
  VARIABLE ScheduleDataArray : INOUT VitalMemoryScheduleDataVectorType
) IS
  VARIABLE Age               : TIME;
  VARIABLE PropDelay         : TIME;
  VARIABLE RetainDelay       : TIME;
  VARIABLE Data              : STD_ULOGIC;
  VARIABLE ExpandedPortFlag  : 
      VitalPortFlagVectorType(ScheduleDataArray'RANGE);
  VARIABLE NumBitsPerSubword : INTEGER;
BEGIN
  NumBitsPerSubword := 
      ScheduleDataArray(ScheduleDataArray'LEFT).NumBitsPerSubword;
  VitalMemoryExpandPortFlag( PortFlag, NumBitsPerSubword, ExpandedPortFlag );
  FOR i IN ScheduleDataArray'RANGE LOOP
    NEXT WHEN ExpandedPortFlag(i).OutputDisable = TRUE;

    PropDelay := ScheduleDataArray(i).PropDelay;
    RetainDelay := ScheduleDataArray(i).OutputRetainDelay;   
    
    NEXT WHEN PropDelay = TIME'HIGH;

    Age  := ScheduleDataArray(i).InputAge; 
    Data := ScheduleDataArray(i).OutputData;

    IF (Age < RetainDelay and RetainDelay < PropDelay) THEN
      OutSignal(i) <= TRANSPORT 'X' AFTER (RetainDelay - Age);
    END IF;

    IF (Age <= PropDelay) THEN
      OutSignal(i)<= TRANSPORT OutputMap(Data)AFTER (PropDelay-Age);
      ScheduleDataArray(i).ScheduleValue := Data;
      ScheduleDataArray(i).ScheduleTime := NOW + PropDelay - Age;
    END IF;
  END LOOP;

  -- for debug purpose
  PrintScheduleDataArray(ScheduleDataArray);

  -- for debug purpose
  ScheduleDebugMsg;
END VitalMemorySchedulePathDelay;

-- ----------------------------------------------------------------------------
PROCEDURE VitalMemorySchedulePathDelay (
  SIGNAL   OutSignal       : OUT STD_ULOGIC;
  CONSTANT OutputSignalName: IN STRING :="";
  CONSTANT PortFlag        : IN VitalPortFlagType := VitalDefaultPortFlag;
  CONSTANT OutputMap       : IN VitalOutputMapType := VitalDefaultOutputMap;
  VARIABLE ScheduleData    : INOUT VitalMemoryScheduleDataType
) IS
  VARIABLE Age               : TIME;
  VARIABLE PropDelay         : TIME;
  VARIABLE RetainDelay       : TIME;
  VARIABLE Data              : STD_ULOGIC;
  VARIABLE ScheduleDataArray : VitalMemoryScheduleDataVectorType (0 downto 0);
BEGIN
  IF (PortFlag.OutputDisable /= TRUE) THEN
    ScheduledataArray(0) := ScheduleData;
    PropDelay := ScheduleDataArray(0).PropDelay;
    RetainDelay := ScheduleDataArray(0).OutputRetainDelay;
    Age := ScheduleDataArray(0).InputAge; 
    Data := ScheduleDataArray(0).OutputData;

    IF (Age < RetainDelay and RetainDelay < PropDelay) THEN
      OutSignal <= TRANSPORT 'X' AFTER (RetainDelay - Age);
    END IF;

    IF (Age <= PropDelay and PropDelay /= TIME'HIGH) THEN
      OutSignal <= TRANSPORT OutputMap(Data)  AFTER (PropDelay - Age);
      ScheduleDataArray(0).ScheduleValue := Data;
      ScheduleDataArray(0).ScheduleTime := NOW + PropDelay - Age;
    END IF;
  END IF;

  -- for debug purpose
  PrintScheduleDataArray(ScheduleDataArray);

  -- for debug purpose
  ScheduleDebugMsg;

END VitalMemorySchedulePathDelay;

-- ----------------------------------------------------------------------------
-- Procedure  : InternalTimingCheck
-- ----------------------------------------------------------------------------
PROCEDURE InternalTimingCheck (
  CONSTANT TestSignal    : IN     std_ulogic;
  CONSTANT RefSignal     : IN     std_ulogic;
  CONSTANT TestDelay     : IN     TIME := 0 ns;
  CONSTANT RefDelay      : IN     TIME := 0 ns;
  CONSTANT SetupHigh     : IN     TIME := 0 ns;
  CONSTANT SetupLow      : IN     TIME := 0 ns;
  CONSTANT HoldHigh      : IN     TIME := 0 ns;
  CONSTANT HoldLow       : IN     TIME := 0 ns;
  VARIABLE RefTime       : IN     TIME;
  VARIABLE RefEdge       : IN     BOOLEAN;
  VARIABLE TestTime      : IN     TIME;
  VARIABLE TestEvent     : IN     BOOLEAN;
  VARIABLE SetupEn       : INOUT  BOOLEAN;
  VARIABLE HoldEn        : INOUT  BOOLEAN;
  VARIABLE CheckInfo     : INOUT  CheckInfoType;
  CONSTANT MsgOn         : IN     BOOLEAN
) IS
  VARIABLE bias           : TIME;
  VARIABLE actualObsTime  : TIME;
  VARIABLE BC             : TIME;
  VARIABLE Message        :LINE;
BEGIN
  -- Check SETUP constraint
  IF (RefEdge) THEN
    IF (SetupEn) THEN
      CheckInfo.ObsTime   := RefTime - TestTime;
      CheckInfo.State     := To_X01(TestSignal);
      CASE CheckInfo.State IS
        WHEN '0' =>
          CheckInfo.ExpTime := SetupLow;
          -- start of new code IR245-246
          BC := HoldHigh;
          -- end of new code IR245-246
        WHEN '1' =>
          CheckInfo.ExpTime := SetupHigh;
          -- start of new code IR245-246
          BC := HoldLow;
          -- end of new code IR245-246
        WHEN 'X' => 
          CheckInfo.ExpTime := Maximum(SetupHigh,SetupLow);
          -- start of new code IR245-246
          BC := Maximum(HoldHigh,HoldLow);
          -- end of new code IR245-246
      END CASE;
      -- added the second condition for IR 245-246
      CheckInfo.Violation := 
          ((CheckInfo.ObsTime < CheckInfo.ExpTime) 
            AND ( NOT ((CheckInfo.ObsTime = BC) and (BC = 0 ns))));
      -- start of new code IR245-246
      IF (CheckInfo.ExpTime = 0 ns) THEN
        CheckInfo.CheckKind := HoldCheck;
      ELSE
        CheckInfo.CheckKind := SetupCheck;
      END IF;
      -- end of new code IR245-246
      SetupEn  := FALSE;
    ELSE
      CheckInfo.Violation := FALSE;
    END IF;

  -- Check HOLD constraint
  ELSIF (TestEvent) THEN
    IF HoldEn THEN
      CheckInfo.ObsTime := TestTime - RefTime;
      CheckInfo.State := To_X01(TestSignal);
      CASE CheckInfo.State IS
        WHEN '0' =>
          CheckInfo.ExpTime := HoldHigh;
          -- new code for unnamed IR 
          CheckInfo.State := '1';
          -- start of new code IR245-246
          BC := SetupLow;
          -- end of new code IR245-246
        WHEN '1' =>
          CheckInfo.ExpTime := HoldLow;
          -- new code for unnamed IR 
          CheckInfo.State := '0';
          -- start of new code IR245-246
          BC := SetupHigh;
          -- end of new code IR245-246
        WHEN 'X' =>
          CheckInfo.ExpTime := Maximum(HoldHigh,HoldLow);
          -- start of new code IR245-246
          BC := Maximum(SetupHigh,SetupLow);
          -- end of new code IR245-246
      END CASE;
      -- added the second condition for IR 245-246
      CheckInfo.Violation := 
        ((CheckInfo.ObsTime < CheckInfo.ExpTime) 
          AND ( NOT ((CheckInfo.ObsTime = BC) and (BC = 0 ns))));
      -- start of new code IR245-246
      IF (CheckInfo.ExpTime = 0 ns) THEN
        CheckInfo.CheckKind := SetupCheck;
      ELSE
        CheckInfo.CheckKind := HoldCheck;
      END IF;
      -- end of new code IR245-246
      HoldEn := NOT CheckInfo.Violation;
    ELSE
      CheckInfo.Violation := FALSE;
    END IF;
  ELSE
    CheckInfo.Violation := FALSE;
  END IF;

  -- Adjust report values to account for internal model delays
  -- Note: TestDelay, RefDelay, TestTime, RefTime are non-negative
  -- Note: bias may be negative or positive
  IF MsgOn AND CheckInfo.Violation THEN
    -- modified the code for correct reporting of violation in case of 
    -- order of signals being reversed because of internal delays
    -- new variable 
    actualObsTime := (TestTime-TestDelay)-(RefTime-RefDelay);
    bias := TestDelay - RefDelay;
    IF (actualObsTime < 0 ns) THEN -- It should be a setup check
      IF ( CheckInfo.CheckKind = HoldCheck) THEN
        CheckInfo.CheckKind := SetupCheck;
        CASE CheckInfo.State IS
          WHEN '0' => CheckInfo.ExpTime := SetupLow; 
          WHEN '1' => CheckInfo.ExpTime := SetupHigh;
          WHEN 'X' => CheckInfo.ExpTime := Maximum(SetupHigh,SetupLow);
        END CASE;	       	  
      END IF;
      CheckInfo.ObsTime := -actualObsTime;
      CheckInfo.ExpTime := CheckInfo.ExpTime + bias;
      CheckInfo.DetTime := RefTime - RefDelay;
    ELSE -- It should be a hold check
      IF (CheckInfo.CheckKind = SetupCheck) THEN
        CheckInfo.CheckKind :=  HoldCheck;
        CASE CheckInfo.State IS
          WHEN '0' =>
            CheckInfo.ExpTime := HoldHigh;
            CheckInfo.State   := '1';
          WHEN '1' =>
            CheckInfo.ExpTime := HoldLow;
            CheckInfo.State   := '0';
          WHEN 'X' =>
            CheckInfo.ExpTime := Maximum(HoldHigh,HoldLow);
        END CASE;
      END IF;
      CheckInfo.ObsTime := actualObsTime;
      CheckInfo.ExpTime := CheckInfo.ExpTime - bias;
      CheckInfo.DetTime := TestTime - TestDelay;
    END IF;
  END IF;
END InternalTimingCheck;


-- ----------------------------------------------------------------------------
-- Setup and Hold Time Check Routine 
-- ----------------------------------------------------------------------------
PROCEDURE TimingArrayIndex (
  SIGNAL InputSignal      : IN Std_logic_vector;
  CONSTANT ArrayIndexNorm : IN INTEGER;
  VARIABLE Index          : OUT INTEGER
) IS
BEGIN
  IF (InputSignal'LEFT > InputSignal'RIGHT) THEN
    Index  := ArrayIndexNorm + InputSignal'RIGHT;
  ELSE
    Index  := InputSignal'RIGHT - ArrayIndexNorm;
  END IF;
END TimingArrayIndex;

-- ----------------------------------------------------------------------------
PROCEDURE VitalMemoryReportViolation (
  CONSTANT TestSignalName : IN STRING := "";
  CONSTANT RefSignalName  : IN STRING := "";
  CONSTANT HeaderMsg      : IN STRING := " ";
  CONSTANT CheckInfo      : IN CheckInfoType;
  CONSTANT MsgSeverity    : IN SEVERITY_LEVEL := WARNING
) IS
  VARIABLE Message : LINE;
BEGIN
  IF (NOT CheckInfo.Violation) THEN
    RETURN;
  END IF;
  Write ( Message, HeaderMsg );
  CASE CheckInfo.CheckKind IS
    WHEN    SetupCheck => Write ( Message, STRING'(" SETUP ")      );
    WHEN     HoldCheck => Write ( Message, STRING'(" HOLD ")       );
    WHEN RecoveryCheck => Write ( Message, STRING'(" RECOVERY ")   );
    WHEN  RemovalCheck => Write ( Message, STRING'(" REMOVAL ")    );
    WHEN PulseWidCheck => Write ( Message, STRING'(" PULSE WIDTH "));
    WHEN   PeriodCheck => Write ( Message, STRING'(" PERIOD ")     );
  END CASE;
  Write ( Message, HiLoStr(CheckInfo.State) );
  Write ( Message, STRING'(" VIOLATION ON ") );
  Write ( Message, TestSignalName );
  IF (RefSignalName'LENGTH > 0) THEN
    Write ( Message, STRING'(" WITH RESPECT TO ") );
    Write ( Message, RefSignalName );
  END IF;
  Write ( Message, ';' & LF );
  Write ( Message, STRING'("  Expected := ")  );
  Write ( Message, CheckInfo.ExpTime);
  Write ( Message, STRING'("; Observed := ")  );
  Write ( Message, CheckInfo.ObsTime);
  Write ( Message, STRING'("; At : ")         );
  Write ( Message, CheckInfo.DetTime);
  ASSERT FALSE REPORT Message.ALL SEVERITY MsgSeverity;
  DEALLOCATE (Message);
END VitalMemoryReportViolation;

-- ----------------------------------------------------------------------------
PROCEDURE VitalMemoryReportViolation (
  CONSTANT TestSignalName : IN STRING := "";
  CONSTANT RefSignalName  : IN STRING := "";
  CONSTANT TestArrayIndex : IN INTEGER;
  CONSTANT RefArrayIndex  : IN INTEGER;
  SIGNAL TestSignal       : IN std_logic_vector;
  SIGNAL RefSignal        : IN std_logic_vector;
  CONSTANT HeaderMsg      : IN STRING := " ";
  CONSTANT CheckInfo      : IN CheckInfoType;
  CONSTANT MsgFormat      : IN VitalMemoryMsgFormatType;
  CONSTANT MsgSeverity    : IN SEVERITY_LEVEL := WARNING
) IS
  VARIABLE Message : LINE;
  VARIABLE i, j : INTEGER;
BEGIN
  IF (NOT CheckInfo.Violation) THEN 
    RETURN; 
  END IF;

  Write ( Message, HeaderMsg );
  CASE CheckInfo.CheckKind IS
    WHEN    SetupCheck => Write ( Message, STRING'(" SETUP ")      );
    WHEN     HoldCheck => Write ( Message, STRING'(" HOLD ")       );
    WHEN PulseWidCheck => Write ( Message, STRING'(" PULSE WIDTH "));
    WHEN   PeriodCheck => Write ( Message, STRING'(" PERIOD ")     );
    WHEN OTHERS        => Write ( Message, STRING'(" UNKNOWN ")     );
  END CASE;
  Write ( Message, HiLoStr(CheckInfo.State) );
  Write ( Message, STRING'(" VIOLATION ON ") );
  Write ( Message, TestSignalName );
  TimingArrayIndex(TestSignal, TestArrayIndex, i);
  CASE MsgFormat IS
    WHEN Scalar => 
      NULL;
    WHEN VectorEnum =>
      Write ( Message, '_');
      Write ( Message, i);  
    WHEN Vector =>
      Write ( Message, '(');
      Write ( Message, i); 
      Write ( Message, ')'); 
  END CASE;

  IF (RefSignalName'LENGTH > 0) THEN
    Write ( Message, STRING'(" WITH RESPECT TO ") );
    Write ( Message, RefSignalName );
  END IF;

  IF(RefSignal'LENGTH > 0) THEN
    TimingArrayIndex(RefSignal, RefArrayIndex, j);
    CASE MsgFormat IS
      WHEN Scalar => 
        NULL;
      WHEN VectorEnum =>
        Write ( Message, '_');
        Write ( Message, j);  
      WHEN Vector =>
        Write ( Message, '(');
        Write ( Message, j); 
        Write ( Message, ')');
    END CASE;
  END IF;

  Write ( Message, ';' & LF );
  Write ( Message, STRING'("  Expected := ")  );
  Write ( Message, CheckInfo.ExpTime);
  Write ( Message, STRING'("; Observed := ")  );
  Write ( Message, CheckInfo.ObsTime);
  Write ( Message, STRING'("; At : ")         );
  Write ( Message, CheckInfo.DetTime);

  ASSERT FALSE REPORT Message.ALL SEVERITY MsgSeverity;

  DEALLOCATE (Message);
END VitalMemoryReportViolation;

-- ----------------------------------------------------------------------------
PROCEDURE VitalMemoryReportViolation (
  CONSTANT TestSignalName : IN STRING := "";
  CONSTANT RefSignalName  : IN STRING := "";
  CONSTANT TestArrayIndex : IN INTEGER;
  CONSTANT HeaderMsg      : IN STRING := " ";
  CONSTANT CheckInfo      : IN CheckInfoType;
  CONSTANT MsgFormat      : IN VitalMemoryMsgFormatType;
  CONSTANT MsgSeverity    : IN SEVERITY_LEVEL := WARNING
) IS
  VARIABLE Message : LINE;
BEGIN
  IF (NOT CheckInfo.Violation) THEN 
    RETURN; 
  END IF;

  Write ( Message, HeaderMsg );
  CASE CheckInfo.CheckKind IS
    WHEN    SetupCheck => Write ( Message, STRING'(" SETUP ")      );
    WHEN     HoldCheck => Write ( Message, STRING'(" HOLD ")       );
    WHEN PulseWidCheck => Write ( Message, STRING'(" PULSE WIDTH "));
    WHEN   PeriodCheck => Write ( Message, STRING'(" PERIOD ")     );
    WHEN        OTHERS => Write ( Message, STRING'(" UNKNOWN ")     );
  END CASE;

  Write ( Message, HiLoStr(CheckInfo.State) );
  Write ( Message, STRING'(" VIOLATION ON ") );
  Write ( Message, TestSignalName );

  CASE MsgFormat IS
    WHEN Scalar => 
      NULL;
    WHEN VectorEnum =>
      Write ( Message, '_');
      Write ( Message, TestArrayIndex);  
    WHEN Vector =>
      Write ( Message, '(');
      Write ( Message, TestArrayIndex); 
      Write ( Message, ')');
  END CASE;

  IF (RefSignalName'LENGTH > 0) THEN
    Write ( Message, STRING'(" WITH RESPECT TO ") );
    Write ( Message, RefSignalName );
  END IF;

  Write ( Message, ';' & LF );
  Write ( Message, STRING'("  Expected := ")  );
  Write ( Message, CheckInfo.ExpTime);
  Write ( Message, STRING'("; Observed := ")  );
  Write ( Message, CheckInfo.ObsTime);
  Write ( Message, STRING'("; At : ")         );
  Write ( Message, CheckInfo.DetTime);

  ASSERT FALSE REPORT Message.ALL SEVERITY MsgSeverity;

  DEALLOCATE (Message);
END VitalMemoryReportViolation;

-- ----------------------------------------------------------------------------
FUNCTION VitalMemoryTimingDataInit 
RETURN VitalMemoryTimingDataType IS
BEGIN
  RETURN (FALSE, 'X', 0 ns, FALSE, 'X', 0 ns, FALSE, 
          NULL, NULL, NULL, NULL, NULL, NULL);
END;

-- ----------------------------------------------------------------------------
-- Procedure:   VitalSetupHoldCheck
-- ----------------------------------------------------------------------------
PROCEDURE VitalMemorySetupHoldCheck (
  VARIABLE Violation     : OUT    X01ArrayT;
  VARIABLE TimingData    : INOUT  VitalMemoryTimingDataType;
  SIGNAL   TestSignal    : IN     std_ulogic;
  CONSTANT TestSignalName: IN     STRING := "";
  CONSTANT TestDelay     : IN     TIME := 0 ns;
  SIGNAL   RefSignal     : IN     std_ulogic;
  CONSTANT RefSignalName : IN     STRING := "";
  CONSTANT RefDelay      : IN     TIME := 0 ns;
  CONSTANT SetupHigh     : IN     VitalDelayType;
  CONSTANT SetupLow      : IN     VitalDelayType;
  CONSTANT HoldHigh      : IN     VitalDelayType;
  CONSTANT HoldLow       : IN     VitalDelayType;
  CONSTANT CheckEnabled  : IN     VitalBoolArrayT;
  CONSTANT RefTransition : IN     VitalEdgeSymbolType;
  CONSTANT HeaderMsg     : IN     STRING := " ";
  CONSTANT XOn           : IN     BOOLEAN := TRUE;
  CONSTANT MsgOn         : IN     BOOLEAN := TRUE;
  CONSTANT MsgSeverity   : IN     SEVERITY_LEVEL := WARNING;
  --IR252 3/23/98
  CONSTANT EnableSetupOnTest : IN   BOOLEAN := TRUE; 
  CONSTANT EnableSetupOnRef  : IN   BOOLEAN := TRUE; 
  CONSTANT EnableHoldOnRef   : IN   BOOLEAN := TRUE; 
  CONSTANT EnableHoldOnTest  : IN   BOOLEAN := TRUE  
) IS
  VARIABLE CheckInfo       : CheckInfoType;
  VARIABLE CheckEnScalar   : BOOLEAN := FALSE;
  VARIABLE ViolationInt    : X01ArrayT(CheckEnabled'RANGE);
  VARIABLE RefEdge         : BOOLEAN;
  VARIABLE TestEvent       : BOOLEAN;
  VARIABLE TestDly         : TIME := Maximum(0 ns, TestDelay);
  VARIABLE RefDly          : TIME := Maximum(0 ns, RefDelay);
  VARIABLE bias            : TIME;
BEGIN

  -- Initialization of working area. 
  IF (TimingData.NotFirstFlag = FALSE) THEN
    TimingData.TestLast     := To_X01(TestSignal);
    TimingData.RefLast      := To_X01(RefSignal);
    TimingData.NotFirstFlag := TRUE;
  END IF;

  -- Detect reference edges and record the time of the last edge
  RefEdge := EdgeSymbolMatch(TimingData.RefLast, To_X01(RefSignal),
               RefTransition);
  TimingData.RefLast := To_X01(RefSignal);
  IF (RefEdge) THEN
    TimingData.RefTime     := NOW;
    --TimingData.HoldEnA.all := (TestSignal'RANGE=>TRUE);
    --IR252 3/23/98
    TimingData.SetupEn := TimingData.SetupEn AND EnableSetupOnRef; 
    TimingData.HoldEn  := EnableHoldOnRef; 
  END IF;

  -- Detect test (data) changes and record the time of the last change
  TestEvent := TimingData.TestLast /= To_X01Z(TestSignal);
  TimingData.TestLast := To_X01Z(TestSignal);
  IF TestEvent THEN
    TimingData.SetupEn  := EnableSetupOnTest ; --IR252 3/23/98
    TimingData.HoldEn := TimingData.HoldEn AND EnableHoldOnTest ;
                                                --IR252 3/23/98
    TimingData.TestTime := NOW;
  END IF;

  FOR i IN CheckEnabled'RANGE LOOP
    IF CheckEnabled(i) = TRUE THEN
      CheckEnScalar := TRUE;
    END IF;
    ViolationInt(i) := '0'; 
  END LOOP;

  IF (CheckEnScalar) THEN
    InternalTimingCheck (
      TestSignal   => TestSignal,
      RefSignal    => RefSignal,
      TestDelay    => TestDly,
      RefDelay     => RefDly,
      SetupHigh    => SetupHigh,
      SetupLow     => SetupLow,
      HoldHigh     => HoldHigh,
      HoldLow      => HoldLow,
      RefTime      => TimingData.RefTime,
      RefEdge      => RefEdge,
      TestTime     => TimingData.TestTime,
      TestEvent    => TestEvent,
      SetupEn      => TimingData.SetupEn,
      HoldEn       => TimingData.HoldEn,
      CheckInfo    => CheckInfo,
      MsgOn        => MsgOn 
    );

    -- Report any detected violations and set return violation flag
    IF CheckInfo.Violation THEN
      IF (MsgOn) THEN
        VitalMemoryReportViolation (TestSignalName, RefSignalName,
          HeaderMsg, CheckInfo, MsgSeverity );
      END IF;
      IF (XOn) THEN 
        FOR i IN CheckEnabled'RANGE LOOP
          IF CheckEnabled(i) = TRUE THEN
              ViolationInt(i) := 'X'; 
          END IF;
        END LOOP;
      END IF;
    END IF;
  END IF;
  Violation := ViolationInt;
END VitalMemorySetupHoldCheck;

-- ----------------------------------------------------------------------------
PROCEDURE VitalMemorySetupHoldCheck (
  VARIABLE Violation     : OUT    X01ArrayT;
  VARIABLE TimingData    : INOUT  VitalMemoryTimingDataType;
  SIGNAL   TestSignal    : IN     std_logic_vector;
  CONSTANT TestSignalName: IN     STRING := "";
  CONSTANT TestDelay     : IN     VitalDelayArraytype;
  SIGNAL   RefSignal     : IN     std_ulogic;
  CONSTANT RefSignalName : IN     STRING := "";
  CONSTANT RefDelay      : IN     TIME := 0 ns;
  CONSTANT SetupHigh     : IN     VitalDelayArraytype;
  CONSTANT SetupLow      : IN     VitalDelayArraytype;
  CONSTANT HoldHigh      : IN     VitalDelayArraytype;
  CONSTANT HoldLow       : IN     VitalDelayArraytype;
  CONSTANT CheckEnabled  : IN     BOOLEAN := TRUE;
  CONSTANT RefTransition : IN     VitalEdgeSymbolType;
  CONSTANT HeaderMsg     : IN     STRING := " ";
  CONSTANT XOn           : IN     BOOLEAN := TRUE;
  CONSTANT MsgOn         : IN     BOOLEAN := TRUE;
  CONSTANT MsgSeverity   : IN     SEVERITY_LEVEL := WARNING;
  CONSTANT MsgFormat     : IN     VitalMemoryMsgFormatType;
  --IR252 3/23/98
  CONSTANT EnableSetupOnTest : IN   BOOLEAN := TRUE; 
  CONSTANT EnableSetupOnRef  : IN   BOOLEAN := TRUE; 
  CONSTANT EnableHoldOnRef   : IN   BOOLEAN := TRUE; 
  CONSTANT EnableHoldOnTest  : IN   BOOLEAN := TRUE  
) IS
  VARIABLE CheckInfo     : CheckInfoType;
  VARIABLE RefEdge       : BOOLEAN;
  VARIABLE TestEvent     : VitalBoolArrayT(TestSignal'RANGE);
  VARIABLE TestDly       : TIME;
  VARIABLE RefDly        : TIME := Maximum(0 ns, RefDelay);
  VARIABLE bias          : TIME;
BEGIN

  -- Initialization of working area. 
  IF (TimingData.NotFirstFlag = FALSE) THEN
    TimingData.TestLastA := NEW std_logic_vector(TestSignal'RANGE);
    TimingData.TestTimeA := NEW VitalTimeArrayT(TestSignal'RANGE);
    TimingData.HoldEnA   := NEW VitalBoolArrayT(TestSignal'RANGE);
    TimingData.SetupEnA  := NEW VitalBoolArrayT(TestSignal'RANGE);
    FOR i IN TestSignal'RANGE LOOP
      TimingData.TestLastA(i) := To_X01(TestSignal(i));
    END LOOP;
    TimingData.RefLast := To_X01(RefSignal);
    TimingData.NotFirstFlag := TRUE;
  END IF;

  -- Detect reference edges and record the time of the last edge
  RefEdge := EdgeSymbolMatch(TimingData.RefLast, To_X01(RefSignal),
               RefTransition);
  TimingData.RefLast := To_X01(RefSignal);
  IF (RefEdge) THEN
    TimingData.RefTime     := NOW;
    --TimingData.HoldEnA.all := (TestSignal'RANGE=>TRUE);
    --IR252 3/23/98
    FOR i IN TestSignal'RANGE LOOP
      TimingData.SetupEnA(i) 
        := TimingData.SetupEnA(i) AND EnableSetupOnRef; 
      TimingData.HoldEnA(i)  := EnableHoldOnRef; 
    END LOOP;
  END IF;

  -- Detect test (data) changes and record the time of the last change
  FOR i IN TestSignal'RANGE LOOP
    TestEvent(i) := TimingData.TestLastA(i) /= To_X01Z(TestSignal(i));
    TimingData.TestLastA(i) := To_X01Z(TestSignal(i));
    IF TestEvent(i) THEN
      TimingData.SetupEnA(i)  := EnableSetupOnTest ; --IR252 3/23/98
      TimingData.HoldEnA(i) := TimingData.HoldEnA(i) AND EnableHoldOnTest ;
                                                  --IR252 3/23/98
      TimingData.TestTimeA(i) := NOW;
      --TimingData.SetupEnA(i)  := TRUE;
      TimingData.TestTime := NOW;
    END IF;
  END LOOP;

  FOR i IN TestSignal'RANGE LOOP
    Violation(i) := '0';

    IF (CheckEnabled) THEN
      TestDly := Maximum(0 ns, TestDelay(i));
      InternalTimingCheck (
        TestSignal   => TestSignal(i),
        RefSignal    => RefSignal,
        TestDelay    => TestDly,
        RefDelay     => RefDly,
        SetupHigh    => SetupHigh(i),
        SetupLow     => SetupLow(i),
        HoldHigh     => HoldHigh(i),
        HoldLow      => HoldLow(i),
        RefTime      => TimingData.RefTime,
        RefEdge      => RefEdge,
        TestTime     => TimingData.TestTimeA(i),
        TestEvent    => TestEvent(i),
        SetupEn      => TimingData.SetupEnA(i),
        HoldEn       => TimingData.HoldEnA(i),
        CheckInfo    => CheckInfo,
        MsgOn        => MsgOn 
      );

      -- Report any detected violations and set return violation flag
      IF CheckInfo.Violation THEN
        IF (MsgOn) THEN
          VitalMemoryReportViolation (TestSignalName, RefSignalName, i ,
            HeaderMsg, CheckInfo, MsgFormat, MsgSeverity );
        END IF;
        IF (XOn) THEN
          Violation(i) := 'X';
        END IF;
      END IF;
    END IF;
  END LOOP;

END VitalMemorySetupHoldCheck;

-- ----------------------------------------------------------------------------
PROCEDURE VitalMemorySetupHoldCheck (
  VARIABLE Violation     : OUT    X01ArrayT;
  VARIABLE TimingData    : INOUT  VitalMemoryTimingDataType;
  SIGNAL   TestSignal    : IN     std_logic_vector;
  CONSTANT TestSignalName: IN     STRING := "";
  CONSTANT TestDelay     : IN     VitalDelayArraytype;
  SIGNAL   RefSignal     : IN     std_ulogic;
  CONSTANT RefSignalName : IN     STRING := "";
  CONSTANT RefDelay      : IN     TIME := 0 ns;
  CONSTANT SetupHigh     : IN     VitalDelayArraytype;
  CONSTANT SetupLow      : IN     VitalDelayArraytype;
  CONSTANT HoldHigh      : IN     VitalDelayArraytype;
  CONSTANT HoldLow       : IN     VitalDelayArraytype;
  CONSTANT CheckEnabled  : IN     VitalBoolArrayT;
  CONSTANT RefTransition : IN     VitalEdgeSymbolType;
  CONSTANT ArcType       : IN     VitalMemoryArcType := CrossArc;
  CONSTANT NumBitsPerSubWord : IN INTEGER := 1;
  CONSTANT HeaderMsg     : IN     STRING := " ";
  CONSTANT XOn           : IN     BOOLEAN := TRUE;
  CONSTANT MsgOn         : IN     BOOLEAN := TRUE;
  CONSTANT MsgSeverity   : IN     SEVERITY_LEVEL := WARNING;
  CONSTANT MsgFormat     : IN     VitalMemoryMsgFormatType;
  --IR252 3/23/98
  CONSTANT EnableSetupOnTest : IN   BOOLEAN := TRUE; 
  CONSTANT EnableSetupOnRef  : IN   BOOLEAN := TRUE; 
  CONSTANT EnableHoldOnRef   : IN   BOOLEAN := TRUE; 
  CONSTANT EnableHoldOnTest  : IN   BOOLEAN := TRUE  
) IS
  VARIABLE CheckInfo       : CheckInfoType;
  VARIABLE ViolationInt    : X01ArrayT(TestSignal'RANGE);
  VARIABLE ViolationIntNorm: X01ArrayT(TestSignal'LENGTH-1 downto 0);
  VARIABLE ViolationNorm   : X01ArrayT(Violation'LENGTH-1 downto 0);
  VARIABLE CheckEnInt      : VitalBoolArrayT(TestSignal'RANGE);
  VARIABLE CheckEnIntNorm  : VitalBoolArrayT(TestSignal'LENGTH-1 downto 0);
  VARIABLE CheckEnScalar   : BOOLEAN := FALSE; --Mem IR 401
  VARIABLE CheckEnabledNorm: VitalBoolArrayT(CheckEnabled'LENGTH-1 downto 0);
  VARIABLE RefEdge         : BOOLEAN;
  VARIABLE TestEvent       : VitalBoolArrayT(TestSignal'RANGE);
  VARIABLE TestDly         : TIME;
  VARIABLE RefDly          : TIME := Maximum(0 ns, RefDelay);
  VARIABLE bias            : TIME;
BEGIN

  -- Initialization of working area. 
  IF (TimingData.NotFirstFlag = FALSE) THEN
    TimingData.TestLastA := NEW std_logic_vector(TestSignal'RANGE);
    TimingData.TestTimeA := NEW VitalTimeArrayT(TestSignal'RANGE);
    TimingData.HoldEnA   := NEW VitalBoolArrayT(TestSignal'RANGE);
    TimingData.SetupEnA  := NEW VitalBoolArrayT(TestSignal'RANGE);
    FOR i IN TestSignal'RANGE LOOP
      TimingData.TestLastA(i) := To_X01(TestSignal(i));
    END LOOP;
    TimingData.RefLast := To_X01(RefSignal);
    TimingData.NotFirstFlag := TRUE;
  END IF;

  -- Detect reference edges and record the time of the last edge
  RefEdge := EdgeSymbolMatch(TimingData.RefLast, To_X01(RefSignal),
               RefTransition);
  TimingData.RefLast := To_X01(RefSignal);
  IF RefEdge THEN
    TimingData.RefTime     := NOW;
    --TimingData.HoldEnA.all := (TestSignal'RANGE=>TRUE);
    --IR252 3/23/98
    FOR i IN TestSignal'RANGE LOOP
      TimingData.SetupEnA(i) 
        := TimingData.SetupEnA(i) AND EnableSetupOnRef; 
      TimingData.HoldEnA(i)  := EnableHoldOnRef; 
    END LOOP;
  END IF;

  -- Detect test (data) changes and record the time of the last change
  FOR i IN TestSignal'RANGE LOOP
    TestEvent(i) := TimingData.TestLastA(i) /= To_X01Z(TestSignal(i));
    TimingData.TestLastA(i) := To_X01Z(TestSignal(i));
    IF TestEvent(i) THEN
      TimingData.SetupEnA(i)  := EnableSetupOnTest ; --IR252 3/23/98
      TimingData.HoldEnA(i) := TimingData.HoldEnA(i) AND EnableHoldOnTest ;
                                                  --IR252 3/23/98
      TimingData.TestTimeA(i) := NOW;
      --TimingData.SetupEnA(i)  := TRUE;
      TimingData.TestTime := NOW;
    END IF;
  END LOOP;

  IF ArcType = CrossArc THEN
    CheckEnScalar := FALSE;
    FOR i IN CheckEnabled'RANGE LOOP
      IF CheckEnabled(i) = TRUE THEN
        CheckEnScalar := TRUE;
      END IF;
    END LOOP;
    FOR i IN CheckEnInt'RANGE LOOP
      CheckEnInt(i) := CheckEnScalar;
    END LOOP;
  ELSE
    FOR i IN CheckEnIntNorm'RANGE LOOP
      CheckEnIntNorm(i) := CheckEnabledNorm(i / NumBitsPerSubWord );
    END LOOP;
    CheckEnInt := CheckEnIntNorm;
  END IF;

  FOR i IN TestSignal'RANGE LOOP
    ViolationInt(i) := '0';

    IF (CheckEnInt(i)) THEN
      TestDly := Maximum(0 ns, TestDelay(i));
      InternalTimingCheck (
        TestSignal   => TestSignal(i),
        RefSignal    => RefSignal,
        TestDelay    => TestDly,
        RefDelay     => RefDly,
        SetupHigh    => SetupHigh(i),
        SetupLow     => SetupLow(i),
        HoldHigh     => HoldHigh(i),
        HoldLow      => HoldLow(i),
        RefTime      => TimingData.RefTime,
        RefEdge      => RefEdge,
        TestTime     => TimingData.TestTimeA(i),
        TestEvent    => TestEvent(i),
        SetupEn      => TimingData.SetupEnA(i),
        HoldEn       => TimingData.HoldEnA(i),
        CheckInfo    => CheckInfo,
        MsgOn        => MsgOn 
      );

      -- Report any detected violations and set return violation flag
      IF CheckInfo.Violation THEN
        IF (MsgOn) THEN
          VitalMemoryReportViolation (TestSignalName, RefSignalName, i ,
            HeaderMsg, CheckInfo, MsgFormat, MsgSeverity );
        END IF;
        IF (XOn) THEN
          ViolationInt(i) := 'X';
        END IF;
      END IF;
    END IF;
  END LOOP;

  IF (ViolationInt'LENGTH = Violation'LENGTH) THEN
    Violation := ViolationInt;
  ELSE
    ViolationIntNorm := ViolationInt;
    FOR i IN ViolationNorm'RANGE LOOP
      ViolationNorm(i) := '0';
    END LOOP;
    FOR i IN ViolationIntNorm'RANGE LOOP
      IF (ViolationIntNorm(i) = 'X') THEN
        ViolationNorm(i / NumBitsPerSubWord) := 'X';
      END IF;
    END LOOP;
    Violation := ViolationNorm;
  END IF;

END VitalMemorySetupHoldCheck;

-- ----------------------------------------------------------------------------
PROCEDURE VitalMemorySetupHoldCheck (
  VARIABLE Violation     : OUT    X01ArrayT;
  VARIABLE TimingData    : INOUT  VitalMemoryTimingDataType;
  SIGNAL   TestSignal    : IN     std_logic_vector;
  CONSTANT TestSignalName: IN     STRING := "";
  CONSTANT TestDelay     : IN     VitalDelayArraytype;
  SIGNAL   RefSignal     : IN     std_logic_vector;
  CONSTANT RefSignalName : IN     STRING := "";
  CONSTANT RefDelay      : IN     VitalDelayArraytype;
  CONSTANT SetupHigh     : IN     VitalDelayArraytype;
  CONSTANT SetupLow      : IN     VitalDelayArraytype;
  CONSTANT HoldHigh      : IN     VitalDelayArraytype;
  CONSTANT HoldLow       : IN     VitalDelayArraytype;
  CONSTANT CheckEnabled  : IN     BOOLEAN := TRUE;
  CONSTANT RefTransition : IN     VitalEdgeSymbolType;
  CONSTANT ArcType       : IN     VitalMemoryArcType := CrossArc;
  CONSTANT NumBitsPerSubWord : IN INTEGER := 1;
  CONSTANT HeaderMsg     : IN     STRING := " ";
  CONSTANT XOn           : IN     BOOLEAN := TRUE;
  CONSTANT MsgOn         : IN     BOOLEAN := TRUE;
  CONSTANT MsgSeverity   : IN     SEVERITY_LEVEL := WARNING;
  CONSTANT MsgFormat     : IN     VitalMemoryMsgFormatType;
  --IR252 3/23/98
  CONSTANT EnableSetupOnTest : IN   BOOLEAN := TRUE; 
  CONSTANT EnableSetupOnRef  : IN   BOOLEAN := TRUE;
  CONSTANT EnableHoldOnRef   : IN   BOOLEAN := TRUE; 
  CONSTANT EnableHoldOnTest  : IN   BOOLEAN := TRUE 
) IS
  VARIABLE CheckInfo       : CheckInfoType;
  VARIABLE RefEdge         : VitalBoolArrayT(RefSignal'LENGTH-1 downto 0);
  VARIABLE TestEvent       : VitalBoolArrayT(TestSignal'LENGTH-1 downto 0);
  VARIABLE TestDly         : TIME;
  VARIABLE RefDly          : TIME;
  VARIABLE bias            : TIME;
  VARIABLE NumTestBits     : NATURAL := TestSignal'LENGTH;
  VARIABLE NumRefBits      : NATURAL := RefSignal'LENGTH;
  VARIABLE NumChecks       : NATURAL;

  VARIABLE ViolationTest   : X01ArrayT(NumTestBits-1 downto 0);
  VARIABLE ViolationRef    : X01ArrayT(NumRefBits-1 downto 0);

  VARIABLE TestSignalNorm  : std_logic_vector(NumTestBits-1 downto 0)
                              := TestSignal;
  VARIABLE TestDelayNorm   : VitalDelayArraytype(NumTestBits-1 downto 0)
                              := TestDelay;
  VARIABLE RefSignalNorm   : std_logic_vector(NumRefBits-1 downto 0)
                              := RefSignal;
  VARIABLE RefDelayNorm    : VitalDelayArraytype(NumRefBits-1 downto 0)
                              := RefDelay;
  VARIABLE SetupHighNorm   : VitalDelayArraytype(SetupHigh'LENGTH-1 downto 0)
                              := SetupHigh;
  VARIABLE SetupLowNorm    : VitalDelayArraytype(SetupLow'LENGTH-1 downto 0)
                              := SetupLow;
  VARIABLE HoldHighNorm    : VitalDelayArraytype(HoldHigh'LENGTH-1 downto 0)
                              := HoldHigh;
  VARIABLE HoldLowNorm     : VitalDelayArraytype(HoldLow'LENGTH-1 downto 0)
                              := HoldLow;

  VARIABLE RefBitLow       : NATURAL;
  VARIABLE RefBitHigh      : NATURAL;
  VARIABLE EnArrayIndex    : NATURAL;
  VARIABLE TimingArrayIndex: NATURAL;
BEGIN

  -- Initialization of working area. 
  IF (TimingData.NotFirstFlag = FALSE) THEN
    TimingData.TestLastA := NEW std_logic_vector(NumTestBits-1 downto 0);
    TimingData.TestTimeA := NEW VitalTimeArrayT(NumTestBits-1 downto 0);
    TimingData.RefTimeA  := NEW VitalTimeArrayT(NumRefBits-1 downto 0);
    TimingData.RefLastA  := NEW X01ArrayT(NumRefBits-1 downto 0);
    IF (ArcType = CrossArc) THEN
      NumChecks := RefSignal'LENGTH * TestSignal'LENGTH;
    ELSE
      NumChecks := TestSignal'LENGTH;
    END IF;
    TimingData.HoldEnA   := NEW VitalBoolArrayT(NumChecks-1 downto 0);
    TimingData.SetupEnA  := NEW VitalBoolArrayT(NumChecks-1 downto 0);

    FOR i IN TestSignalNorm'RANGE LOOP
      TimingData.TestLastA(i) := To_X01(TestSignalNorm(i));
    END LOOP;

    FOR i IN RefSignalNorm'RANGE LOOP
      TimingData.RefLastA(i) := To_X01(RefSignalNorm(i));
    END LOOP;
    TimingData.NotFirstFlag := TRUE;
  END IF;

  -- Detect reference edges and record the time of the last edge
  FOR i IN RefSignalNorm'RANGE LOOP
    RefEdge(i) := EdgeSymbolMatch(TimingData.RefLastA(i), 
                  To_X01(RefSignalNorm(i)), RefTransition);
    TimingData.RefLastA(i) := To_X01(RefSignalNorm(i));
    IF (RefEdge(i)) THEN
      TimingData.RefTimeA(i)  := NOW;
    END IF;
  END LOOP;

  -- Detect test (data) changes and record the time of the last change
  FOR i IN TestSignalNorm'RANGE LOOP
    TestEvent(i) := TimingData.TestLastA(i) /= To_X01Z(TestSignalNorm(i));
    TimingData.TestLastA(i) := To_X01Z(TestSignalNorm(i));
    IF (TestEvent(i)) THEN
      TimingData.TestTimeA(i) := NOW;
    END IF;
  END LOOP;

  FOR i IN ViolationTest'RANGE LOOP
    ViolationTest(i) := '0'; 
  END LOOP;
  FOR i IN ViolationRef'RANGE LOOP
    ViolationRef(i) := '0'; 
  END LOOP;

  FOR i IN TestSignalNorm'RANGE LOOP
    IF (ArcType = CrossArc) THEN
      FOR j IN RefSignalNorm'RANGE LOOP
        IF (TestEvent(i)) THEN
          --TimingData.SetupEnA(i*NumRefBits+j) := TRUE;
          --IR252
          TimingData.SetupEnA(i*NumRefBits+j) := EnableSetupOnTest;
          TimingData.HoldEnA(i*NumRefBits+j) 
               := TimingData.HoldEnA(i*NumRefBits+j) AND EnableHoldOnTest;
        END IF;
        IF (RefEdge(j)) THEN
          --TimingData.HoldEnA(i*NumRefBits+j) := TRUE;
          --IR252 
          TimingData.HoldEnA(i*NumRefBits+j) := EnableHoldOnRef; 
          TimingData.SetupEnA(i*NumRefBits+j) 
               := TimingData.SetupEnA(i*NumRefBits+j) AND EnableSetupOnRef;
        END IF;
      END LOOP;
      RefBitLow := 0;
      RefBitHigh := NumRefBits-1;
      TimingArrayIndex := i;
    ELSE
      IF ArcType = SubwordArc THEN
        RefBitLow  := i / NumBitsPerSubWord;
        TimingArrayIndex := i + NumTestBits * RefBitLow;
      ELSE
        RefBitLow  := i;
        TimingArrayIndex := i;
      END IF;
      RefBitHigh := RefBitLow;
      IF TestEvent(i) THEN
        --TimingData.SetupEnA(i) := TRUE;
        --IR252
        TimingData.SetupEnA(i) := EnableSetupOnTest;
        TimingData.HoldEnA(i) := TimingData.HoldEnA(i) AND EnableHoldOnTest;
      END IF;
      IF RefEdge(RefBitLow) THEN
        --TimingData.HoldEnA(i) := TRUE;
        --IR252
        TimingData.HoldEnA(i) := EnableHoldOnRef;
        TimingData.SetupEnA(i) := TimingData.SetupEnA(i) AND EnableSetupOnRef;
      END IF;
    END IF;

    EnArrayIndex := i;
    FOR j IN RefBitLow to RefBitHigh LOOP

      IF (CheckEnabled) THEN
        TestDly := Maximum(0 ns, TestDelayNorm(i));
        RefDly  := Maximum(0 ns, RefDelayNorm(j));

        InternalTimingCheck (
          TestSignal   => TestSignalNorm(i),
          RefSignal    => RefSignalNorm(j),
          TestDelay    => TestDly,
          RefDelay     => RefDly,
          SetupHigh    => SetupHighNorm(TimingArrayIndex),
          SetupLow     => SetupLowNorm(TimingArrayIndex),
          HoldHigh     => HoldHighNorm(TimingArrayIndex),
          HoldLow      => HoldLowNorm(TimingArrayIndex),
          RefTime      => TimingData.RefTimeA(j),
          RefEdge      => RefEdge(j),
          TestTime     => TimingData.TestTimeA(i),
          TestEvent    => TestEvent(i),
          SetupEn      => TimingData.SetupEnA(EnArrayIndex),
          HoldEn       => TimingData.HoldEnA(EnArrayIndex),
          CheckInfo    => CheckInfo,
          MsgOn        => MsgOn 
        );

        -- Report any detected violations and set return violation flag
        IF (CheckInfo.Violation) THEN
          IF (MsgOn) THEN
             VitalMemoryReportViolation (TestSignalName, RefSignalName, i, j,
                    TestSignal, RefSignal, HeaderMsg, CheckInfo, 
                    MsgFormat, MsgSeverity );
          END IF;
          IF (XOn) THEN
              ViolationTest(i) := 'X'; 
              ViolationRef(j) := 'X'; 
          END IF;
        END IF;
      END IF;

      TimingArrayIndex := TimingArrayIndex + NumRefBits;
      EnArrayIndex    := EnArrayIndex + NumRefBits;

    END LOOP;
  END LOOP;

  IF (ArcType = CrossArc) THEN
    Violation := ViolationRef;
  ELSE
    IF (Violation'LENGTH = ViolationRef'LENGTH) THEN
      Violation := ViolationRef;
    ELSE
      Violation := ViolationTest;
    END IF;
  END IF;

END VitalMemorySetupHoldCheck;

-- ----------------------------------------------------------------------------
PROCEDURE VitalMemorySetupHoldCheck (
  VARIABLE Violation     : OUT    X01ArrayT;
  VARIABLE TimingData    : INOUT  VitalMemoryTimingDataType;
  SIGNAL   TestSignal    : IN     std_logic_vector;
  CONSTANT TestSignalName: IN     STRING := "";
  CONSTANT TestDelay     : IN     VitalDelayArraytype;
  SIGNAL   RefSignal     : IN     std_logic_vector;
  CONSTANT RefSignalName : IN     STRING := "";
  CONSTANT RefDelay      : IN     VitalDelayArraytype;
  CONSTANT SetupHigh     : IN     VitalDelayArraytype;
  CONSTANT SetupLow      : IN     VitalDelayArraytype;
  CONSTANT HoldHigh      : IN     VitalDelayArraytype;
  CONSTANT HoldLow       : IN     VitalDelayArraytype;
  CONSTANT CheckEnabled  : IN     VitalBoolArrayT;
  CONSTANT RefTransition : IN     VitalEdgeSymbolType;
  CONSTANT ArcType       : IN     VitalMemoryArcType := CrossArc;
  CONSTANT NumBitsPerSubWord : IN INTEGER := 1;
  CONSTANT HeaderMsg     : IN     STRING := " ";
  CONSTANT XOn           : IN     BOOLEAN := TRUE;
  CONSTANT MsgOn         : IN     BOOLEAN := TRUE;
  CONSTANT MsgSeverity   : IN     SEVERITY_LEVEL := WARNING;
  CONSTANT MsgFormat     : IN     VitalMemoryMsgFormatType;
  --IR252 3/23/98
  CONSTANT EnableSetupOnTest : IN   BOOLEAN := TRUE; 
  CONSTANT EnableSetupOnRef  : IN   BOOLEAN := TRUE;
  CONSTANT EnableHoldOnRef   : IN   BOOLEAN := TRUE; 
  CONSTANT EnableHoldOnTest  : IN   BOOLEAN := TRUE 
) IS

  VARIABLE CheckInfo       : CheckInfoType;
  VARIABLE RefEdge         : VitalBoolArrayT(RefSignal'LENGTH-1 downto 0);
  VARIABLE TestEvent       : VitalBoolArrayT(TestSignal'LENGTH-1 downto 0);
  VARIABLE TestDly         : TIME;
  VARIABLE RefDly          : TIME;
  VARIABLE bias            : TIME;
  VARIABLE NumTestBits     : NATURAL := TestSignal'LENGTH;
  VARIABLE NumRefBits      : NATURAL := RefSignal'LENGTH;
  VARIABLE NumChecks       : NATURAL;

  VARIABLE ViolationTest   : X01ArrayT(NumTestBits-1 downto 0);
  VARIABLE ViolationRef    : X01ArrayT(NumRefBits-1 downto 0);

  VARIABLE TestSignalNorm  : std_logic_vector(NumTestBits-1 downto 0)
                              := TestSignal;
  VARIABLE TestDelayNorm   : VitalDelayArraytype(NumTestBits-1 downto 0)
                              := TestDelay;
  VARIABLE RefSignalNorm   : std_logic_vector(NumRefBits-1 downto 0)
                              := RefSignal;
  VARIABLE RefDelayNorm    : VitalDelayArraytype(NumRefBits-1 downto 0)
                              := RefDelay;
  VARIABLE CheckEnNorm     : VitalBoolArrayT(NumRefBits-1 downto 0)
                              := CheckEnabled;
  VARIABLE SetupHighNorm   : VitalDelayArraytype(SetupHigh'LENGTH-1 downto 0)
                              := SetupHigh;
  VARIABLE SetupLowNorm    : VitalDelayArraytype(SetupLow'LENGTH-1 downto 0)
                              := SetupLow;
  VARIABLE HoldHighNorm    : VitalDelayArraytype(HoldHigh'LENGTH-1 downto 0)
                              := HoldHigh;
  VARIABLE HoldLowNorm     : VitalDelayArraytype(HoldLow'LENGTH-1 downto 0)
                              := HoldLow;

  VARIABLE RefBitLow       : NATURAL;
  VARIABLE RefBitHigh      : NATURAL;
  VARIABLE EnArrayIndex    : NATURAL;
  VARIABLE TimingArrayIndex: NATURAL;
BEGIN

  -- Initialization of working area. 
  IF (TimingData.NotFirstFlag = FALSE) THEN
    TimingData.TestLastA := NEW std_logic_vector(NumTestBits-1 downto 0);
    TimingData.TestTimeA := NEW VitalTimeArrayT(NumTestBits-1 downto 0);
    TimingData.RefTimeA  := NEW VitalTimeArrayT(NumRefBits-1 downto 0);
    TimingData.RefLastA  := NEW X01ArrayT(NumRefBits-1 downto 0);
    IF ArcType = CrossArc THEN
      NumChecks := RefSignal'LENGTH * TestSignal'LENGTH;
    ELSE
      NumChecks := TestSignal'LENGTH;
    END IF;
    TimingData.HoldEnA   := NEW VitalBoolArrayT(NumChecks-1 downto 0);
    TimingData.SetupEnA  := NEW VitalBoolArrayT(NumChecks-1 downto 0);

    FOR i IN TestSignalNorm'RANGE LOOP
      TimingData.TestLastA(i) := To_X01(TestSignalNorm(i));
    END LOOP;

    FOR i IN RefSignalNorm'RANGE LOOP
      TimingData.RefLastA(i) := To_X01(RefSignalNorm(i));
    END LOOP;
    TimingData.NotFirstFlag := TRUE;
  END IF;

  -- Detect reference edges and record the time of the last edge
  FOR i IN RefSignalNorm'RANGE LOOP
    RefEdge(i) := EdgeSymbolMatch(TimingData.RefLastA(i), 
                  To_X01(RefSignalNorm(i)), RefTransition);
    TimingData.RefLastA(i) := To_X01(RefSignalNorm(i));
    IF RefEdge(i) THEN
      TimingData.RefTimeA(i)  := NOW;
    END IF;
  END LOOP;

  -- Detect test (data) changes and record the time of the last change
  FOR i IN TestSignalNorm'RANGE LOOP
    TestEvent(i) := TimingData.TestLastA(i) /= To_X01Z(TestSignalNorm(i));
    TimingData.TestLastA(i) := To_X01Z(TestSignalNorm(i));
    IF TestEvent(i) THEN
      TimingData.TestTimeA(i) := NOW;
    END IF;
  END LOOP;

  FOR i IN ViolationTest'RANGE LOOP
    ViolationTest(i) := '0'; 
  END LOOP;
  FOR i IN ViolationRef'RANGE LOOP
    ViolationRef(i) := '0'; 
  END LOOP;

  FOR i IN TestSignalNorm'RANGE LOOP
    IF (ArcType = CrossArc) THEN
      FOR j IN RefSignalNorm'RANGE LOOP
        IF (TestEvent(i)) THEN
          --TimingData.SetupEnA(i*NumRefBits+j) := TRUE;
          --IR252
          TimingData.SetupEnA(i*NumRefBits+j) := EnableSetupOnTest;
          TimingData.HoldEnA(i*NumRefBits+j) 
               := TimingData.HoldEnA(i*NumRefBits+j) AND EnableHoldOnTest;
        END IF;
        IF (RefEdge(j)) THEN
          --TimingData.HoldEnA(i*NumRefBits+j) := TRUE;
          --IR252 
          TimingData.HoldEnA(i*NumRefBits+j) := EnableHoldOnRef; 
          TimingData.SetupEnA(i*NumRefBits+j) 
               := TimingData.SetupEnA(i*NumRefBits+j) AND EnableSetupOnRef;
        END IF;
      END LOOP;
      RefBitLow := 0;
      RefBitHigh := NumRefBits-1;
      TimingArrayIndex := i;
    ELSE
      IF (ArcType = SubwordArc) THEN
        RefBitLow  := i / NumBitsPerSubWord;
        TimingArrayIndex := i + NumTestBits * RefBitLow;
      ELSE
        RefBitLow  := i;
        TimingArrayIndex := i;
      END IF;
      RefBitHigh := RefBitLow;
      IF (TestEvent(i)) THEN
        --TimingData.SetupEnA(i) := TRUE;
        --IR252
        TimingData.SetupEnA(i) := EnableSetupOnTest;
        TimingData.HoldEnA(i) := TimingData.HoldEnA(i) AND EnableHoldOnTest;
      END IF;
      IF (RefEdge(RefBitLow)) THEN
        --TimingData.HoldEnA(i) := TRUE;
        --IR252
        TimingData.HoldEnA(i) := EnableHoldOnRef;
        TimingData.SetupEnA(i) := TimingData.SetupEnA(i) AND EnableSetupOnRef;
      END IF;
    END IF;

    EnArrayIndex := i;
    FOR j IN RefBitLow to RefBitHigh LOOP
      IF (CheckEnNorm(j)) THEN
        TestDly := Maximum(0 ns, TestDelayNorm(i));
        RefDly  := Maximum(0 ns, RefDelayNorm(j));

        InternalTimingCheck (
          TestSignal   => TestSignalNorm(i),
          RefSignal    => RefSignalNorm(j),
          TestDelay    => TestDly,
          RefDelay     => RefDly,
          SetupHigh    => SetupHighNorm(TimingArrayIndex),
          SetupLow     => SetupLowNorm(TimingArrayIndex),
          HoldHigh     => HoldHighNorm(TimingArrayIndex),
          HoldLow      => HoldLowNorm(TimingArrayIndex),
          RefTime      => TimingData.RefTimeA(j),
          RefEdge      => RefEdge(j),
          TestTime     => TimingData.TestTimeA(i),
          TestEvent    => TestEvent(i),
          SetupEn      => TimingData.SetupEnA(EnArrayIndex),
          HoldEn       => TimingData.HoldEnA(EnArrayIndex),
          CheckInfo    => CheckInfo,
          MsgOn        => MsgOn 
        );

        -- Report any detected violations and set return violation flag
        IF (CheckInfo.Violation) THEN
          IF (MsgOn) THEN
            VitalMemoryReportViolation (TestSignalName, RefSignalName, i, j,
                    TestSignal, RefSignal, HeaderMsg, CheckInfo, 
                    MsgFormat, MsgSeverity );
          END IF;

          IF (XOn) THEN
            ViolationTest(i) := 'X'; 
            ViolationRef(j) := 'X'; 
          END IF;
        END IF;
      END IF;

      TimingArrayIndex := TimingArrayIndex + NumRefBits;
      EnArrayIndex    := EnArrayIndex + NumRefBits;
    END LOOP;
  END LOOP;

  IF (ArcType = CrossArc) THEN
    Violation := ViolationRef;
  ELSE
    IF (Violation'LENGTH = ViolationRef'LENGTH) THEN
      Violation := ViolationRef;
    ELSE
      Violation := ViolationTest;
    END IF;
  END IF;

END VitalMemorySetupHoldCheck;

-- ----------------------------------------------------------------------------
-- scalar violations not needed 
-- ----------------------------------------------------------------------------
PROCEDURE VitalMemorySetupHoldCheck (
  VARIABLE Violation     : OUT    X01;
  VARIABLE TimingData    : INOUT  VitalMemoryTimingDataType;
  SIGNAL   TestSignal    : IN     std_logic_vector;
  CONSTANT TestSignalName: IN     STRING := "";
  CONSTANT TestDelay     : IN     VitalDelayArraytype;
  SIGNAL   RefSignal     : IN     std_ulogic;
  CONSTANT RefSignalName : IN     STRING := "";
  CONSTANT RefDelay      : IN     TIME := 0 ns;
  CONSTANT SetupHigh     : IN     VitalDelayArraytype;
  CONSTANT SetupLow      : IN     VitalDelayArraytype;
  CONSTANT HoldHigh      : IN     VitalDelayArraytype;
  CONSTANT HoldLow       : IN     VitalDelayArraytype;
  CONSTANT CheckEnabled  : IN     BOOLEAN := TRUE;
  CONSTANT RefTransition : IN     VitalEdgeSymbolType;
  CONSTANT HeaderMsg     : IN     STRING := " ";
  CONSTANT XOn           : IN     BOOLEAN := TRUE;
  CONSTANT MsgOn         : IN     BOOLEAN := TRUE;
  CONSTANT MsgSeverity   : IN     SEVERITY_LEVEL := WARNING;
  CONSTANT MsgFormat     : IN     VitalMemoryMsgFormatType;
  --IR252 3/23/98
  CONSTANT EnableSetupOnTest : IN   BOOLEAN := TRUE; 
  CONSTANT EnableSetupOnRef  : IN   BOOLEAN := TRUE; 
  CONSTANT EnableHoldOnRef   : IN   BOOLEAN := TRUE; 
  CONSTANT EnableHoldOnTest  : IN   BOOLEAN := TRUE  
) IS
  VARIABLE CheckInfo     : CheckInfoType;
  VARIABLE RefEdge       : BOOLEAN;
  VARIABLE TestEvent     : VitalBoolArrayT(TestSignal'RANGE);
  VARIABLE TestDly       : TIME;
  VARIABLE RefDly        : TIME := Maximum(0 ns, RefDelay);
  VARIABLE bias          : TIME;

BEGIN

  -- Initialization of working area. 
  IF (TimingData.NotFirstFlag = FALSE) THEN
    TimingData.TestLastA := NEW std_logic_vector(TestSignal'RANGE);
    TimingData.TestTimeA := NEW VitalTimeArrayT(TestSignal'RANGE);
    TimingData.HoldEnA   := NEW VitalBoolArrayT(TestSignal'RANGE);
    TimingData.SetupEnA  := NEW VitalBoolArrayT(TestSignal'RANGE);
    FOR i IN TestSignal'RANGE LOOP
      TimingData.TestLastA(i) := To_X01(TestSignal(i));
    END LOOP;
    TimingData.RefLast := To_X01(RefSignal);
    TimingData.NotFirstFlag := TRUE;
  END IF;

  -- Detect reference edges and record the time of the last edge
  RefEdge := EdgeSymbolMatch(TimingData.RefLast, To_X01(RefSignal),
               RefTransition);
  TimingData.RefLast := To_X01(RefSignal);
  IF (RefEdge) THEN
    TimingData.RefTime     := NOW;
    --TimingData.HoldEnA.all := (TestSignal'RANGE=>TRUE);
    --IR252 3/23/98
    FOR i IN TestSignal'RANGE LOOP
      TimingData.SetupEnA(i) 
        := TimingData.SetupEnA(i) AND EnableSetupOnRef; 
      TimingData.HoldEnA(i)  := EnableHoldOnRef; 
    END LOOP;
  END IF;

  -- Detect test (data) changes and record the time of the last change
  FOR i IN TestSignal'RANGE LOOP
    TestEvent(i) := TimingData.TestLastA(i) /= To_X01Z(TestSignal(i));
    TimingData.TestLastA(i) := To_X01Z(TestSignal(i));
    IF TestEvent(i) THEN
      TimingData.SetupEnA(i)  := EnableSetupOnTest ; --IR252 3/23/98
      TimingData.HoldEnA(i) := TimingData.HoldEnA(i) AND EnableHoldOnTest ;
                                                  --IR252 3/23/98
      TimingData.TestTimeA(i) := NOW;
      --TimingData.SetupEnA(i)  := TRUE;
      TimingData.TestTime := NOW;
    END IF;
  END LOOP;

  Violation := '0';
  FOR i IN TestSignal'RANGE LOOP
    IF (CheckEnabled) THEN
      TestDly := Maximum(0 ns, TestDelay(i));
      InternalTimingCheck (
        TestSignal   => TestSignal(i),
        RefSignal    => RefSignal,
        TestDelay    => TestDly,
        RefDelay     => RefDly,
        SetupHigh    => SetupHigh(i),
        SetupLow     => SetupLow(i),
        HoldHigh     => HoldHigh(i),
        HoldLow      => HoldLow(i),
        RefTime      => TimingData.RefTime,
        RefEdge      => RefEdge,
        TestTime     => TimingData.TestTimeA(i),
        TestEvent    => TestEvent(i),
        SetupEn      => TimingData.SetupEnA(i),
        HoldEn       => TimingData.HoldEnA(i),
        CheckInfo    => CheckInfo,
        MsgOn        => MsgOn 
      );

      -- Report any detected violations and set return violation flag
      IF CheckInfo.Violation THEN
        IF (MsgOn) THEN
          VitalMemoryReportViolation (TestSignalName, RefSignalName, i ,
                  HeaderMsg, CheckInfo, MsgFormat, MsgSeverity );
        END IF;
        IF (XOn) THEN
          Violation := 'X';
        END IF;
      END IF;
    END IF;
  END LOOP;

END VitalMemorySetupHoldCheck;

-- ----------------------------------------------------------------------------
PROCEDURE VitalMemorySetupHoldCheck (
  VARIABLE Violation     : OUT    X01;
  VARIABLE TimingData    : INOUT  VitalMemoryTimingDataType;            
  SIGNAL   TestSignal    : IN     std_logic_vector;
  CONSTANT TestSignalName: IN     STRING := "";
  CONSTANT TestDelay     : IN     VitalDelayArraytype;
  SIGNAL   RefSignal     : IN     std_logic_vector;
  CONSTANT RefSignalName : IN     STRING := "";
  CONSTANT RefDelay      : IN     VitalDelayArraytype;
  CONSTANT SetupHigh     : IN     VitalDelayArraytype;
  CONSTANT SetupLow      : IN     VitalDelayArraytype;
  CONSTANT HoldHigh      : IN     VitalDelayArraytype;
  CONSTANT HoldLow       : IN     VitalDelayArraytype;
  CONSTANT CheckEnabled  : IN     BOOLEAN := TRUE;
  CONSTANT RefTransition : IN     VitalEdgeSymbolType;
  CONSTANT HeaderMsg     : IN     STRING := " ";
  CONSTANT XOn           : IN     BOOLEAN := TRUE;
  CONSTANT MsgOn         : IN     BOOLEAN := TRUE;
  CONSTANT MsgSeverity   : IN     SEVERITY_LEVEL := WARNING;
  CONSTANT ArcType       : IN     VitalMemoryArcType := CrossArc;
  CONSTANT NumBitsPerSubWord : IN INTEGER := 1;
  CONSTANT MsgFormat     : IN     VitalMemoryMsgFormatType;
  --IR252 3/23/98
  CONSTANT EnableSetupOnTest : IN   BOOLEAN := TRUE; 
  CONSTANT EnableSetupOnRef  : IN   BOOLEAN := TRUE; 
  CONSTANT EnableHoldOnRef   : IN   BOOLEAN := TRUE;
  CONSTANT EnableHoldOnTest  : IN   BOOLEAN := TRUE 
) IS
  VARIABLE CheckInfo       : CheckInfoType;
  VARIABLE RefEdge         : VitalBoolArrayT(RefSignal'LENGTH-1 downto 0);
  VARIABLE TestEvent       : VitalBoolArrayT(TestSignal'LENGTH-1 downto 0);
  VARIABLE TestDly         : TIME;
  VARIABLE RefDly          : TIME;
  VARIABLE bias            : TIME;
  VARIABLE NumTestBits     : NATURAL := TestSignal'LENGTH;
  VARIABLE NumRefBits      : NATURAL := RefSignal'LENGTH;
  VARIABLE NumChecks       : NATURAL;

  VARIABLE TestSignalNorm  : std_logic_vector(NumTestBits-1 downto 0)
                              := TestSignal;
  VARIABLE TestDelayNorm   : VitalDelayArraytype(NumTestBits-1 downto 0)
                              := TestDelay;
  VARIABLE RefSignalNorm   : std_logic_vector(NumRefBits-1 downto 0)
                              := RefSignal;
  VARIABLE RefDelayNorm    : VitalDelayArraytype(NumRefBits-1 downto 0)
                              := RefDelay;
  VARIABLE SetupHighNorm   : VitalDelayArraytype(SetupHigh'LENGTH-1 downto 0)
                              := SetupHigh;
  VARIABLE SetupLowNorm    : VitalDelayArraytype(SetupLow'LENGTH-1 downto 0)
                              := SetupLow;
  VARIABLE HoldHighNorm    : VitalDelayArraytype(HoldHigh'LENGTH-1 downto 0)
                              := HoldHigh;
  VARIABLE HoldLowNorm     : VitalDelayArraytype(HoldLow'LENGTH-1 downto 0)
                              := HoldLow;

  VARIABLE RefBitLow       : NATURAL;
  VARIABLE RefBitHigh      : NATURAL;
  VARIABLE EnArrayIndex    : NATURAL;
  VARIABLE TimingArrayIndex: NATURAL;
BEGIN

  -- Initialization of working area. 
  IF (TimingData.NotFirstFlag = FALSE) THEN
    TimingData.TestLastA := NEW std_logic_vector(NumTestBits-1 downto 0);
    TimingData.TestTimeA := NEW VitalTimeArrayT(NumTestBits-1 downto 0);
    TimingData.RefTimeA  := NEW VitalTimeArrayT(NumRefBits-1 downto 0);
    TimingData.RefLastA  := NEW X01ArrayT(NumRefBits-1 downto 0);
    IF (ArcType = CrossArc) THEN
      NumChecks := RefSignal'LENGTH * TestSignal'LENGTH;
    ELSE
      NumChecks := TestSignal'LENGTH;
    END IF;
    TimingData.HoldEnA   := NEW VitalBoolArrayT(NumChecks-1 downto 0);
    TimingData.SetupEnA  := NEW VitalBoolArrayT(NumChecks-1 downto 0);

    FOR i IN TestSignalNorm'RANGE LOOP
      TimingData.TestLastA(i) := To_X01(TestSignalNorm(i));
    END LOOP;

    FOR i IN RefSignalNorm'RANGE LOOP
      TimingData.RefLastA(i) := To_X01(RefSignalNorm(i));
    END LOOP;
    TimingData.NotFirstFlag := TRUE;
  END IF;

  -- Detect reference edges and record the time of the last edge
  FOR i IN RefSignalNorm'RANGE LOOP
    RefEdge(i) := EdgeSymbolMatch(TimingData.RefLastA(i), 
                  To_X01(RefSignalNorm(i)), RefTransition);
    TimingData.RefLastA(i) := To_X01(RefSignalNorm(i));
    IF (RefEdge(i)) THEN
      TimingData.RefTimeA(i)  := NOW;
    END IF;
  END LOOP;

  -- Detect test (data) changes and record the time of the last change
  FOR i IN TestSignalNorm'RANGE LOOP
    TestEvent(i) := TimingData.TestLastA(i) /= To_X01Z(TestSignalNorm(i));
    TimingData.TestLastA(i) := To_X01Z(TestSignalNorm(i));
    IF (TestEvent(i)) THEN
      TimingData.TestTimeA(i) := NOW;
    END IF;
  END LOOP;

  FOR i IN TestSignalNorm'RANGE LOOP
    IF (ArcType = CrossArc) THEN
      FOR j IN RefSignalNorm'RANGE LOOP
        IF (TestEvent(i)) THEN
          --TimingData.SetupEnA(i*NumRefBits+j) := TRUE;
          --IR252
          TimingData.SetupEnA(i*NumRefBits+j) := EnableSetupOnTest;
          TimingData.HoldEnA(i*NumRefBits+j) 
                 := TimingData.HoldEnA(i*NumRefBits+j) AND EnableHoldOnTest;
        END IF;
        IF (RefEdge(j)) THEN
          --TimingData.HoldEnA(i*NumRefBits+j) := TRUE;
          --IR252 
          TimingData.HoldEnA(i*NumRefBits+j) := EnableHoldOnRef; 
          TimingData.SetupEnA(i*NumRefBits+j) 
                 := TimingData.SetupEnA(i*NumRefBits+j) AND EnableSetupOnRef;
        END IF;
      END LOOP;
      RefBitLow := 0;
      RefBitHigh := NumRefBits-1;
      TimingArrayIndex := i;
    ELSE
      IF (ArcType = SubwordArc) THEN
        RefBitLow  := i / NumBitsPerSubWord;
        TimingArrayIndex := i + NumTestBits * RefBitLow;
      ELSE
        RefBitLow  := i;
        TimingArrayIndex := i;
      END IF;
      RefBitHigh := RefBitLow;
      IF (TestEvent(i)) THEN
        --TimingData.SetupEnA(i) := TRUE;
        --IR252
        TimingData.SetupEnA(i) := EnableSetupOnTest;
        TimingData.HoldEnA(i) := TimingData.HoldEnA(i) AND EnableHoldOnTest;
      END IF;
      IF (RefEdge(RefBitLow)) THEN
        --TimingData.HoldEnA(i) := TRUE;
        --IR252
        TimingData.HoldEnA(i) := EnableHoldOnRef;
        TimingData.SetupEnA(i) := TimingData.SetupEnA(i) AND EnableSetupOnRef;
      END IF;
    END IF;

    EnArrayIndex := i;
    Violation := '0';
    FOR j IN RefBitLow to RefBitHigh LOOP

      IF (CheckEnabled) THEN
        TestDly := Maximum(0 ns, TestDelayNorm(i));
        RefDly  := Maximum(0 ns, RefDelayNorm(j));

        InternalTimingCheck (
          TestSignal   => TestSignalNorm(i),
          RefSignal    => RefSignalNorm(j),
          TestDelay    => TestDly,
          RefDelay     => RefDly,
          SetupHigh    => SetupHighNorm(TimingArrayIndex),
          SetupLow     => SetupLowNorm(TimingArrayIndex),
          HoldHigh     => HoldHighNorm(TimingArrayIndex),
          HoldLow      => HoldLowNorm(TimingArrayIndex),
          RefTime      => TimingData.RefTimeA(j),
          RefEdge      => RefEdge(j),
          TestTime     => TimingData.TestTimeA(i),
          TestEvent    => TestEvent(i),
          SetupEn      => TimingData.SetupEnA(EnArrayIndex),
          HoldEn       => TimingData.HoldEnA(EnArrayIndex),
          CheckInfo    => CheckInfo,
          MsgOn        => MsgOn 
        );

        -- Report any detected violations and set return violation flag
        IF (CheckInfo.Violation) THEN
          IF (MsgOn) THEN
            VitalMemoryReportViolation (TestSignalName, RefSignalName, i, j,
                    TestSignal, RefSignal, HeaderMsg, CheckInfo, 
                    MsgFormat, MsgSeverity );
          END IF;

          IF (XOn) THEN
            Violation := 'X'; 
          END IF;
        END IF;
      END IF;

      TimingArrayIndex := TimingArrayIndex + NumRefBits;
      EnArrayIndex    := EnArrayIndex + NumRefBits;

    END LOOP;
  END LOOP;

END VitalMemorySetupHoldCheck;

-- ----------------------------------------------------------------------------
PROCEDURE  VitalMemoryPeriodPulseCheck  (
  VARIABLE Violation      : OUT   X01; 
  VARIABLE PeriodData     : INOUT VitalPeriodDataArrayType;
  SIGNAL   TestSignal     : IN    std_logic_vector;
  CONSTANT TestSignalName : IN    STRING := "";
  CONSTANT TestDelay      : IN    VitalDelayArraytype;
  CONSTANT Period         : IN    VitalDelayArraytype;
  CONSTANT PulseWidthHigh : IN    VitalDelayArraytype;
  CONSTANT PulseWidthLow  : IN    VitalDelayArraytype;
  CONSTANT CheckEnabled   : IN    BOOLEAN := TRUE;
  CONSTANT HeaderMsg      : IN    STRING := " ";
  CONSTANT XOn            : IN    BOOLEAN := TRUE;
  CONSTANT MsgOn          : IN    BOOLEAN := TRUE;
  CONSTANT MsgSeverity    : IN    SEVERITY_LEVEL := WARNING;
  CONSTANT MsgFormat      : IN    VitalMemoryMsgFormatType
) IS
  VARIABLE TestDly   : VitalDelayType;
  VARIABLE CheckInfo : CheckInfoType;
  VARIABLE PeriodObs : VitalDelayType;
  VARIABLE PulseTest : BOOLEAN;
  VARIABLE PeriodTest: BOOLEAN;
  VARIABLE TestValue : X01;
BEGIN

    -- Initialize for no violation
    Violation := '0';  --MEM IR 402

  FOR i IN TestSignal'RANGE LOOP
    TestDly := Maximum(0 ns, TestDelay(i));
    TestValue := To_X01(TestSignal(i));

    IF (PeriodData(i).NotFirstFlag = FALSE) THEN
      PeriodData(i).Rise := -Maximum(Period(i), 
          Maximum(PulseWidthHigh(i),PulseWidthLow(i)));
      PeriodData(i).Fall := -Maximum(Period(i), 
          Maximum(PulseWidthHigh(i),PulseWidthLow(i)));
      PeriodData(i).Last := TestValue;
      PeriodData(i).NotFirstFlag := TRUE;
    END IF;

    -- Initialize for no violation
  --  Violation := '0'; --Mem IR 402

    -- No violation possible if no test signal change
    NEXT WHEN (PeriodData(i).Last = TestValue);

    -- record starting pulse times
    IF (EdgeSymbolMatch(PeriodData(i).Last, TestValue, 'P')) THEN
      -- Compute period times, then record the High Rise Time
      PeriodObs := NOW - PeriodData(i).Rise;
      PeriodData(i).Rise := NOW;
      PeriodTest := TRUE;
    ELSIF (EdgeSymbolMatch(PeriodData(i).Last, TestValue, 'N')) THEN
      -- Compute period times, then record the Low Fall Time
      PeriodObs := NOW - PeriodData(i).Fall;
      PeriodData(i).Fall := NOW;
      PeriodTest := TRUE;
    ELSE
      PeriodTest := FALSE;
    END IF;

    -- do checks on pulse ends
    IF (EdgeSymbolMatch(PeriodData(i).Last, TestValue, 'p')) THEN
      -- Compute pulse times
      CheckInfo.ObsTime := NOW - PeriodData(i).Fall;
      CheckInfo.ExpTime := PulseWidthLow(i);
      PulseTest := TRUE;
    ELSIF (EdgeSymbolMatch(PeriodData(i).Last, TestValue, 'n')) THEN
      -- Compute pulse times
      CheckInfo.ObsTime := NOW - PeriodData(i).Rise;
      CheckInfo.ExpTime := PulseWidthHigh(i);
      PulseTest := TRUE;
    ELSE
      PulseTest := FALSE;
    END IF;

    IF (PulseTest AND CheckEnabled) THEN
      -- Verify Pulse Width [ignore 1st edge]
      IF (CheckInfo.ObsTime < CheckInfo.ExpTime) THEN
        IF (XOn) THEN 
         Violation := 'X';
        END IF;
        IF (MsgOn) THEN
          CheckInfo.Violation := TRUE;
          CheckInfo.CheckKind := PulseWidCheck;
          CheckInfo.DetTime   := NOW - TestDly;
          CheckInfo.State     := PeriodData(i).Last;
          VitalMemoryReportViolation (TestSignalName, "", i, 
               HeaderMsg, CheckInfo, MsgFormat, MsgSeverity );
        END IF; -- MsgOn
      END IF;
    END IF;

    IF (PeriodTest AND CheckEnabled) THEN
      -- Verify the Period [ignore 1st edge]
      CheckInfo.ObsTime := PeriodObs;
      CheckInfo.ExpTime := Period(i);
      IF ( CheckInfo.ObsTime < CheckInfo.ExpTime ) THEN
        IF (XOn) THEN 
          Violation := 'X'; 
        END IF;
        IF (MsgOn) THEN
          CheckInfo.Violation := TRUE;
          CheckInfo.CheckKind := PeriodCheck;
          CheckInfo.DetTime   := NOW - TestDly;
          CheckInfo.State     := TestValue;
          VitalMemoryReportViolation (TestSignalName, "", i,
               HeaderMsg, CheckInfo, MsgFormat, MsgSeverity );
        END IF; -- MsgOn
      END IF;
    END IF;

    PeriodData(i).Last := TestValue;
  END LOOP;

END VitalMemoryPeriodPulseCheck;

-- ----------------------------------------------------------------------------
PROCEDURE  VitalMemoryPeriodPulseCheck  (
  VARIABLE Violation      : OUT   X01ArrayT; 
  VARIABLE PeriodData     : INOUT VitalPeriodDataArrayType; 
  SIGNAL   TestSignal     : IN    std_logic_vector;
  CONSTANT TestSignalName : IN    STRING := "";
  CONSTANT TestDelay      : IN    VitalDelayArraytype;
  CONSTANT Period         : IN    VitalDelayArraytype;
  CONSTANT PulseWidthHigh : IN    VitalDelayArraytype;
  CONSTANT PulseWidthLow  : IN    VitalDelayArraytype;
  CONSTANT CheckEnabled   : IN    BOOLEAN := TRUE;
  CONSTANT HeaderMsg      : IN    STRING := " ";
  CONSTANT XOn            : IN    BOOLEAN := TRUE;
  CONSTANT MsgOn          : IN    BOOLEAN := TRUE;
  CONSTANT MsgSeverity    : IN    SEVERITY_LEVEL := WARNING;
  CONSTANT MsgFormat      : IN    VitalMemoryMsgFormatType
)IS
  VARIABLE TestDly   : VitalDelayType;
  VARIABLE CheckInfo : CheckInfoType;
  VARIABLE PeriodObs : VitalDelayType;
  VARIABLE PulseTest : BOOLEAN;
  VARIABLE PeriodTest: BOOLEAN;
  VARIABLE TestValue : X01;
BEGIN

  FOR i IN TestSignal'RANGE LOOP
    TestDly := Maximum(0 ns, TestDelay(i));
    TestValue := To_X01(TestSignal(i));

    IF (PeriodData(i).NotFirstFlag = FALSE) THEN
      PeriodData(i).Rise := -Maximum(Period(i), 
          Maximum(PulseWidthHigh(i),PulseWidthLow(i)));
      PeriodData(i).Fall := -Maximum(Period(i), 
          Maximum(PulseWidthHigh(i),PulseWidthLow(i)));
      PeriodData(i).Last := TestValue;
      PeriodData(i).NotFirstFlag := TRUE;
    END IF;

    -- Initialize for no violation
    Violation(i) := '0';

    -- No violation possible if no test signal change
    NEXT WHEN (PeriodData(i).Last = TestValue);

    -- record starting pulse times
    IF (EdgeSymbolMatch(PeriodData(i).Last, TestValue, 'P')) THEN
      -- Compute period times, then record the High Rise Time
      PeriodObs := NOW - PeriodData(i).Rise;
      PeriodData(i).Rise := NOW;
      PeriodTest := TRUE;
    ELSIF (EdgeSymbolMatch(PeriodData(i).Last, TestValue, 'N')) THEN
      -- Compute period times, then record the Low Fall Time
      PeriodObs := NOW - PeriodData(i).Fall;
      PeriodData(i).Fall := NOW;
      PeriodTest := TRUE;
    ELSE
      PeriodTest := FALSE;
    END IF;

    -- do checks on pulse ends
    IF (EdgeSymbolMatch(PeriodData(i).Last, TestValue, 'p')) THEN
      -- Compute pulse times
      CheckInfo.ObsTime := NOW - PeriodData(i).Fall;
      CheckInfo.ExpTime := PulseWidthLow(i);
      PulseTest := TRUE;
    ELSIF (EdgeSymbolMatch(PeriodData(i).Last, TestValue, 'n')) THEN
      -- Compute pulse times
      CheckInfo.ObsTime := NOW - PeriodData(i).Rise;
      CheckInfo.ExpTime := PulseWidthHigh(i);
      PulseTest := TRUE;
    ELSE
      PulseTest := FALSE;
    END IF;

    IF (PulseTest AND CheckEnabled) THEN
      -- Verify Pulse Width [ignore 1st edge]
      IF (CheckInfo.ObsTime < CheckInfo.ExpTime) THEN
        IF (XOn) THEN 
          Violation(i) := 'X';
        END IF;
        IF (MsgOn) THEN
          CheckInfo.Violation := TRUE;
          CheckInfo.CheckKind := PulseWidCheck;
          CheckInfo.DetTime   := NOW - TestDly;
          CheckInfo.State     := PeriodData(i).Last;
          VitalMemoryReportViolation (TestSignalName, "", i, 
              HeaderMsg, CheckInfo, MsgFormat, MsgSeverity );
        END IF; -- MsgOn
      END IF;
    END IF;

    IF (PeriodTest AND CheckEnabled) THEN
      -- Verify the Period [ignore 1st edge]
      CheckInfo.ObsTime := PeriodObs;
      CheckInfo.ExpTime := Period(i);
      IF ( CheckInfo.ObsTime < CheckInfo.ExpTime ) THEN
        IF (XOn) THEN 
          Violation(i) := 'X'; 
        END IF;
        IF (MsgOn) THEN
          CheckInfo.Violation := TRUE;
          CheckInfo.CheckKind := PeriodCheck;
          CheckInfo.DetTime   := NOW - TestDly;
          CheckInfo.State     := TestValue;
          VitalMemoryReportViolation (TestSignalName, "", i,
              HeaderMsg, CheckInfo, MsgFOrmat, MsgSeverity );
        END IF; -- MsgOn
      END IF;
    END IF;

    PeriodData(i).Last := TestValue;
  END LOOP;

END VitalMemoryPeriodPulseCheck;

-- ----------------------------------------------------------------------------
-- Functionality Section
-- ----------------------------------------------------------------------------

-- Look-up table.  Given an int, we can get the 4-bit bit_vector.
TYPE HexToBitvTableType IS  ARRAY (NATURAL RANGE <>) OF 
  std_logic_vector(3 DOWNTO 0) ;

CONSTANT HexToBitvTable : HexToBitvTableType (0 TO 15) :=
  (
    "0000", "0001", "0010", "0011",
    "0100", "0101", "0110", "0111",
    "1000", "1001", "1010", "1011",
    "1100", "1101", "1110", "1111"
  ) ;

-- ----------------------------------------------------------------------------
-- Misc Utilities Local Utilities
-- ----------------------------------------------------------------------------

-- ----------------------------------------------------------------------------
-- Procedure:   IsSpace
-- Parameters:  ch      -- input character
-- Description: Returns TRUE or FALSE depending on the input character
--              being white space or not.
-- ----------------------------------------------------------------------------
FUNCTION IsSpace (ch : character) 
RETURN boolean IS
BEGIN
  RETURN ((ch = ' ') OR (ch = CR) OR (ch = HT) OR (ch = NUL));
END IsSpace;

-- ----------------------------------------------------------------------------
-- Procedure:   LenOfString
-- Parameters:  Str         -- input string
-- Description: Returns the NATURAL length of the input string.
--              as terminated by the first NUL character.
-- ----------------------------------------------------------------------------
FUNCTION LenOfString (Str : STRING) 
RETURN NATURAL IS
  VARIABLE StrRight : NATURAL;
BEGIN
  StrRight := Str'RIGHT;
  FOR i IN Str'RANGE LOOP
    IF (Str(i) = NUL) THEN
      StrRight := i - 1;
      EXIT;
    END IF;
  END LOOP;
  RETURN (StrRight);
END LenOfString;

-- ----------------------------------------------------------------------------
-- Procedure:   HexToInt
-- Parameters:  Hex         -- input character or string
-- Description: Converts input character or string interpreted as a
--              hexadecimal representation to integer value.
-- ----------------------------------------------------------------------------
FUNCTION HexToInt(Hex : CHARACTER) RETURN INTEGER IS
  CONSTANT HexChars : STRING := "0123456789ABCDEFabcdef";
  CONSTANT XHiChar  : CHARACTER := 'X';
  CONSTANT XLoChar  : CHARACTER := 'x';
BEGIN
  IF (Hex = XLoChar OR Hex = XHiChar) THEN
    RETURN (23);
  END IF;
  FOR i IN 1 TO 16 LOOP
    IF(Hex = HexChars(i)) THEN
      RETURN (i-1);
    END IF;
  END LOOP;
  FOR i IN 17 TO 22 LOOP
    IF (Hex = HexChars(i)) THEN
      RETURN (i-7);
    END IF;
  END LOOP;
  ASSERT FALSE REPORT
    "Invalid character received by HexToInt function"
    SEVERITY WARNING;
  RETURN (0);
END HexToInt;

-- ----------------------------------------------------------------------------
FUNCTION HexToInt (Hex : STRING) RETURN INTEGER IS
  VARIABLE Value  : INTEGER := 0;
  VARIABLE Length : INTEGER;
BEGIN
  Length := LenOfString(hex);
  IF (Length > 8) THEN
    ASSERT FALSE REPORT
      "Invalid string length received by HexToInt function"
      SEVERITY WARNING;
  ELSE
    FOR i IN 1 TO Length LOOP
      Value := Value + HexToInt(Hex(i)) * 16 ** (Length - i);
    END LOOP;
  END IF;
  RETURN (Value);
END HexToInt;

-- ----------------------------------------------------------------------------
-- Procedure:   HexToBitv
-- Parameters:  Hex         -- Input hex string
-- Description: Converts input hex string to a std_logic_vector 
-- ----------------------------------------------------------------------------
FUNCTION HexToBitv( 
  Hex     : STRING
) RETURN std_logic_vector is
  VARIABLE   Index       : INTEGER := 0 ;
  VARIABLE   ValHexToInt : INTEGER ;
  VARIABLE   BitsPerHex  : INTEGER := 4 ; -- Denotes no. of bits per hex char.
  VARIABLE   HexLen      : NATURAL := (BitsPerHex * LenOfString(Hex)) ;
  VARIABLE   TableVal    : std_logic_vector(3 DOWNTO 0) ;  
  VARIABLE   Result      : std_logic_vector(HexLen-1 DOWNTO 0) ;
BEGIN
  --  Assign 4-bit wide bit vector to result directly from a look-up table.
  Index := 0 ;
  WHILE ( Index < HexLen )  LOOP
    ValHexToInt := HexToInt( Hex((HexLen - Index)/BitsPerHex ) );
    IF ( ValHexToInt = 23 ) THEN
      TableVal := "XXXX";
    ELSE
      -- Look up from the table.
      TableVal := HexToBitvTable( ValHexToInt ) ;
    END IF;
    -- Assign now.
    Result(Index+3 DOWNTO Index) := TableVal ;
    -- Get ready for next block of 4-bits.
    Index := Index + 4 ;
  END LOOP ;
  RETURN Result ;
END HexToBitv ;

-- ----------------------------------------------------------------------------
-- Procedure:   BinToBitv
-- Parameters:  Bin         -- Input bin string
-- Description: Converts input bin string to a std_logic_vector 
-- ----------------------------------------------------------------------------
FUNCTION BinToBitv( 
  Bin     : STRING
) RETURN std_logic_vector is
  VARIABLE   Index        : INTEGER := 0 ;
  VARIABLE   Length       : NATURAL := LenOfString(Bin);
  VARIABLE   BitVal       : std_ulogic;
  VARIABLE   Result       : std_logic_vector(Length-1 DOWNTO 0) ;  
BEGIN
  Index := 0 ;
  WHILE ( Index < Length )  LOOP
    IF (Bin(Length-Index) = '0') THEN
      BitVal := '0';
    ELSIF (Bin(Length-Index) = '1') THEN
      BitVal := '1';
    ELSE
      BitVal := 'X';
    END IF ;
    -- Assign now.
    Result(Index) := BitVal ;
    Index := Index + 1 ;
  END LOOP ;
  RETURN Result ;
END BinToBitv ;

-- ----------------------------------------------------------------------------
-- For Memory Table Modeling
-- ----------------------------------------------------------------------------

TYPE To_MemoryCharType IS ARRAY (VitalMemorySymbolType) OF CHARACTER;
CONSTANT To_MemoryChar : To_MemoryCharType :=
  ( '/', '\', 'P', 'N', 'r', 'f', 'p', 'n', 'R', 'F', '^', 'v',
    'E', 'A', 'D', '*', 'X', '0', '1', '-', 'B', 'Z', 'S',
    'g', 'u', 'i', 'G', 'U', 'I', 
    'w', 's',
    'c', 'l', 'd', 'e', 'C', 'L',
    'M', 'm', 't' );

TYPE ValidMemoryTableInputType IS ARRAY (VitalMemorySymbolType) OF BOOLEAN;
CONSTANT ValidMemoryTableInput : ValidMemoryTableInputType :=
    -- '/',   '\',   'P',   'N',   'r',   'f',
  (   TRUE,  TRUE,  TRUE,  TRUE,  TRUE,  TRUE,
    -- 'p',   'n',   'R',   'F',   '^',   'v',
      TRUE,  TRUE,  TRUE,  TRUE,  TRUE,  TRUE,
    -- 'E',   'A',    'D',  '*',
      TRUE,  TRUE,  TRUE,  TRUE,
    -- 'X',   '0',   '1',   '-',   'B',   'Z',
      TRUE,  TRUE,  TRUE,  TRUE,  TRUE, FALSE,
    -- 'S',
      TRUE, 
    -- 'g',   'u',   'i',   'G',   'U',   'I', 
      FALSE, FALSE, FALSE, FALSE, FALSE, FALSE,
    -- 'w',   's',
      FALSE, FALSE, 
    -- 'c',   'l',   'd',   'e',   'C',   'L',  
      FALSE, FALSE, FALSE, FALSE, FALSE, FALSE,
  -- 'M',   'm',   't' 
      FALSE, FALSE, FALSE);

TYPE MemoryTableMatchType IS ARRAY (X01,X01,VitalMemorySymbolType) OF BOOLEAN;
-- last value, present value, table symbol
CONSTANT MemoryTableMatch : MemoryTableMatchType :=  (
  ( -- X (lastvalue)
    -- /     \     P     N     r     f
    -- p     n     R     F     ^     v
    -- E     A     D     *
    -- X     0     1     -     B     Z     S
    -- g     u     i     G     U     I  
    -- w     s
    -- c     l     d     e,    C     L
    -- m     t 
    ( FALSE,FALSE,FALSE,FALSE,FALSE,FALSE,
      FALSE,FALSE,FALSE,FALSE,FALSE,FALSE,
      FALSE,FALSE,FALSE,FALSE,
      TRUE, FALSE,FALSE,TRUE, FALSE,FALSE,FALSE,
      FALSE,FALSE,FALSE,FALSE,FALSE,FALSE,
      FALSE,FALSE,
      FALSE,FALSE,FALSE,FALSE,FALSE,FALSE,
      FALSE,FALSE,FALSE),
    ( FALSE,FALSE,FALSE,TRUE, FALSE,FALSE,
      FALSE,FALSE,FALSE,TRUE, FALSE,TRUE,
      TRUE, FALSE,TRUE, TRUE,
      FALSE,TRUE, FALSE,TRUE, TRUE, FALSE,FALSE,
      FALSE,FALSE,FALSE,FALSE,FALSE,FALSE,
      FALSE,FALSE,
      FALSE,FALSE,FALSE,FALSE,FALSE,FALSE,
      FALSE,FALSE,FALSE),
    ( FALSE,FALSE,TRUE, FALSE,FALSE,FALSE,
      FALSE,FALSE,TRUE, FALSE,TRUE, FALSE,
      TRUE, TRUE, FALSE,TRUE,
      FALSE,FALSE,TRUE, TRUE, TRUE, FALSE,FALSE,
      FALSE,FALSE,FALSE,FALSE,FALSE,FALSE,
      FALSE,FALSE,
      FALSE,FALSE,FALSE,FALSE,FALSE,FALSE,
      FALSE,FALSE,FALSE)
    ),

    (-- 0 (lastvalue)
    -- /     \     P     N     r     f
    -- p     n     R     F     ^     v
    -- E     A     D     *
    -- X     0     1     -     B     Z     S
    -- g     u     i     G     U     I  
    -- w     s
    -- c     l     d     e,    C     L 
    -- m     t 
    ( FALSE,FALSE,FALSE,FALSE,TRUE, FALSE,
      TRUE, FALSE,TRUE, FALSE,FALSE,FALSE,
      FALSE,TRUE, FALSE,TRUE,
      TRUE, FALSE,FALSE,TRUE, FALSE,FALSE,FALSE,
      FALSE,FALSE,FALSE,FALSE,FALSE,FALSE,
      FALSE,FALSE,
      FALSE,FALSE,FALSE,FALSE,FALSE,FALSE,
      FALSE,FALSE,FALSE),
    ( FALSE,FALSE,FALSE,FALSE,FALSE,FALSE,
      FALSE,FALSE,FALSE,FALSE,FALSE,FALSE,
      FALSE,FALSE,FALSE,FALSE,
      FALSE,TRUE, FALSE,TRUE, TRUE, FALSE,TRUE,
      FALSE,FALSE,FALSE,FALSE,FALSE,FALSE,
      FALSE,FALSE,
      FALSE,FALSE,FALSE,FALSE,FALSE,FALSE,
      FALSE,FALSE,FALSE),
    ( TRUE, FALSE,TRUE, FALSE,FALSE,FALSE,
      TRUE, FALSE,TRUE, FALSE,FALSE,FALSE,
      FALSE,FALSE,FALSE,TRUE,
      FALSE,FALSE,TRUE, TRUE, TRUE, FALSE,FALSE,
      FALSE,FALSE,FALSE,FALSE,FALSE,FALSE,
      FALSE,FALSE,
      FALSE,FALSE,FALSE,FALSE,FALSE,FALSE,
      FALSE,FALSE,FALSE)
    ),

    (-- 1 (lastvalue)
    -- /     \     P     N     r     f
    -- p     n     R     F     ^     v
    -- E     A     D     *
    -- X     0     1     -     B     Z     S
    -- g     u     i     G     U     I  
    -- w     s
    -- c     l     d     e,    C     L
    -- m     t 
    ( FALSE,FALSE,FALSE,FALSE,FALSE,TRUE ,
      FALSE,TRUE, FALSE,TRUE, FALSE,FALSE,
      FALSE,FALSE,TRUE, TRUE,
      TRUE, FALSE,FALSE,TRUE, FALSE,FALSE,FALSE,
      FALSE,FALSE,FALSE,FALSE,FALSE,FALSE,
      FALSE,FALSE,
      FALSE,FALSE,FALSE,FALSE,FALSE,FALSE,
      FALSE,FALSE,FALSE),
    ( FALSE,TRUE, FALSE,TRUE, FALSE,FALSE,
      FALSE,TRUE, FALSE,TRUE, FALSE,FALSE,
      FALSE,FALSE,FALSE,TRUE,
      FALSE,TRUE, FALSE,TRUE, TRUE, FALSE,FALSE,
      FALSE,FALSE,FALSE,FALSE,FALSE,FALSE,
      FALSE,FALSE,
      FALSE,FALSE,FALSE,FALSE,FALSE,FALSE,
      FALSE,FALSE,FALSE),
    ( FALSE,FALSE,FALSE,FALSE,FALSE,FALSE,
      FALSE,FALSE,FALSE,FALSE,FALSE,FALSE,
      FALSE,FALSE,FALSE,FALSE,
      FALSE,FALSE,TRUE, TRUE, TRUE, FALSE,TRUE,
      FALSE,FALSE,FALSE,FALSE,FALSE,FALSE,
      FALSE,FALSE,
      FALSE,FALSE,FALSE,FALSE,FALSE,FALSE,
      FALSE,FALSE,FALSE)
    )
  );


-- ----------------------------------------------------------------------------
-- Error Message Types and Tables
-- ----------------------------------------------------------------------------

TYPE VitalMemoryErrorType IS (
  ErrGoodAddr,   -- 'g' Good address (no transition)
  ErrUnknAddr,   -- 'u' 'X' levels in address (no transition)
  ErrInvaAddr,   -- 'i' Invalid address (no transition)
  ErrGoodTrAddr, -- 'G' Good address (with transition)
  ErrUnknTrAddr, -- 'U' 'X' levels in address (with transition)
  ErrInvaTrAddr, -- 'I' Invalid address (with transition)
  ErrWrDatMem,   -- 'w' Writing data to memory
  ErrNoChgMem,   -- 's' Retaining previous memory contents
  ErrCrAllMem,   -- 'c' Corrupting entire memory with 'X'
  ErrCrWrdMem,   -- 'l' Corrupting a word in memory with 'X'
  ErrCrBitMem,   -- 'd' Corrupting a single bit in memory with 'X'
  ErrCrDatMem,   -- 'e' Corrupting a word with 'X' based on data in
  ErrCrAllSubMem,-- 'C' Corrupting a sub-word entire memory with 'X'
  ErrCrWrdSubMem,-- 'L' Corrupting a sub-word in memory with 'X'
  ErrCrBitSubMem,-- 'D' Corrupting a single bit of a memory sub-word with 'X'
  ErrCrDatSubMem,-- 'E' Corrupting a sub-word with 'X' based on data in
  ErrCrWrdOut,   -- 'l' Corrupting data out with 'X'
  ErrCrBitOut,   -- 'd' Corrupting a single bit of data out with 'X'
  ErrCrDatOut,   -- 'e' Corrupting data out with 'X' based on data in
  ErrCrWrdSubOut,-- 'L' Corrupting data out sub-word with 'X'
  ErrCrBitSubOut,-- 'D' Corrupting a single bit of data out sub-word with 'X'
  ErrCrDatSubOut,-- 'E' Corrupting data out sub-word with 'X' based on data in
  ErrImplOut,    -- 'M' Implicit read from memory to data out
  ErrReadOut,    -- 'm' Reading data from memory to data out
  ErrAssgOut,    -- 't' Transferring from data in to data out
  ErrAsgXOut,    -- 'X' Assigning unknown level to data out
  ErrAsg0Out,    -- '0' Assigning low level to data out
  ErrAsg1Out,    -- '1' Assigning high level to data out
  ErrAsgZOut,    -- 'Z' Assigning high impedence to data out
  ErrAsgSOut,    -- 'S' Keeping data out at steady value
  ErrAsgXMem,    -- 'X' Assigning unknown level to memory location
  ErrAsg0Mem,    -- '0' Assigning low level to memory location
  ErrAsg1Mem,    -- '1' Assigning high level to memory location
  ErrAsgZMem,    -- 'Z' Assigning high impedence to memory location
  ErrDefMemAct,  --     No memory table match, using default action
  ErrInitMem,    --     Initialize memory contents
  ErrMcpWrCont,  --     Memory cross port to same port write contention
  ErrMcpCpCont,  --     Memory cross port read/write data/memory contention
  ErrMcpCpRead,  --     Memory cross port read to same port
  ErrMcpRdWrCo,  --     Memory cross port read/write data only contention
  ErrMcpCpWrCont,--     Memory cross port to cross port write contention
  ErrUnknMemDo,  --     Unknown memory action
  ErrUnknDatDo,  --     Unknown data action
  ErrUnknSymbol, --     Illegal memory symbol
  ErrLdIlgArg,
  ErrLdAddrRng,
  ErrLdMemInfo,
  ErrLdFileEmpty,
  ErrPrintString
);

TYPE VitalMemoryErrorSeverityType IS
ARRAY (VitalMemoryErrorType) OF SEVERITY_LEVEL;
CONSTANT VitalMemoryErrorSeverity : 
  VitalMemoryErrorSeverityType := (
    ErrGoodAddr     => NOTE,
    ErrUnknAddr     => WARNING,
    ErrInvaAddr     => WARNING,
    ErrGoodTrAddr   => NOTE,
    ErrUnknTrAddr   => WARNING,
    ErrInvaTrAddr   => WARNING,
    ErrWrDatMem     => NOTE,
    ErrNoChgMem     => NOTE,
    ErrCrAllMem     => WARNING,
    ErrCrWrdMem     => WARNING,
    ErrCrBitMem     => WARNING,
    ErrCrDatMem     => WARNING,
    ErrCrAllSubMem  => WARNING,
    ErrCrWrdSubMem  => WARNING,
    ErrCrBitSubMem  => WARNING,
    ErrCrDatSubMem  => WARNING,
    ErrCrWrdOut     => WARNING,
    ErrCrBitOut     => WARNING,
    ErrCrDatOut     => WARNING,
    ErrCrWrdSubOut  => WARNING,
    ErrCrBitSubOut  => WARNING,
    ErrCrDatSubOut  => WARNING,
    ErrImplOut      => NOTE,
    ErrReadOut      => NOTE,
    ErrAssgOut      => NOTE,
    ErrAsgXOut      => NOTE,
    ErrAsg0Out      => NOTE,
    ErrAsg1Out      => NOTE,
    ErrAsgZOut      => NOTE,
    ErrAsgSOut      => NOTE,
    ErrAsgXMem      => NOTE,
    ErrAsg0Mem      => NOTE,
    ErrAsg1Mem      => NOTE,
    ErrAsgZMem      => NOTE,
    ErrDefMemAct    => NOTE,
    ErrInitMem      => NOTE,
    ErrMcpWrCont    => WARNING,
    ErrMcpCpCont    => WARNING,
    ErrMcpCpRead    => WARNING,
    ErrMcpRdWrCo    => WARNING,
    ErrMcpCpWrCont  => WARNING,
    ErrUnknMemDo    => ERROR,
    ErrUnknDatDo    => ERROR,
    ErrUnknSymbol   => ERROR,
    ErrLdIlgArg     => ERROR,
    ErrLdAddrRng    => WARNING,
    ErrLdMemInfo    => NOTE,
    ErrLdFileEmpty  => ERROR,
    ErrPrintString  => WARNING
  );

-- ----------------------------------------------------------------------------
CONSTANT MsgGoodAddr    : STRING
          := "Good address (no transition)";
CONSTANT MsgUnknAddr    : STRING
          := "Unknown address (no transition)";
CONSTANT MsgInvaAddr    : STRING
          := "Invalid address (no transition)";
CONSTANT MsgGoodTrAddr  : STRING
          := "Good address (with transition)"; 
CONSTANT MsgUnknTrAddr  : STRING
          := "Unknown address (with transition)"; 
CONSTANT MsgInvaTrAddr  : STRING
          := "Invalid address (with transition)"; 
CONSTANT MsgNoChgMem    : STRING
          := "Retaining previous memory contents";
CONSTANT MsgWrDatMem    : STRING
          := "Writing data to memory";
CONSTANT MsgCrAllMem    : STRING
          := "Corrupting entire memory with 'X'";
CONSTANT MsgCrWrdMem    : STRING
          := "Corrupting a word in memory with 'X'";
CONSTANT MsgCrBitMem    : STRING
          := "Corrupting a single bit in memory with 'X'";
CONSTANT MsgCrDatMem    : STRING
          := "Corrupting a word with 'X' based on data in";
CONSTANT MsgCrAllSubMem : STRING
          := "Corrupting a sub-word entire memory with 'X'";
CONSTANT MsgCrWrdSubMem : STRING
          := "Corrupting a sub-word in memory with 'X'";
CONSTANT MsgCrBitSubMem : STRING
          := "Corrupting a single bit of a sub-word with 'X'";
CONSTANT MsgCrDatSubMem : STRING
          := "Corrupting a sub-word with 'X' based on data in";
CONSTANT MsgCrWrdOut    : STRING
          := "Corrupting data out with 'X'";
CONSTANT MsgCrBitOut    : STRING
          := "Corrupting a single bit of data out with 'X'";
CONSTANT MsgCrDatOut    : STRING
          := "Corrupting data out with 'X' based on data in";
CONSTANT MsgCrWrdSubOut : STRING
          := "Corrupting data out sub-word with 'X'";
CONSTANT MsgCrBitSubOut : STRING
          := "Corrupting a single bit of data out sub-word with 'X'";
CONSTANT MsgCrDatSubOut : STRING
          := "Corrupting data out sub-word with 'X' based on data in";
CONSTANT MsgImplOut     : STRING
          := "Implicit read from memory to data out";
CONSTANT MsgReadOut     : STRING
          := "Reading data from memory to data out";
CONSTANT MsgAssgOut     : STRING
          := "Transferring from data in to data out";
CONSTANT MsgAsgXOut     : STRING
          := "Assigning unknown level to data out";
CONSTANT MsgAsg0Out     : STRING
          := "Assigning low level to data out";
CONSTANT MsgAsg1Out     : STRING
          := "Assigning high level to data out";
CONSTANT MsgAsgZOut     : STRING
          := "Assigning high impedance to data out";
CONSTANT MsgAsgSOut     : STRING
          := "Keeping data out at steady value";
CONSTANT MsgAsgXMem     : STRING
          := "Assigning unknown level to memory location";
CONSTANT MsgAsg0Mem     : STRING
          := "Assigning low level to memory location";
CONSTANT MsgAsg1Mem     : STRING
          := "Assigning high level to memory location";
CONSTANT MsgAsgZMem     : STRING
          := "Assigning high impedance to memory location";
CONSTANT MsgDefMemAct   : STRING
          := "No memory table match, using default action";
CONSTANT MsgInitMem     : STRING
          := "Initializing memory contents";
CONSTANT MsgMcpWrCont   : STRING
          := "Same port write contention";
CONSTANT MsgMcpCpCont   : STRING
          := "Cross port read/write data/memory contention";
CONSTANT MsgMcpCpRead   : STRING
          := "Cross port read to same port";
CONSTANT MsgMcpRdWrCo   : STRING
          := "Cross port read/write data only contention";
CONSTANT MsgMcpCpWrCont : STRING
          := "Cross port write contention";
CONSTANT MsgUnknMemDo   : STRING
          := "Unknown memory action";
CONSTANT MsgUnknDatDo   : STRING
          := "Unknown data action";
CONSTANT MsgUnknSymbol  : STRING
          := "Illegal memory symbol";

CONSTANT MsgLdIlgArg    : STRING
          := "Illegal bit arguments while loading memory.";
CONSTANT MsgLdMemInfo   : STRING
          := "Loading data from the file into memory.";
CONSTANT MsgLdAddrRng   : STRING
          := "Address out of range while loading memory.";
CONSTANT MsgLdFileEmpty : STRING
          := "Memory load file is empty.";
CONSTANT MsgPrintString : STRING
          := "";

CONSTANT MsgUnknown     : STRING
          := "Unknown error message.";

CONSTANT MsgVMT         : STRING
          := "VitalMemoryTable";
CONSTANT MsgVMV         : STRING
          := "VitalMemoryViolation";
CONSTANT MsgVDM         : STRING
          := "VitalDeclareMemory";
CONSTANT MsgVMCP        : STRING
          := "VitalMemoryCrossPorts";

-- ----------------------------------------------------------------------------
-- LOCAL Utilities
-- ----------------------------------------------------------------------------

-- ----------------------------------------------------------------------------
-- Procedure:   MemoryMessage
-- Parameters:  ErrorId     -- Input error code
-- Description: This function looks up the input error code and returns
--              the string value of the associated message.
-- ----------------------------------------------------------------------------

FUNCTION MemoryMessage (
  CONSTANT ErrorId : IN VitalMemoryErrorType
) RETURN STRING IS
BEGIN
  CASE ErrorId IS
    WHEN ErrGoodAddr      => RETURN MsgGoodAddr   ;
    WHEN ErrUnknAddr      => RETURN MsgUnknAddr   ;
    WHEN ErrInvaAddr      => RETURN MsgInvaAddr   ;
    WHEN ErrGoodTrAddr    => RETURN MsgGoodTrAddr ;
    WHEN ErrUnknTrAddr    => RETURN MsgUnknTrAddr ;
    WHEN ErrInvaTrAddr    => RETURN MsgInvaTrAddr ;
    WHEN ErrWrDatMem      => RETURN MsgWrDatMem   ;
    WHEN ErrNoChgMem      => RETURN MsgNoChgMem   ;
    WHEN ErrCrAllMem      => RETURN MsgCrAllMem   ;
    WHEN ErrCrWrdMem      => RETURN MsgCrWrdMem   ;
    WHEN ErrCrBitMem      => RETURN MsgCrBitMem   ;
    WHEN ErrCrDatMem      => RETURN MsgCrDatMem   ;
    WHEN ErrCrAllSubMem   => RETURN MsgCrAllSubMem;
    WHEN ErrCrWrdSubMem   => RETURN MsgCrWrdSubMem;
    WHEN ErrCrBitSubMem   => RETURN MsgCrBitSubMem;
    WHEN ErrCrDatSubMem   => RETURN MsgCrDatSubMem;
    WHEN ErrCrWrdOut      => RETURN MsgCrWrdOut   ;
    WHEN ErrCrBitOut      => RETURN MsgCrBitOut   ;
    WHEN ErrCrDatOut      => RETURN MsgCrDatOut   ;
    WHEN ErrCrWrdSubOut   => RETURN MsgCrWrdSubOut;
    WHEN ErrCrBitSubOut   => RETURN MsgCrBitSubOut;
    WHEN ErrCrDatSubOut   => RETURN MsgCrDatSubOut;
    WHEN ErrImplOut       => RETURN MsgImplOut    ;
    WHEN ErrReadOut       => RETURN MsgReadOut    ;
    WHEN ErrAssgOut       => RETURN MsgAssgOut    ;
    WHEN ErrAsgXOut       => RETURN MsgAsgXOut    ;
    WHEN ErrAsg0Out       => RETURN MsgAsg0Out    ;
    WHEN ErrAsg1Out       => RETURN MsgAsg1Out    ;
    WHEN ErrAsgZOut       => RETURN MsgAsgZOut    ;
    WHEN ErrAsgSOut       => RETURN MsgAsgSOut    ;
    WHEN ErrAsgXMem       => RETURN MsgAsgXMem    ;
    WHEN ErrAsg0Mem       => RETURN MsgAsg0Mem    ;
    WHEN ErrAsg1Mem       => RETURN MsgAsg1Mem    ;
    WHEN ErrAsgZMem       => RETURN MsgAsgZMem    ;
    WHEN ErrDefMemAct     => RETURN MsgDefMemAct  ;
    WHEN ErrInitMem       => RETURN MsgInitMem    ;
    WHEN ErrMcpWrCont     => RETURN MsgMcpWrCont  ;
    WHEN ErrMcpCpCont     => RETURN MsgMcpCpCont  ;
    WHEN ErrMcpCpRead     => RETURN MsgMcpCpRead  ;
    WHEN ErrMcpRdWrCo     => RETURN MsgMcpRdWrCo  ;
    WHEN ErrMcpCpWrCont   => RETURN MsgMcpCpWrCont;
    WHEN ErrUnknMemDo     => RETURN MsgUnknMemDo  ;
    WHEN ErrUnknDatDo     => RETURN MsgUnknDatDo  ;
    WHEN ErrUnknSymbol    => RETURN MsgUnknSymbol ;
    WHEN ErrLdIlgArg      => RETURN MsgLdIlgArg   ;
    WHEN ErrLdAddrRng     => RETURN MsgLdAddrRng  ;
    WHEN ErrLdMemInfo     => RETURN MsgLdMemInfo  ;
    WHEN ErrLdFileEmpty   => RETURN MsgLdFileEmpty;
    WHEN ErrPrintString   => RETURN MsgPrintString;
    WHEN OTHERS           => RETURN MsgUnknown    ;
  END CASE;
END;

-- ----------------------------------------------------------------------------
-- Procedure:   PrintMemoryMessage
-- Parameters:  Routine     -- String identifying the calling routine
--                  ErrorId     -- Input error code for message lookup
--                  Info        -- Output string or character
--                  InfoStr     -- Additional output string
--                  Info1       -- Additional output integer
--                  Info2       -- Additional output integer
--                  Info3       -- Additional output integer
-- Description: This procedure prints out a memory status message
--              given the input error id and other status information.
-- ----------------------------------------------------------------------------
PROCEDURE PrintMemoryMessage (
  CONSTANT Routine : IN STRING;
  CONSTANT ErrorId : IN VitalMemoryErrorType
) IS
BEGIN
  ASSERT FALSE
    REPORT Routine & ": " & MemoryMessage(ErrorId)
    SEVERITY VitalMemoryErrorSeverity(ErrorId);
END;

-- ----------------------------------------------------------------------------
PROCEDURE PrintMemoryMessage (
  CONSTANT Routine : IN STRING;
  CONSTANT ErrorId : IN VitalMemoryErrorType;
  CONSTANT Info    : IN STRING
) IS
BEGIN
  ASSERT FALSE
    REPORT Routine & ": " & MemoryMessage(ErrorId) & " " & Info
    SEVERITY VitalMemoryErrorSeverity(ErrorId);
END;

-- ----------------------------------------------------------------------------
PROCEDURE PrintMemoryMessage (
  CONSTANT Routine : IN STRING;
  CONSTANT ErrorId : IN VitalMemoryErrorType;
  CONSTANT Info1   : IN STRING;
  CONSTANT Info2   : IN STRING
) IS
BEGIN
  ASSERT FALSE
    REPORT Routine & ": " & MemoryMessage(ErrorId) & " " & Info1 & " " & Info2
    SEVERITY VitalMemoryErrorSeverity(ErrorId);
END;

-- ----------------------------------------------------------------------------
PROCEDURE PrintMemoryMessage (
  CONSTANT Routine : IN STRING;
  CONSTANT ErrorId : IN VitalMemoryErrorType;
  CONSTANT Info    : IN CHARACTER
) IS
BEGIN
  ASSERT FALSE
    REPORT Routine & ": " & MemoryMessage(ErrorId) & " " & Info
    SEVERITY VitalMemoryErrorSeverity(ErrorId);
END;

-- ----------------------------------------------------------------------------
PROCEDURE PrintMemoryMessage (
  CONSTANT Routine    : IN STRING;
  CONSTANT ErrorId    : IN VitalMemoryErrorType;
  CONSTANT InfoStr    : IN STRING;
  CONSTANT Info1      : IN NATURAL
) IS
  VARIABLE TmpStr         : STRING ( 1 TO 256 ) ;
  VARIABLE TmpInt         : INTEGER := 1;
BEGIN
  IntToStr(Info1,TmpStr,TmpInt);
  ASSERT FALSE
    REPORT Routine & ": " & MemoryMessage(ErrorId) & " " & InfoStr & " " & TmpStr
    SEVERITY VitalMemoryErrorSeverity(ErrorId);
END;

-- ----------------------------------------------------------------------------
PROCEDURE PrintMemoryMessage (
  CONSTANT Routine    : IN STRING;
  CONSTANT ErrorId    : IN VitalMemoryErrorType;
  CONSTANT InfoStr    : IN STRING;
  CONSTANT Info1      : IN NATURAL;
  CONSTANT Info2      : IN NATURAL
) IS
  VARIABLE TmpStr         : STRING ( 1 TO 256 ) ;
  VARIABLE TmpInt         : INTEGER := 1;
BEGIN
  IntToStr(Info1,TmpStr,TmpInt);
  IntToStr(Info2,TmpStr,TmpInt);
  ASSERT FALSE
    REPORT Routine & ": " & MemoryMessage(ErrorId) & " " & InfoStr & " " & TmpStr
    SEVERITY VitalMemoryErrorSeverity(ErrorId);
END;

-- ----------------------------------------------------------------------------
PROCEDURE PrintMemoryMessage (
  CONSTANT Routine    : IN STRING;
  CONSTANT ErrorId    : IN VitalMemoryErrorType;
  CONSTANT InfoStr    : IN STRING;
  CONSTANT Info1      : IN NATURAL;
  CONSTANT Info2      : IN NATURAL;
  CONSTANT Info3      : IN NATURAL
) IS
  VARIABLE TmpStr         : STRING ( 1 TO 256 ) ;
  VARIABLE TmpInt         : INTEGER := 1;
BEGIN
  IntToStr(Info1,TmpStr,TmpInt);
  IntToStr(Info2,TmpStr,TmpInt);
  IntToStr(Info3,TmpStr,TmpInt);
  ASSERT FALSE
    REPORT Routine & ": " & MemoryMessage(ErrorId) & " " & InfoStr & " " & TmpStr
    SEVERITY VitalMemoryErrorSeverity(ErrorId);
END;

-- ----------------------------------------------------------------------------
PROCEDURE PrintMemoryMessage (
  CONSTANT Routine    : IN STRING;
  CONSTANT Table      : IN VitalMemoryTableType;
  CONSTANT Index      : IN INTEGER;
  CONSTANT InfoStr    : IN STRING
) IS
  CONSTANT TableEntries   : INTEGER := Table'LENGTH(1);
  CONSTANT TableWidth     : INTEGER := Table'LENGTH(2);
  VARIABLE TmpStr         : STRING ( 1 TO 256 ) ;
  VARIABLE TmpInt         : INTEGER := 1;
BEGIN
  IF (Index < 0 AND Index > TableEntries-1) THEN 
    ASSERT FALSE
      REPORT Routine & ": Memory table search failure"
      SEVERITY ERROR;
  END IF;
  ColLoop:
  FOR i IN 0 TO TableWidth-1 LOOP
    IF (i >= 64) THEN
      TmpStr(TmpInt) := '.';
      TmpInt := TmpInt + 1;
      TmpStr(TmpInt) := '.';
      TmpInt := TmpInt + 1;
      TmpStr(TmpInt) := '.';
      TmpInt := TmpInt + 1;
      EXIT ColLoop;
    END IF;
    TmpStr(TmpInt) := ''';
    TmpInt := TmpInt + 1;
    TmpStr(TmpInt) := To_MemoryChar(Table(Index,i));
    TmpInt := TmpInt + 1;
    TmpStr(TmpInt) := ''';
    TmpInt := TmpInt + 1;
    IF (i < TableWidth-1) THEN
      TmpStr(TmpInt) := ',';
      TmpInt := TmpInt + 1;
    END IF;
  END LOOP;
  ASSERT FALSE
    REPORT Routine & ": Port=" & InfoStr & " TableRow=" & TmpStr
    SEVERITY NOTE;
END;

-- ----------------------------------------------------------------------------
-- Procedure:   DecodeAddress
-- Parameters:  Address        - Converted address.
--              AddrFlag       - Flag to indicte address match
--              MemoryData     - Information about memory characteristics
--              PrevAddressBus - Previous input address value
--              AddressBus     - Input address value.
-- Description: This procedure is used for transforming a valid
--              address value to an integer in order to access memory.
--              It performs address bound checking as well.
--              Sets Address to -1 for unknowns
--              Sets Address to -2 for out of range
-- ----------------------------------------------------------------------------

PROCEDURE DecodeAddress (
  VARIABLE Address        : INOUT INTEGER;
  VARIABLE AddrFlag       : INOUT VitalMemorySymbolType;
  VARIABLE MemoryData     : IN VitalMemoryDataType;
  CONSTANT PrevAddressBus : IN std_logic_vector;
  CONSTANT AddressBus     : IN std_logic_vector
) IS
  VARIABLE Power      : NATURAL;
  VARIABLE AddrUnkn   : BOOLEAN;
BEGIN
  Power := 0;
  AddrUnkn := FALSE;
  -- It is assumed that always Address'LEFT represents the Most significant bit.   
  FOR i IN AddressBus'RANGE LOOP
    Power := Power * 2;
    IF (AddressBus(i) /= '1' AND AddressBus(i) /= '0') THEN
      AddrUnkn := TRUE;
      Power := 0;
      EXIT;
    ELSIF (AddressBus(i) = '1') THEN
      Power := Power + 1;
    END IF;
  END LOOP;
  Address := Power;
  AddrFlag := 'g';
  IF (AddrUnkn) THEN
    AddrFlag := 'u';    -- unknown addr
    Address := -1;
  END IF;
  IF ( Power > (MemoryData.NoOfWords - 1)) THEN
    AddrFlag := 'i';    -- invalid addr
    Address := -2;
  END IF;
  IF (PrevAddressBus /= AddressBus) THEN
    CASE AddrFlag IS
      WHEN 'g'    => AddrFlag := 'G';
      WHEN 'u'    => AddrFlag := 'U';
      WHEN 'i'    => AddrFlag := 'I';
      WHEN OTHERS => 
        ASSERT FALSE REPORT
          "DecodeAddress: Internal error. [AddrFlag]=" 
          & To_MemoryChar(AddrFlag)
          SEVERITY ERROR;
    END CASE;
  END IF;
END DecodeAddress;

-- ----------------------------------------------------------------------------
-- Procedure:   DecodeData
-- Parameters:  DataFlag      - Flag to indicte data match
--              PrevDataInBus - Previous input data value
--              DataInBus     - Input data value.
--              HighBit       - High bit offset value.
--              LowBit        - Low  bit offset value.
-- Description: This procedure is used for interpreting the input data
--              as a data flag for subsequent table matching.
-- ----------------------------------------------------------------------------
PROCEDURE DecodeData (
  VARIABLE DataFlag       : INOUT VitalMemorySymbolType;
  CONSTANT PrevDataInBus  : IN std_logic_vector;
  CONSTANT DataInBus      : IN std_logic_vector;
  CONSTANT HighBit        : IN    NATURAL;
  CONSTANT LowBit         : IN    NATURAL
) IS
  VARIABLE DataUnkn   : BOOLEAN := FALSE;
BEGIN
  FOR i IN LowBit TO HighBit LOOP
    IF DataInBus(i) /= '1' AND DataInBus(i) /= '0' THEN
      DataUnkn := TRUE;
      EXIT;
    END IF;
  END LOOP;
  DataFlag := 'g';
  IF (DataUnkn) THEN
    DataFlag := 'u';    -- unknown addr
  END IF;
  IF (PrevDataInBus(HighBit DOWNTO LowBit) /= 
      DataInBus(HighBit DOWNTO LowBit)) THEN
    CASE DataFlag IS
      WHEN 'g'    => DataFlag := 'G';
      WHEN 'u'    => DataFlag := 'U';
      WHEN OTHERS => 
        ASSERT FALSE REPORT
          "DecodeData: Internal error. [DataFlag]=" 
          & To_MemoryChar(DataFlag)
          SEVERITY ERROR;
    END CASE;
  END IF;
END DecodeData;

-- ----------------------------------------------------------------------------
-- Procedure:   WriteMemory
-- Parameters:  MemoryPtr   - Pointer to the memory array.
--              DataInBus   - Input Data to be written.
--              Address     - Address of the memory location.
--              BitPosition - Position of bit in memory location.
--              HighBit     - High bit offset value.
--              LowBit      - Low  bit offset value.
-- Description: This procedure is used to write to a memory location
--              on a bit/byte/word basis. 
--              The high bit and low bit offset are used for byte write
--              operations.These parameters specify the data byte for write.
--              In the case of word write the complete memory word is used.
--              This procedure is overloaded for bit,byte and word write 
--              memory operations.The number of parameters may vary.
-- ----------------------------------------------------------------------------
PROCEDURE WriteMemory (
  VARIABLE MemoryPtr   : INOUT VitalMemoryDataType;
  CONSTANT DataInBus   : IN    std_logic_vector;
  CONSTANT Address     : IN    INTEGER;
  CONSTANT HighBit     : IN    NATURAL;
  CONSTANT LowBit      : IN    NATURAL
) IS
  VARIABLE TmpData : std_logic_vector(DataInBus'LENGTH - 1 DOWNTO 0);
BEGIN
  -- Address bound checking.
  IF ( Address < 0 OR Address > (MemoryPtr.NoOfWords - 1)) THEN
    PrintMemoryMessage ( "WriteMemory", ErrPrintString, 
      "Aborting write operation as address is out of range.") ;
    RETURN;
  END IF;
  TmpData  := To_UX01(DataInBus);
  FOR i in LowBit to HighBit LOOP
    MemoryPtr.MemoryArrayPtr(Address).all(i) := TmpData(i);
  END LOOP;
END WriteMemory;

-- ----------------------------------------------------------------------------
PROCEDURE WriteMemory (
  VARIABLE MemoryPtr      : INOUT VitalMemoryDataType;
  CONSTANT DataInBus      : IN    std_logic_vector;
  CONSTANT Address        : IN    INTEGER;
  CONSTANT BitPosition    : IN    NATURAL
) IS
  VARIABLE HighBit      : NATURAL;
  VARIABLE LowBit       : NATURAL;
BEGIN
  HighBit      := BitPosition;
  LowBit       := BitPosition;
  WriteMemory (MemoryPtr, DataInBus, Address, HighBit, LowBit);
END WriteMemory;

-- ----------------------------------------------------------------------------
PROCEDURE WriteMemory   (
  VARIABLE MemoryPtr   : INOUT VitalMemoryDataType;
  CONSTANT DataInBus   : IN    std_logic_vector;
  CONSTANT Address     : IN    INTEGER
) IS
  VARIABLE HighBit      : NATURAL;
  VARIABLE LowBit       : NATURAL;
BEGIN
  HighBit      := MemoryPtr.NoOfBitsPerWord - 1;
  LowBit       := 0;
  WriteMemory (MemoryPtr, DataInBus, Address, HighBit, LowBit);
END WriteMemory;

-- ----------------------------------------------------------------------------
-- Procedure:   ReadMemory
-- Parameters:  MemoryPtr    - Pointer to the memory array.
--              DataOut      - Output Data to be read in this.
--              Address      - Address of the memory location.
--              BitPosition  - Position of bit in memory location.
--              HighBit      - High bit offset value.
--              LowBit       - Low  bit offset value.
-- Description: This procedure is used to read from a memory location
--              on a bit/byte/word basis. 
--              The high bit and low bit offset are used for byte write
--              operations.These parameters specify the data byte for 
--              read.In the case of word write the complete memory word 
--              is used.This procedure is overloaded for bit,byte and 
--              word write memory operations.The number of parameters 
--              may vary.
-- ----------------------------------------------------------------------------
PROCEDURE ReadMemory (
  VARIABLE MemoryPtr  : INOUT VitalMemoryDataType;
  VARIABLE DataOut    : OUT   std_logic_vector;
  CONSTANT Address    : IN    INTEGER;
  CONSTANT HighBit    : IN    NATURAL;
  CONSTANT LowBit     : IN    NATURAL
) IS
  VARIABLE DataOutTmp : std_logic_vector(MemoryPtr.NoOfBitsPerWord-1 DOWNTO 0);
  VARIABLE length     : NATURAL := (HighBit - LowBit + 1);
BEGIN
  -- Address bound checking.
  IF ( Address > (MemoryPtr.NoOfWords - 1)) THEN
    PrintMemoryMessage ( 
      "ReadMemory",ErrInvaAddr,
      "[Address,NoOfWords]=",Address,MemoryPtr.NoOfWords 
    );
    FOR i in LowBit to HighBit LOOP
      DataOutTmp(i) := 'X';
    END LOOP;
  ELSE
    FOR i in LowBit to HighBit LOOP
      DataOutTmp(i) := MemoryPtr.MemoryArrayPtr (Address).all(i);
    END LOOP;
  END IF;
  DataOut :=  DataOutTmp;
END ReadMemory;

-- ----------------------------------------------------------------------------
PROCEDURE ReadMemory (
  VARIABLE MemoryPtr   : INOUT VitalMemoryDataType;
  VARIABLE DataOut     : OUT   std_logic_vector;
  CONSTANT Address     : IN    INTEGER;
  CONSTANT BitPosition : IN    NATURAL
) IS
  VARIABLE HighBit    : NATURAL;
  VARIABLE LowBit     : NATURAL;
BEGIN
  HighBit      := BitPosition;
  LowBit       := BitPosition;
  ReadMemory (MemoryPtr, DataOut, Address, HighBit, LowBit);
END ReadMemory;

-- ----------------------------------------------------------------------------
PROCEDURE ReadMemory (
  VARIABLE MemoryPtr  : INOUT VitalMemoryDataType;
  VARIABLE DataOut    : OUT   std_logic_vector;
  CONSTANT Address    : IN    INTEGER
) IS
  VARIABLE HighBit    : NATURAL;
  VARIABLE LowBit     : NATURAL;
BEGIN
  HighBit      := MemoryPtr.NoOfBitsPerWord - 1;
  LowBit       := 0;
  ReadMemory (MemoryPtr, DataOut, Address, HighBit, LowBit);
END ReadMemory;


-- ----------------------------------------------------------------------------
-- Procedure:   LoadMemory
-- Parameters:  MemoryPtr   - Pointer to the memory array.
--              FileName    - Name of the output file.
--              HighBit     - High bit offset value.
--              LowBit      - Low  bit offset value.
-- Description: This procedure is used to load the contents of the memory
--              from a specified input file.
--              The high bit and low bit offset are used so that same task
--              can be used for all bit/byte/word write operations.
--              In the case of a bit write RAM the HighBit and LowBit have
--              the same value.
--              This procedure is overloaded for word write operations.
-- ----------------------------------------------------------------------------
PROCEDURE LoadMemory (
  VARIABLE MemoryPtr  : INOUT VitalMemoryDataType;
  CONSTANT FileName   : IN    STRING;
  CONSTANT BinaryFile : IN BOOLEAN := FALSE
) IS
  FILE     Fptr      : TEXT OPEN read_mode IS FileName;
  VARIABLE OneLine   : LINE;
  VARIABLE Ignore    : CHARACTER;
  VARIABLE Index     : NATURAL  := 1;
  VARIABLE LineNo    : NATURAL  := 0;
  VARIABLE Address   : INTEGER  := 0;
  VARIABLE DataInBus : std_logic_vector(MemoryPtr.NoOfBitsPerWord-1  DOWNTO 0);
  VARIABLE AddrStr   : STRING(1 TO 80) ;    
  VARIABLE DataInStr : STRING(1 TO 255) ;    
BEGIN
  IF (ENDFILE(fptr)) THEN
    PrintMemoryMessage (MsgVDM, ErrLdFileEmpty, 
      "[FileName]="&FileName);
    RETURN;
  END IF ;
  PrintMemoryMessage (
    MsgVDM,ErrLdMemInfo, "[FileName]="&FileName
  );
  WHILE (NOT ENDFILE(fptr)) LOOP
    ReadLine(Fptr, OneLine);
    LineNo := LineNo + 1 ;
    --  First ignoring leading spaces.  
    WHILE (OneLine'LENGTH /= 0 and IsSpace(OneLine(1))) LOOP
      READ (OneLine, Ignore) ;     -- Ignoring the space character.
    END LOOP ;
    --  Note that, by now oneline has been "stripped" of its leading spaces.
    IF ( OneLine(1) = '@' ) THEN
      READ (OneLine, Ignore);  -- Ignore the '@' character and read the string.
      -- Now strip off spaces, if any, between '@' and Address string.
      WHILE (OneLine'LENGTH /= 0 and IsSpace(OneLine(1))) LOOP
        READ (OneLine, Ignore) ;     -- Ignoring the space character.
      END LOOP ;
      -- Now get the string which represents the address into string variable.
      Index := 1;
      WHILE (OneLine'LENGTH /= 0 AND (NOT(IsSpace(OneLine(1))))) LOOP
        READ(OneLine, AddrStr(Index));
        Index := Index + 1;
      END LOOP ;
      AddrStr(Index) := NUL;
      -- Now convert the hex string into a hex integer
      Address := HexToInt(AddrStr) ;
    ELSE
      IF ( LineNo /= 1 ) THEN
        Address := Address + 1;
      END IF;
    END IF ;
    IF ( Address > (MemoryPtr.NoOfWords - 1) ) THEN
      PrintMemoryMessage (MsgVDM, ErrLdAddrRng, 
      "[Address,lineno]=", Address, LineNo) ;
      EXIT ;
    END IF;
    -- Now strip off spaces, between Address string and DataInBus string.
    WHILE (OneLine'LENGTH /= 0 AND IsSpace(OneLine(1))) LOOP
      READ (OneLine, Ignore) ;     -- Ignoring the space character.
    END LOOP ;
    Index := 1;
    WHILE (OneLine'LENGTH /= 0 AND (NOT(IsSpace(OneLine(1))))) LOOP
      READ(OneLine, DataInStr(Index));
      Index := Index + 1;
    END LOOP ;
    DataInStr(Index) := NUL;
    IF (BinaryFile) THEN
      DataInBus := BinToBitv (DataInStr);
    ELSE
      DataInBus := HexToBitv (DataInStr);
    END IF ;
    WriteMemory (MemoryPtr, DataInBus, Address);
  END LOOP ;
END LoadMemory;

-- ----------------------------------------------------------------------------
-- Procedure:   MemoryMatch
-- Parameters:  Symbol       - Symbol from memory table
--              TestFlag     - Interpreted data or address symbol
--              In2          - input from VitalMemoryTable procedure
--                             to memory table
--              In2LastValue - Previous value of input
--              Err          - TRUE if symbol is not a valid input symbol
--              ReturnValue  - TRUE if match occurred
-- Description: This procedure sets ReturnValue to true if in2 matches
--              symbol (from the memory table).  If symbol is an edge
--              value edge is set to true and in2 and in2LastValue are
--              checked against symbol.  Err is set to true if symbol
--              is an invalid value for the input portion of the memory
--              table.
-- ----------------------------------------------------------------------------
PROCEDURE MemoryMatch (
  CONSTANT Symbol       : IN VitalMemorySymbolType;
  CONSTANT In2          : IN std_ulogic;
  CONSTANT In2LastValue : IN std_ulogic;
  VARIABLE Err          : OUT BOOLEAN;
  VARIABLE ReturnValue  : OUT BOOLEAN
) IS
BEGIN
  IF (NOT ValidMemoryTableInput(Symbol) ) THEN
    PrintMemoryMessage(MsgVMT,ErrUnknSymbol,To_MemoryChar(Symbol));
    Err := TRUE;
    ReturnValue := FALSE;
  ELSE
    ReturnValue := MemoryTableMatch(To_X01(In2LastValue), To_X01(In2), Symbol);
    Err := FALSE;
  END IF;
END;

-- ----------------------------------------------------------------------------
PROCEDURE MemoryMatch (
  CONSTANT Symbol       : IN VitalMemorySymbolType;
  CONSTANT TestFlag     : IN VitalMemorySymbolType;
  VARIABLE Err          : OUT BOOLEAN;
  VARIABLE ReturnValue  : OUT BOOLEAN
) IS
BEGIN
  Err := FALSE;
  ReturnValue := FALSE;
  CASE Symbol IS
    WHEN 'g'|'u'|'i'|'G'|'U'|'I'|'-'|'*'|'S' =>
      IF (Symbol = TestFlag) THEN
        ReturnValue := TRUE;
      ELSE
        CASE Symbol IS
          WHEN '-'    =>
            ReturnValue := TRUE;
            Err := FALSE;
          WHEN '*'    =>
            IF (TestFlag = 'G' OR 
                TestFlag = 'U' OR 
                TestFlag = 'I') THEN
              ReturnValue := TRUE;
              Err := FALSE;
            END IF;
          WHEN 'S'    =>
            IF (TestFlag = 'g' OR 
                TestFlag = 'u' OR 
                TestFlag = 'i') THEN
                ReturnValue := TRUE;
              Err := FALSE;
            END IF;
          WHEN OTHERS =>
            ReturnValue := FALSE;
        END CASE;
      END IF;
    WHEN OTHERS =>
      Err := TRUE;
      RETURN;
  END CASE;
END;

-- ----------------------------------------------------------------------------
-- Procedure:   MemoryTableCorruptMask
-- Description: Compute memory and data corruption masks for memory table
-- ----------------------------------------------------------------------------
PROCEDURE MemoryTableCorruptMask (
  VARIABLE CorruptMask            : OUT std_logic_vector;
  CONSTANT Action                 : IN VitalMemorySymbolType;
  CONSTANT EnableIndex            : IN INTEGER;
  CONSTANT BitsPerWord            : IN INTEGER;
  CONSTANT BitsPerSubWord         : IN INTEGER;
  CONSTANT BitsPerEnable          : IN INTEGER
) IS
  VARIABLE CorruptMaskTmp : std_logic_vector (CorruptMask'RANGE)
                          := (OTHERS => '0');
  VARIABLE ViolFlAryPosn  : INTEGER;
  VARIABLE HighBit        : INTEGER;
  VARIABLE LowBit         : INTEGER;
BEGIN
  CASE (Action) IS
    WHEN 'c'|'l'|'e'  =>
      -- Corrupt whole word
      CorruptMaskTmp := (OTHERS => 'X');
      CorruptMask := CorruptMaskTmp;
      RETURN;
    WHEN 'd'|'C'|'L'|'D'|'E' =>
      -- Process corruption below
    WHEN OTHERS =>
      -- No data or memory corruption
      CorruptMaskTmp := (OTHERS => '0');
      CorruptMask := CorruptMaskTmp;
      RETURN;
  END CASE;
  IF (Action = 'd') THEN
    CorruptMaskTmp := (OTHERS => 'X');
    CorruptMask := CorruptMaskTmp;
    RETURN;
  END IF;
  -- Remaining are subword cases 'C', 'L', 'D', 'E'
  CorruptMaskTmp := (OTHERS => '0');
  LowBit := 0;
  HighBit := BitsPerSubWord-1;
  SubWordLoop:
  FOR i IN 0 TO BitsPerEnable-1 LOOP
    IF (i = EnableIndex) THEN
      FOR j IN HighBit TO LowBit LOOP
        CorruptMaskTmp(j) := 'X';
      END LOOP;
    END IF;
    -- Calculate HighBit and LowBit
    LowBit := LowBit + BitsPerSubWord;
    IF (LowBit > BitsPerWord) THEN
      LowBit := BitsPerWord;
    END IF;
    HighBit := LowBit + BitsPerSubWord;
    IF (HighBit > BitsPerWord) THEN
      HighBit := BitsPerWord;
    ELSE
      HighBit := HighBit - 1;
    END IF;
  END LOOP;
  CorruptMask := CorruptMaskTmp;
  RETURN;
END;

-- ----------------------------------------------------------------------------
PROCEDURE MemoryTableCorruptMask (
  VARIABLE CorruptMask            : OUT std_logic_vector;
  CONSTANT Action                 : IN VitalMemorySymbolType
) IS
  VARIABLE CorruptMaskTmp : std_logic_vector (0 TO CorruptMask'LENGTH-1)
                          := (OTHERS => '0');
  VARIABLE ViolFlAryPosn  : INTEGER;
  VARIABLE HighBit        : INTEGER;
  VARIABLE LowBit         : INTEGER;
BEGIN
  CASE (Action) IS
    WHEN 'c'|'l'|'d'|'e'|'C'|'L'|'D'|'E' =>
      -- Corrupt whole word
      CorruptMaskTmp := (OTHERS => 'X');
      CorruptMask := CorruptMaskTmp;
      RETURN;
    WHEN OTHERS =>
      -- No data or memory corruption
      CorruptMaskTmp := (OTHERS => '0');
      CorruptMask := CorruptMaskTmp;
      RETURN;
  END CASE;
  RETURN;
END;

-- ----------------------------------------------------------------------------
-- Procedure:   MemoryTableCorruptMask
-- Description: Compute memory and data corruption masks for violation table
-- ----------------------------------------------------------------------------
PROCEDURE ViolationTableCorruptMask (
  VARIABLE CorruptMask            : OUT std_logic_vector;
  CONSTANT Action                 : IN VitalMemorySymbolType;
  CONSTANT ViolationFlags         : IN std_logic_vector;
  CONSTANT ViolationFlagsArray    : IN std_logic_vector;
  CONSTANT ViolationSizesArray    : IN VitalMemoryViolFlagSizeType;
  CONSTANT ViolationTable         : IN VitalMemoryTableType;
  CONSTANT TableIndex             : IN INTEGER;
  CONSTANT BitsPerWord            : IN INTEGER;
  CONSTANT BitsPerSubWord         : IN INTEGER;
  CONSTANT BitsPerEnable          : IN INTEGER
) IS
  VARIABLE CorruptMaskTmp : std_logic_vector (CorruptMask'RANGE)
                          := (OTHERS => '0');
  VARIABLE ViolMaskTmp    : std_logic_vector (CorruptMask'RANGE)
                          := (OTHERS => '0');
  VARIABLE ViolFlAryPosn  : INTEGER;
  VARIABLE HighBit        : INTEGER;
  VARIABLE LowBit         : INTEGER;
  CONSTANT ViolFlagsSize  : INTEGER := ViolationFlags'LENGTH;
  CONSTANT ViolFlArySize  : INTEGER := ViolationFlagsArray'LENGTH;
  CONSTANT TableEntries   : INTEGER := ViolationTable'LENGTH(1);
  CONSTANT TableWidth     : INTEGER := ViolationTable'LENGTH(2);
  CONSTANT DatActionNdx   : INTEGER := TableWidth - 1;
  CONSTANT MemActionNdx   : INTEGER := TableWidth - 2;
BEGIN
  CASE (Action) IS
    WHEN 'c'|'l'|'e'  =>
      -- Corrupt whole word
      CorruptMaskTmp := (OTHERS => 'X');
      CorruptMask := CorruptMaskTmp;
      RETURN;
    WHEN 'd'|'C'|'L'|'D'|'E' =>
      -- Process corruption below
    WHEN OTHERS =>
      -- No data or memory corruption
      CorruptMaskTmp := (OTHERS => '0');
      CorruptMask := CorruptMaskTmp;
      RETURN;
  END CASE;
  RowLoop: -- Check each element of the ViolationFlags
  FOR j IN 0 TO ViolFlagsSize LOOP
    IF (j = ViolFlagsSize) THEN
      ViolFlAryPosn := 0;
      RowLoop2: -- Check relevant elements of the ViolationFlagsArray
      FOR k IN 0 TO MemActionNdx - ViolFlagsSize - 1 LOOP
        IF (ViolationTable(TableIndex, k + ViolFlagsSize) = 'X') THEN
          MaskLoop: -- Set the 'X' bits in the violation mask
          FOR m IN INTEGER RANGE 0 TO CorruptMask'LENGTH-1 LOOP
            IF (m <= ViolationSizesArray(k)-1) THEN
              ViolMaskTmp(m) := ViolMaskTmp(m) XOR 
              ViolationFlagsArray(ViolFlAryPosn+m);
            ELSE
              EXIT MaskLoop;
            END IF;
          END LOOP;
        END IF;
        ViolFlAryPosn := ViolFlAryPosn + ViolationSizesArray(k);
      END LOOP;
    ELSE
      IF (ViolationTable(TableIndex, j) = 'X') THEN
        ViolMaskTmp(0) := ViolMaskTmp(0) XOR ViolationFlags(j);
      END IF;
    END IF;
  END LOOP;
  IF (Action = 'd') THEN
    CorruptMask := ViolMaskTmp;
    RETURN;
  END IF;
  -- Remaining are subword cases 'C', 'L', 'D', 'E'
  CorruptMaskTmp := (OTHERS => '0');
  LowBit := 0;
  HighBit := BitsPerSubWord-1;
  SubWordLoop:
  FOR i IN 0 TO BitsPerEnable-1 LOOP
    IF (ViolMaskTmp(i) = 'X') THEN
      FOR j IN HighBit TO LowBit LOOP
        CorruptMaskTmp(j) := 'X';
      END LOOP;
    END IF;
    -- Calculate HighBit and LowBit
    LowBit := LowBit + BitsPerSubWord;
    IF (LowBit > BitsPerWord) THEN
      LowBit := BitsPerWord;
    END IF;
    HighBit := LowBit + BitsPerSubWord;
    IF (HighBit > BitsPerWord) THEN
      HighBit := BitsPerWord;
    ELSE
      HighBit := HighBit - 1;
    END IF;
  END LOOP;
  CorruptMask := CorruptMaskTmp;
  RETURN;
END;

-- ----------------------------------------------------------------------------
-- Procedure:   MemoryTableLookUp
-- Parameters:  MemoryAction  - Output memory action to be performed
--              DataAction    - Output data action to be performed
--              PrevControls  - Previous data in for edge detection
--              PrevEnableBus - Previous enables for edge detection
--              Controls      - Agregate of scalar control lines
--              EnableBus     - Concatenation of vector control lines
--              EnableIndex   - Current slice of vector control lines
--              AddrFlag      - Matching symbol from address decoding
--              DataFlag      - Matching symbol from data decoding
--              MemoryTable   - Input memory action table
--              PortName      - Port name string for messages
--              HeaderMsg     - Header string for messages
--              MsgOn         - Control message output
--
-- Description: This function is used to find the output of the
--              MemoryTable corresponding to a given set of inputs.
--
-- ----------------------------------------------------------------------------
PROCEDURE MemoryTableLookUp (
  VARIABLE MemoryAction       : OUT VitalMemorySymbolType;
  VARIABLE DataAction         : OUT VitalMemorySymbolType;
  VARIABLE MemoryCorruptMask  : OUT std_logic_vector;
  VARIABLE DataCorruptMask    : OUT std_logic_vector;
  CONSTANT PrevControls       : IN std_logic_vector;
  CONSTANT Controls           : IN std_logic_vector;
  CONSTANT AddrFlag           : IN VitalMemorySymbolType;
  CONSTANT DataFlag           : IN VitalMemorySymbolType;
  CONSTANT MemoryTable        : IN VitalMemoryTableType;
  CONSTANT PortName           : IN STRING := "";
  CONSTANT HeaderMsg          : IN STRING := "";
  CONSTANT MsgOn              : IN BOOLEAN := TRUE
) IS
  CONSTANT ControlsSize   : INTEGER := Controls'LENGTH;
  CONSTANT TableEntries   : INTEGER := MemoryTable'LENGTH(1);
  CONSTANT TableWidth     : INTEGER := MemoryTable'LENGTH(2);
  CONSTANT DatActionNdx   : INTEGER := TableWidth - 1;
  CONSTANT MemActionNdx   : INTEGER := TableWidth - 2;
  CONSTANT DataInBusNdx   : INTEGER := TableWidth - 3;
  CONSTANT AddressBusNdx  : INTEGER := TableWidth - 4;
  VARIABLE AddrFlagTable  : VitalMemorySymbolType;
  VARIABLE Match  : BOOLEAN;
  VARIABLE Err    : BOOLEAN := FALSE;
  VARIABLE TableAlias     : VitalMemoryTableType(
                              0 TO TableEntries - 1,
                              0 TO TableWidth - 1)
                              := MemoryTable;
BEGIN
  ColLoop: -- Compare each entry in the table
  FOR i IN TableAlias'RANGE(1) LOOP
    RowLoop: -- Check each element of the Controls
    FOR j IN 0 TO ControlsSize LOOP
      IF (j = ControlsSize) THEN
        -- a match occurred, now check AddrFlag, DataFlag
        MemoryMatch(TableAlias(i,AddressBusNdx),AddrFlag,Err,Match);
        IF (Match) THEN
          MemoryMatch(TableAlias(i,DataInBusNdx),DataFlag,Err,Match);
          IF (Match) THEN
            MemoryTableCorruptMask (
              CorruptMask     => MemoryCorruptMask    ,
              Action          => TableAlias(i, MemActionNdx)
            );
            MemoryTableCorruptMask (
              CorruptMask     => DataCorruptMask      ,
              Action          => TableAlias(i, DatActionNdx)
            );
            -- get the return memory and data actions
            MemoryAction := TableAlias(i, MemActionNdx);
            DataAction   := TableAlias(i, DatActionNdx);
            -- DEBUG: The lines below report table search
            IF (MsgOn) THEN
              PrintMemoryMessage(MsgVMT,TableAlias,i,PortName);
            END IF;
            -- DEBUG: The lines above report table search
            RETURN;
          END IF;
        END IF;
      ELSE
        -- Match memory table inputs
        MemoryMatch (   TableAlias(i,j),
        Controls(j), PrevControls(j),
        Err, Match);
      END IF;
      EXIT RowLoop WHEN NOT(Match);
      EXIT ColLoop WHEN Err;
    END LOOP RowLoop;
  END LOOP ColLoop;
  -- no match found, return default action
  MemoryAction := 's';   -- no change to memory
  DataAction   := 'S';   -- no change to dataout
  IF (MsgOn) THEN
  PrintMemoryMessage(MsgVMT,ErrDefMemAct,HeaderMsg,PortName);
  END IF;
  RETURN;
END;

-- ----------------------------------------------------------------------------
PROCEDURE MemoryTableLookUp (
  VARIABLE MemoryAction       : OUT VitalMemorySymbolType;
  VARIABLE DataAction         : OUT VitalMemorySymbolType;
  VARIABLE MemoryCorruptMask  : OUT std_logic_vector;
  VARIABLE DataCorruptMask    : OUT std_logic_vector;
  CONSTANT PrevControls       : IN std_logic_vector;
  CONSTANT PrevEnableBus      : IN std_logic_vector;
  CONSTANT Controls           : IN std_logic_vector;
  CONSTANT EnableBus          : IN std_logic_vector;
  CONSTANT EnableIndex        : IN INTEGER;
  CONSTANT BitsPerWord        : IN INTEGER;
  CONSTANT BitsPerSubWord     : IN INTEGER;
  CONSTANT BitsPerEnable      : IN INTEGER;
  CONSTANT AddrFlag           : IN VitalMemorySymbolType;
  CONSTANT DataFlag           : IN VitalMemorySymbolType;
  CONSTANT MemoryTable        : IN VitalMemoryTableType;
  CONSTANT PortName           : IN STRING := "";
  CONSTANT HeaderMsg          : IN STRING := "";
  CONSTANT MsgOn              : IN BOOLEAN := TRUE
) IS
  CONSTANT ControlsSize   : INTEGER := Controls'LENGTH;
  CONSTANT TableEntries   : INTEGER := MemoryTable'LENGTH(1);
  CONSTANT TableWidth     : INTEGER := MemoryTable'LENGTH(2);
  CONSTANT DatActionNdx   : INTEGER := TableWidth - 1;
  CONSTANT MemActionNdx   : INTEGER := TableWidth - 2;
  CONSTANT DataInBusNdx   : INTEGER := TableWidth - 3;
  CONSTANT AddressBusNdx  : INTEGER := TableWidth - 4;
  VARIABLE AddrFlagTable  : VitalMemorySymbolType;
  VARIABLE Match  : BOOLEAN;
  VARIABLE Err    : BOOLEAN := FALSE;
  VARIABLE TableAlias     : VitalMemoryTableType(
                              0 TO TableEntries - 1,
                              0 TO TableWidth - 1)
                                := MemoryTable;
BEGIN
  ColLoop: -- Compare each entry in the table
  FOR i IN TableAlias'RANGE(1) LOOP
    RowLoop: -- Check each element of the Controls
    FOR j IN 0 TO ControlsSize LOOP
      IF (j = ControlsSize) THEN
        -- a match occurred, now check EnableBus, AddrFlag, DataFlag
        IF (EnableIndex >= 0) THEN
          RowLoop2: -- Check relevant elements of the EnableBus
          FOR k IN 0 TO AddressBusNdx - ControlsSize - 1 LOOP
            MemoryMatch (   TableAlias(i,k + ControlsSize),
                            EnableBus(k * BitsPerEnable + EnableIndex),
                            PrevEnableBus(k * BitsPerEnable + EnableIndex),
                            Err, Match);
            EXIT RowLoop2 WHEN NOT(Match);
          END LOOP;
        END IF;
        IF (Match) THEN
          MemoryMatch(TableAlias(i,AddressBusNdx),AddrFlag,Err,Match);
          IF (Match) THEN
            MemoryMatch(TableAlias(i,DataInBusNdx),DataFlag,Err,Match);
            IF (Match) THEN
              MemoryTableCorruptMask (
                CorruptMask     => MemoryCorruptMask    ,
                Action          => TableAlias(i, MemActionNdx),
                EnableIndex     => EnableIndex          ,
                BitsPerWord     => BitsPerWord          ,
                BitsPerSubWord  => BitsPerSubWord       ,
                BitsPerEnable   => BitsPerEnable
              );
              MemoryTableCorruptMask (
                CorruptMask     => DataCorruptMask      ,
                Action          => TableAlias(i, DatActionNdx),
                EnableIndex     => EnableIndex          ,
                BitsPerWord     => BitsPerWord          ,
                BitsPerSubWord  => BitsPerSubWord       ,
                BitsPerEnable   => BitsPerEnable
              );
              -- get the return memory and data actions
              MemoryAction := TableAlias(i, MemActionNdx);
              DataAction   := TableAlias(i, DatActionNdx);
              -- DEBUG: The lines below report table search
              IF (MsgOn) THEN
                PrintMemoryMessage(MsgVMT,TableAlias,i,PortName);
              END IF;
              -- DEBUG: The lines above report table search
              RETURN;
            END IF;
          END IF;
        END IF;
      ELSE
        -- Match memory table inputs
        MemoryMatch (   TableAlias(i,j),
                        Controls(j), PrevControls(j),
                        Err, Match);
      END IF;
      EXIT RowLoop WHEN NOT(Match);
      EXIT ColLoop WHEN Err;
    END LOOP RowLoop;
  END LOOP ColLoop;
  -- no match found, return default action
  MemoryAction := 's';   -- no change to memory
  DataAction   := 'S';   -- no change to dataout
  IF (MsgOn) THEN
    PrintMemoryMessage(MsgVMT,ErrDefMemAct,HeaderMsg,PortName);
  END IF;
  RETURN;
END;

-- ----------------------------------------------------------------------------
-- Procedure:   ViolationTableLookUp
-- Parameters:  MemoryAction  - Output memory action to be performed
--              DataAction    - Output data action to be performed
--              TimingDataArray   - This is currently not used (comment out) 
--              ViolationArray    - Aggregation of violation variables
--              ViolationTable    - Input memory violation table
--              PortName      - Port name string for messages
--              HeaderMsg     - Header string for messages
--              MsgOn         - Control message output
-- Description: This function is used to find the output of the
--              ViolationTable corresponding to a given set of inputs.
-- ----------------------------------------------------------------------------
PROCEDURE ViolationTableLookUp (
  VARIABLE MemoryAction           : OUT VitalMemorySymbolType;
  VARIABLE DataAction             : OUT VitalMemorySymbolType;
  VARIABLE MemoryCorruptMask      : OUT std_logic_vector;
  VARIABLE DataCorruptMask        : OUT std_logic_vector;
  CONSTANT ViolationFlags         : IN std_logic_vector;
  CONSTANT ViolationFlagsArray    : IN std_logic_vector;
  CONSTANT ViolationSizesArray    : IN VitalMemoryViolFlagSizeType;
  CONSTANT ViolationTable         : IN VitalMemoryTableType;
  CONSTANT BitsPerWord            : IN INTEGER;
  CONSTANT BitsPerSubWord         : IN INTEGER;
  CONSTANT BitsPerEnable          : IN INTEGER;
  CONSTANT PortName               : IN STRING := "";
  CONSTANT HeaderMsg              : IN STRING := "";
  CONSTANT MsgOn                  : IN BOOLEAN := TRUE
) IS
  CONSTANT ViolFlagsSize  : INTEGER := ViolationFlags'LENGTH;
  CONSTANT ViolFlArySize  : INTEGER := ViolationFlagsArray'LENGTH;
  VARIABLE ViolFlAryPosn  : INTEGER;
  VARIABLE ViolFlAryItem  : std_ulogic;
  CONSTANT ViolSzArySize  : INTEGER := ViolationSizesArray'LENGTH;
  CONSTANT TableEntries   : INTEGER := ViolationTable'LENGTH(1);
  CONSTANT TableWidth     : INTEGER := ViolationTable'LENGTH(2);
  CONSTANT DatActionNdx   : INTEGER := TableWidth - 1;
  CONSTANT MemActionNdx   : INTEGER := TableWidth - 2;
  VARIABLE HighBit        : NATURAL := 0;
  VARIABLE LowBit         : NATURAL := 0;
  VARIABLE Match  : BOOLEAN;
  VARIABLE Err    : BOOLEAN := FALSE;
  VARIABLE TableAlias     : VitalMemoryTableType(
                              0 TO TableEntries - 1,
                              0 TO TableWidth - 1)
                                := ViolationTable;
BEGIN
  ColLoop: -- Compare each entry in the table
  FOR i IN TableAlias'RANGE(1) LOOP
    RowLoop: -- Check each element of the ViolationFlags
    FOR j IN 0 TO ViolFlagsSize LOOP
      IF (j = ViolFlagsSize) THEN
        ViolFlAryPosn := 0;
        RowLoop2: -- Check relevant elements of the ViolationFlagsArray
        FOR k IN 0 TO MemActionNdx - ViolFlagsSize - 1 LOOP
          ViolFlAryItem := '0';
          SubwordLoop: -- Check for 'X' in ViolationFlagsArray chunk
          FOR s IN ViolFlAryPosn TO ViolFlAryPosn+ViolationSizesArray(k)-1 LOOP
            IF (ViolationFlagsArray(s) = 'X') THEN
              ViolFlAryItem := 'X';
              EXIT SubwordLoop;
            END IF;
          END LOOP;
          MemoryMatch (   TableAlias(i,k + ViolFlagsSize),
          ViolFlAryItem,ViolFlAryItem,
          Err, Match);
          ViolFlAryPosn := ViolFlAryPosn + ViolationSizesArray(k);
          EXIT RowLoop2 WHEN NOT(Match);
        END LOOP;
        IF (Match) THEN
          -- Compute memory and data corruption masks
          ViolationTableCorruptMask(
            CorruptMask             => MemoryCorruptMask    ,
            Action                  => TableAlias(i, MemActionNdx),
            ViolationFlags          => ViolationFlags       ,
            ViolationFlagsArray     => ViolationFlagsArray  ,
            ViolationSizesArray     => ViolationSizesArray  ,
            ViolationTable          => ViolationTable       ,
            TableIndex              => i                    ,
            BitsPerWord             => BitsPerWord          ,
            BitsPerSubWord          => BitsPerSubWord       ,
            BitsPerEnable           => BitsPerEnable
          );
          ViolationTableCorruptMask(
            CorruptMask             => DataCorruptMask      ,
            Action                  => TableAlias(i, DatActionNdx),
            ViolationFlags          => ViolationFlags       ,
            ViolationFlagsArray     => ViolationFlagsArray  ,
            ViolationSizesArray     => ViolationSizesArray  ,
            ViolationTable          => ViolationTable       ,
            TableIndex              => i                    ,
            BitsPerWord             => BitsPerWord          ,
            BitsPerSubWord          => BitsPerSubWord       ,
            BitsPerEnable           => BitsPerEnable
          );
          -- get the return memory and data actions
          MemoryAction := TableAlias(i, MemActionNdx);
          DataAction   := TableAlias(i, DatActionNdx);
          -- DEBUG: The lines below report table search
          IF (MsgOn) THEN
            PrintMemoryMessage(MsgVMV,TableAlias,i,PortName);
          END IF;
          -- DEBUG: The lines above report table search
          RETURN;
        END IF;
      ELSE
        -- Match violation table inputs
        Err := FALSE;
        Match := FALSE;
        IF (TableAlias(i,j) /= 'X' AND 
            TableAlias(i,j) /= '0' AND
            TableAlias(i,j) /= '-') THEN
          Err := TRUE;
        ELSIF (TableAlias(i,j) = '-' OR
            (TableAlias(i,j) = 'X' AND ViolationFlags(j) = 'X') OR
            (TableAlias(i,j) = '0' AND ViolationFlags(j) = '0')) THEN
          Match := TRUE;
        END IF;
      END IF;
      EXIT RowLoop WHEN NOT(Match);
      EXIT ColLoop WHEN Err;
    END LOOP RowLoop;
  END LOOP ColLoop;
  -- no match found, return default action
  MemoryAction := 's';   -- no change to memory
  DataAction   := 'S';   -- no change to dataout
  IF (MsgOn) THEN
    PrintMemoryMessage(MsgVMV,ErrDefMemAct,HeaderMsg,PortName);
  END IF;
  RETURN;
END;

-- ----------------------------------------------------------------------------
-- Procedure:   HandleMemoryAction
-- Parameters:  MemoryData   - Pointer to memory data structure
--              PortFlag     - Indicates read/write mode of port
--              CorruptMask  - XOR'ed with DataInBus when corrupting
--              DataInBus    - Current data bus in
--              Address      - Current address integer
--              HighBit      - Current address high bit
--              LowBit       - Current address low bit
--              MemoryTable  - Input memory action table
--              MemoryAction - Memory action to be performed
--              PortName     - Port name string for messages
--              HeaderMsg    - Header string for messages
--              MsgOn        - Control message output
-- Description: This procedure performs the specified memory action on
--              the input memory data structure.
-- ----------------------------------------------------------------------------
PROCEDURE HandleMemoryAction (
  VARIABLE MemoryData     : INOUT VitalMemoryDataType;
  VARIABLE PortFlag       : INOUT VitalPortFlagType;
  CONSTANT CorruptMask    : IN std_logic_vector;
  CONSTANT DataInBus      : IN std_logic_vector;
  CONSTANT Address        : IN INTEGER;
  CONSTANT HighBit        : IN NATURAL;
  CONSTANT LowBit         : IN NATURAL;
  CONSTANT MemoryTable    : IN VitalMemoryTableType;
  CONSTANT MemoryAction   : IN VitalMemorySymbolType;
  CONSTANT CallerName     : IN STRING;
  CONSTANT PortName       : IN STRING := "";
  CONSTANT HeaderMsg      : IN STRING := "";
  CONSTANT MsgOn          : IN BOOLEAN := TRUE
) IS
  VARIABLE DataInTmp : std_logic_vector(DataInBus'RANGE)
                        := DataInBus;
  BEGIN

  -- Handle the memory action
  CASE MemoryAction IS

  WHEN 'w'    =>
    -- Writing data to memory
    IF (MsgOn) THEN
      PrintMemoryMessage(CallerName,ErrWrDatMem,HeaderMsg,PortName);
    END IF;
    WriteMemory(MemoryData,DataInBus,Address,HighBit,LowBit);
    PortFlag.MemoryCurrent := WRITE;

  WHEN 's'    =>
    -- Retaining previous memory contents
    IF (MsgOn) THEN
      PrintMemoryMessage(CallerName,ErrNoChgMem,HeaderMsg,PortName);
    END IF;
    -- Set memory current to quiet state
    PortFlag.MemoryCurrent := READ;

  WHEN 'c'    =>
    -- Corrupting entire memory with 'X'
    IF (MsgOn) THEN
      PrintMemoryMessage(CallerName,ErrCrAllMem,HeaderMsg,PortName);
    END IF;
    DataInTmp := (OTHERS => 'X');
    -- No need to CorruptMask
    FOR i IN 0 TO MemoryData.NoOfWords-1 LOOP
      WriteMemory(MemoryData,DataInTmp,i);
    END LOOP;
    PortFlag.MemoryCurrent := CORRUPT;

  WHEN 'l'    =>
    -- Corrupting a word in memory with 'X'
    IF (MsgOn) THEN
      PrintMemoryMessage(CallerName,ErrCrWrdMem,HeaderMsg,PortName);
    END IF;
    DataInTmp := (OTHERS => 'X');
    -- No need to CorruptMask
    WriteMemory(MemoryData,DataInTmp,Address);
    PortFlag.MemoryCurrent := CORRUPT;

  WHEN 'd'    =>
    -- Corrupting a single bit in memory with 'X'
    IF (MsgOn) THEN
      PrintMemoryMessage(CallerName,ErrCrBitMem,HeaderMsg,PortName);
    END IF;
    ReadMemory(MemoryData,DataInTmp,Address);
    DataInTmp := DataInTmp XOR CorruptMask;
    WriteMemory(MemoryData,DataInTmp,Address,HighBit,LowBit);
    PortFlag.MemoryCurrent := CORRUPT;

  WHEN 'e'    =>
    -- Corrupting a word with 'X' based on data in
    IF (MsgOn) THEN
      PrintMemoryMessage(CallerName,ErrCrDatMem,HeaderMsg,PortName);
    END IF;
    ReadMemory(MemoryData,DataInTmp,Address);
    IF (DataInTmp /= DataInBus) THEN
      DataInTmp := (OTHERS => 'X');
      -- No need to CorruptMask
      WriteMemory(MemoryData,DataInTmp,Address);
    END IF;
    PortFlag.MemoryCurrent := CORRUPT;

  WHEN 'C'    =>
    -- Corrupting a sub-word entire memory with 'X'
    IF (MsgOn) THEN
      PrintMemoryMessage(CallerName,ErrCrAllSubMem,HeaderMsg,PortName);
    END IF;
    FOR i IN 0 TO MemoryData.NoOfWords-1 LOOP
      ReadMemory(MemoryData,DataInTmp,i);
      DataInTmp := DataInTmp XOR CorruptMask;
      WriteMemory(MemoryData,DataInTmp,i,HighBit,LowBit);
    END LOOP;
    PortFlag.MemoryCurrent := CORRUPT;

  WHEN 'L'    =>
    -- Corrupting a sub-word in memory with 'X'
    IF (MsgOn) THEN
      PrintMemoryMessage(CallerName,ErrCrWrdSubMem,HeaderMsg,PortName);
    END IF;
    ReadMemory(MemoryData,DataInTmp,Address);
    DataInTmp := DataInTmp XOR CorruptMask;
    WriteMemory(MemoryData,DataInTmp,Address,HighBit,LowBit);
    PortFlag.MemoryCurrent := CORRUPT;

  WHEN 'D'    =>
    -- Corrupting a single bit of a memory sub-word with 'X'
    IF (MsgOn) THEN
      PrintMemoryMessage(CallerName,ErrCrBitSubMem,HeaderMsg,PortName);
    END IF;
    ReadMemory(MemoryData,DataInTmp,Address);
    DataInTmp := DataInTmp XOR CorruptMask;
    WriteMemory(MemoryData,DataInTmp,Address,HighBit,LowBit);
    PortFlag.MemoryCurrent := CORRUPT;

  WHEN 'E'    =>
    -- Corrupting a sub-word with 'X' based on data in
    IF (MsgOn) THEN
      PrintMemoryMessage(CallerName,ErrCrDatSubMem,HeaderMsg,PortName);
    END IF;
    ReadMemory(MemoryData,DataInTmp,Address);
    IF (DataInBus(HighBit DOWNTO LowBit) /= 
      DataInTmp(HighBit DOWNTO LowBit)) THEN
      DataInTmp(HighBit DOWNTO LowBit) := (OTHERS => 'X');
      WriteMemory(MemoryData,DataInTmp,Address,HighBit,LowBit);
    END IF;
    --PortFlag := WRITE;
    PortFlag.MemoryCurrent := CORRUPT;

  WHEN '0'    =>
    -- Assigning low level to memory location
    IF (MsgOn) THEN
      PrintMemoryMessage(CallerName,ErrAsg0Mem,HeaderMsg,PortName);
    END IF;
    DataInTmp := (OTHERS => '0');
    WriteMemory(MemoryData,DataInTmp,Address, HighBit, LowBit);
    PortFlag.MemoryCurrent := WRITE;

  WHEN '1'    =>
    -- Assigning high level to memory location
    IF (MsgOn) THEN
      PrintMemoryMessage(CallerName,ErrAsg1Mem,HeaderMsg,PortName);
    END IF;
    DataInTmp := (OTHERS => '1');
    WriteMemory(MemoryData,DataInTmp,Address, HighBit, LowBit);
    PortFlag.MemoryCurrent := WRITE;

  WHEN 'Z'    =>
    -- Assigning high impedence to memory location
    IF (MsgOn) THEN
      PrintMemoryMessage(CallerName,ErrAsgZMem,HeaderMsg,PortName);
    END IF;
    DataInTmp := (OTHERS => 'Z');
    WriteMemory(MemoryData,DataInTmp,Address, HighBit, LowBit);
    PortFlag.MemoryCurrent := WRITE;

  WHEN OTHERS =>
    -- Unknown memory action
    PortFlag.MemoryCurrent := UNDEF;
    IF (MsgOn) THEN
      PrintMemoryMessage(CallerName,ErrUnknMemDo,HeaderMsg,PortName);
    END IF;

  END CASE;

  -- Note: HandleMemoryAction does not change the PortFlag.OutputDisable
END;

-- ----------------------------------------------------------------------------
-- Procedure:   HandleDataAction
-- Parameters:  DataOutBus  - Output result of the data action
--              MemoryData  - Input pointer to memory data structure
--              PortFlag    - Indicates read/write mode of port
--              CorruptMask - XOR'ed with DataInBus when corrupting
--              DataInBus   - Current data bus in
--              Address     - Current address integer
--              HighBit     - Current address high bit
--              LowBit      - Current address low bit
--              MemoryTable - Input memory action table
--              DataAction  - Data action to be performed
--              PortName    - Port name string for messages
--              HeaderMsg   - Header string for messages
--              MsgOn       - Control message output
-- Description: This procedure performs the specified data action based
--              on the input memory data structure.  Checks whether
--              the previous state is HighZ. If yes then portFlag
--              should be NOCHANGE for VMPD to ignore IORetain
--              corruption. The idea is that the first Z should be
--              propagated but later ones should be ignored.
-- ----------------------------------------------------------------------------
PROCEDURE HandleDataAction (
  VARIABLE DataOutBus     : INOUT std_logic_vector;
  VARIABLE MemoryData     : INOUT VitalMemoryDataType;
  VARIABLE PortFlag       : INOUT VitalPortFlagType;
  CONSTANT CorruptMask    : IN std_logic_vector;
  CONSTANT DataInBus      : IN std_logic_vector;
  CONSTANT Address        : IN INTEGER;
  CONSTANT HighBit        : IN NATURAL;
  CONSTANT LowBit         : IN NATURAL;
  CONSTANT MemoryTable    : IN VitalMemoryTableType;
  CONSTANT DataAction     : IN VitalMemorySymbolType;
  CONSTANT CallerName     : IN STRING;
  CONSTANT PortName       : IN STRING := "";
  CONSTANT HeaderMsg      : IN STRING := "";
  CONSTANT MsgOn          : IN BOOLEAN := TRUE
) IS

  VARIABLE DataOutTmp : std_logic_vector(DataOutBus'RANGE)
                          := DataOutBus;

BEGIN

  -- Handle the data action
  CASE DataAction IS

  WHEN 'l'    =>
    -- Corrupting data out with 'X'
    IF (MsgOn) THEN
      PrintMemoryMessage(CallerName,ErrCrWrdOut,HeaderMsg,PortName);
    END IF;
    DataOutTmp := (OTHERS => 'X');
    -- No need to CorruptMask
    PortFlag.DataCurrent := CORRUPT;

  WHEN 'd'    =>
    -- Corrupting a single bit of data out with 'X'
    IF (MsgOn) THEN
      PrintMemoryMessage(CallerName,ErrCrBitOut,HeaderMsg,PortName);
    END IF;
    DataOutTmp(HighBit DOWNTO LowBit) := 
      DataOutTmp(HighBit DOWNTO LowBit) XOR 
      CorruptMask(HighBit DOWNTO LowBit);
    PortFlag.DataCurrent := CORRUPT;

  WHEN 'e'    =>
    -- Corrupting data out with 'X' based on data in
    IF (MsgOn) THEN
      PrintMemoryMessage(CallerName,ErrCrDatOut,HeaderMsg,PortName);
    END IF;
    ReadMemory(MemoryData,DataOutTmp,Address);
    IF (DataOutTmp /= DataInBus) THEN
      DataOutTmp := (OTHERS => 'X');
      -- No need to CorruptMask
    END IF;
    PortFlag.DataCurrent := CORRUPT;

  WHEN 'L'    =>
    -- Corrupting data out sub-word with 'X'
    IF (MsgOn) THEN
      PrintMemoryMessage(CallerName,ErrCrWrdSubOut,HeaderMsg,PortName);
    END IF;
    ReadMemory(MemoryData,DataOutTmp,Address);
    DataOutTmp(HighBit DOWNTO LowBit) := 
      DataOutTmp(HighBit DOWNTO LowBit) XOR 
      CorruptMask(HighBit DOWNTO LowBit);
    PortFlag.DataCurrent := CORRUPT;

  WHEN 'D'    =>
    -- Corrupting a single bit of data out sub-word with 'X'
    IF (MsgOn) THEN
      PrintMemoryMessage(CallerName,ErrCrBitSubOut,HeaderMsg,PortName);
    END IF;
    DataOutTmp(HighBit DOWNTO LowBit) := 
      DataOutTmp(HighBit DOWNTO LowBit) XOR 
      CorruptMask(HighBit DOWNTO LowBit);
    PortFlag.DataCurrent := CORRUPT;

  WHEN 'E'    =>
    -- Corrupting data out sub-word with 'X' based on data in
    IF (MsgOn) THEN
    PrintMemoryMessage(CallerName,ErrCrDatSubOut,HeaderMsg,PortName);
    END IF;
    ReadMemory(MemoryData,DataOutTmp,Address);
    IF (DataInBus(HighBit DOWNTO LowBit) /= 
        DataOutTmp(HighBit DOWNTO LowBit)) THEN
      DataOutTmp(HighBit DOWNTO LowBit) := (OTHERS => 'X');
      -- No need to CorruptMask
    END IF;
    PortFlag.DataCurrent := CORRUPT;

  WHEN 'M'    =>
    -- Implicit read from memory to data out
    IF (MsgOn) THEN
      PrintMemoryMessage(CallerName,ErrImplOut,HeaderMsg,PortName);
    END IF;
    PortFlag.DataCurrent := READ;

  WHEN 'm'    =>
    -- Reading data from memory to data out
    IF (MsgOn) THEN
      PrintMemoryMessage(CallerName,ErrReadOut,HeaderMsg,PortName);
    END IF;
    ReadMemory(MemoryData,DataOutTmp,Address);
    PortFlag.DataCurrent := READ;

  WHEN 't'    =>
    -- Transferring from data in to data out
    IF (MsgOn) THEN
      PrintMemoryMessage(CallerName,ErrAssgOut,HeaderMsg,PortName);
    END IF;
    DataOutTmp := DataInBus;
    PortFlag.DataCurrent := READ;

  WHEN '0'    =>
    -- Assigning low level to data out
    IF (MsgOn) THEN
      PrintMemoryMessage(CallerName,ErrAsg0Out,HeaderMsg,PortName);
    END IF;
    DataOutTmp := (OTHERS => '0');
    PortFlag.DataCurrent := READ;

  WHEN '1'    =>
    -- Assigning high level to data out
    IF (MsgOn) THEN
      PrintMemoryMessage(CallerName,ErrAsg1Out,HeaderMsg,PortName);
    END IF;
    DataOutTmp := (OTHERS => '1');
    PortFlag.DataCurrent := READ;

  WHEN 'Z'    =>
    -- Assigning high impedence to data out
    IF (MsgOn) THEN
      PrintMemoryMessage(CallerName,ErrAsgZOut,HeaderMsg,PortName);
    END IF;
    DataOutTmp := (OTHERS => 'Z');
    PortFlag.DataCurrent := HIGHZ;

  WHEN 'S'    =>
    -- Keeping data out at steady value
    PortFlag.OutputDisable := TRUE;
    IF (MsgOn) THEN
      PrintMemoryMessage(CallerName,ErrAsgSOut,HeaderMsg,PortName);
    END IF;

  WHEN OTHERS =>
    -- Unknown data action
    PortFlag.DataCurrent := UNDEF;
    IF (MsgOn) THEN
      PrintMemoryMessage(CallerName,ErrUnknDatDo,HeaderMsg,PortName);
    END IF;

  END CASE;

  DataOutBus(HighBit DOWNTO LowBit) := DataOutTmp(HighBit DOWNTO LowBit);

END;


-- ----------------------------------------------------------------------------
-- Memory Table Modeling Primitives
-- ----------------------------------------------------------------------------

-- ----------------------------------------------------------------------------
-- Procedure:   VitalDeclareMemory
-- Parameters:  NoOfWords          - Number of words in the memory
--              NoOfBitsPerWord    - Number of bits per word in memory
--              NoOfBitsPerSubWord - Number of bits per sub word
--              MemoryLoadFile     - Name of data file to load
-- Description: This function is intended to be used to initialize
--              memory data declarations, i.e. to be executed duing
--              simulation elaboration time.  Handles the allocation
--              and initialization of memory for the memory data.
--              Default NoOfBitsPerSubWord is NoOfBitsPerWord.
-- ----------------------------------------------------------------------------
IMPURE FUNCTION VitalDeclareMemory (
  CONSTANT NoOfWords          : IN POSITIVE;
  CONSTANT NoOfBitsPerWord    : IN POSITIVE;
  CONSTANT MemoryLoadFile     : IN string := "";
  CONSTANT BinaryLoadFile     : IN BOOLEAN := FALSE
) RETURN VitalMemoryDataType IS
  VARIABLE MemoryPtr      : VitalMemoryDataType;
BEGIN
  MemoryPtr := VitalDeclareMemory(
    NoOfWords           => NoOfWords,
    NoOfBitsPerWord     => NoOfBitsPerWord,
    NoOfBitsPerSubWord  => NoOfBitsPerWord,
    MemoryLoadFile      => MemoryLoadFile,
    BinaryLoadFile      => BinaryLoadFile
  );
  RETURN MemoryPtr;
END;

-- ----------------------------------------------------------------------------
IMPURE FUNCTION VitalDeclareMemory (
  CONSTANT NoOfWords          : IN POSITIVE;
  CONSTANT NoOfBitsPerWord    : IN POSITIVE;
  CONSTANT NoOfBitsPerSubWord : IN POSITIVE;
  CONSTANT MemoryLoadFile     : IN string := "";
  CONSTANT BinaryLoadFile     : IN BOOLEAN := FALSE
) RETURN VitalMemoryDataType IS
  VARIABLE MemoryPtr      : VitalMemoryDataType;
  VARIABLE BitsPerEnable  : NATURAL 
                            := ((NoOfBitsPerWord-1)
                                /NoOfBitsPerSubWord)+1;
BEGIN
  PrintMemoryMessage(MsgVDM,ErrInitMem);
  MemoryPtr := new VitalMemoryArrayRecType '( 
      NoOfWords           => NoOfWords,
      NoOfBitsPerWord     => NoOfBitsPerWord,
      NoOfBitsPerSubWord  => NoOfBitsPerSubWord,
      NoOfBitsPerEnable   => BitsPerEnable,
      MemoryArrayPtr      => NULL 
    );
  MemoryPtr.MemoryArrayPtr 
    := new MemoryArrayType (0 to MemoryPtr.NoOfWords - 1);
  FOR i IN 0 TO MemoryPtr.NoOfWords - 1 LOOP
    MemoryPtr.MemoryArrayPtr(i) 
      := new MemoryWordType (MemoryPtr.NoOfBitsPerWord - 1 DOWNTO 0);
  END LOOP;
  IF (MemoryLoadFile /= "") THEN
    LoadMemory (MemoryPtr, MemoryLoadFile, BinaryLoadFile);
  END IF;
  RETURN MemoryPtr;
END;

-- ----------------------------------------------------------------------------
-- Procedure:   VitalMemoryTable
-- Parameters:  DataOutBus     - Output candidate zero delay data bus out
--              MemoryData     - Pointer to memory data structure
--              PrevControls   - Previous data in for edge detection
--              PrevEnableBus  - Previous enables for edge detection
--              PrevDataInBus  - Previous data bus for edge detection
--              PrevAddressBus - Previous address bus for edge detection
--              PortFlag       - Indicates port operating mode
--              PortFlagArray  - Vector form of PortFlag for sub-word
--              Controls       - Agregate of scalar control lines
--              EnableBus      - Concatenation of vector control lines
--              DataInBus      - Input value of data bus in
--              AddressBus     - Input value of address bus in
--              AddressValue   - Decoded value of the AddressBus
--              MemoryTable    - Input memory action table
--              PortType       - The type of port (currently not used)
--              PortName       - Port name string for messages
--              HeaderMsg      - Header string for messages
--              MsgOn          - Control the generation of messages
--              MsgSeverity    - Control level of message generation
-- Description: This procedure implements the majority of the memory 
--              modeling functionality via lookup of the memory action
--              tables and performing the specified actions if matches
--              are found, or the default actions otherwise.  The
--              overloadings are provided for the word and sub-word
--              (using the EnableBus and PortFlagArray arguments) addressing
--              cases.
-- ----------------------------------------------------------------------------
PROCEDURE VitalMemoryTable (
  VARIABLE DataOutBus     : INOUT std_logic_vector;
  VARIABLE MemoryData     : INOUT VitalMemoryDataType;
  VARIABLE PrevControls   : INOUT std_logic_vector;
  VARIABLE PrevDataInBus  : INOUT std_logic_vector;
  VARIABLE PrevAddressBus : INOUT std_logic_vector;
  VARIABLE PortFlag       : INOUT VitalPortFlagVectorType;
  CONSTANT Controls       : IN std_logic_vector;
  CONSTANT DataInBus      : IN std_logic_vector;
  CONSTANT AddressBus     : IN std_logic_vector;
  VARIABLE AddressValue   : INOUT VitalAddressValueType;
  CONSTANT MemoryTable    : IN VitalMemoryTableType;
  CONSTANT PortType       : IN VitalPortType := UNDEF;
  CONSTANT PortName       : IN STRING := "";
  CONSTANT HeaderMsg      : IN STRING := "";
  CONSTANT MsgOn          : IN BOOLEAN := TRUE;
  CONSTANT MsgSeverity    : IN SEVERITY_LEVEL := WARNING
) IS

  VARIABLE DataOutTmp     : std_logic_vector(DataOutBus'RANGE)
                            := DataOutBus;
  VARIABLE MemoryAction   : VitalMemorySymbolType;
  VARIABLE DataAction     : VitalMemorySymbolType;
  VARIABLE HighBit        : NATURAL := MemoryData.NoOfBitsPerWord-1;
  VARIABLE LowBit         : NATURAL := 0;
  VARIABLE Address        : INTEGER := 0;
  VARIABLE PortFlagTmp    : VitalPortFlagType;
  VARIABLE AddrFlag       : VitalMemorySymbolType := 'g';  -- good addr
  VARIABLE DataFlag       : VitalMemorySymbolType := 'g';  -- good data
  VARIABLE MemCorruptMask : std_logic_vector (DataOutBus'RANGE);
  VARIABLE DatCorruptMask : std_logic_vector (DataOutBus'RANGE);

BEGIN

  -- Optimize for case when all current inputs are same as previous
  IF (PrevDataInBus = DataInBus 
      AND PrevAddressBus = AddressBus 
      AND PrevControls = Controls
      AND PortFlag(0).MemoryCurrent = PortFlag(0).MemoryPrevious
      AND PortFlag(0).DataCurrent = PortFlag(0).DataPrevious) THEN
    PortFlag(0).OutputDisable := TRUE;
    RETURN;
  END IF;

  PortFlag(0).DataPrevious := PortFlag(0).DataCurrent;
  PortFlag(0).MemoryPrevious := PortFlag(0).MemoryCurrent;
  PortFlag(0).OutputDisable := FALSE;
  PortFlagTmp := PortFlag(0);

  -- Convert address bus to integer value and table lookup flag
  DecodeAddress(
    Address         => Address             ,
    AddrFlag        => AddrFlag            ,
    MemoryData      => MemoryData          ,
    PrevAddressBus  => PrevAddressBus      ,
    AddressBus      => AddressBus          
  );

  -- Interpret data bus as a table lookup flag
  DecodeData (
    DataFlag        => DataFlag           ,
    PrevDataInBus   => PrevDataInBus      ,
    DataInBus       => DataInBus          ,
    HighBit         => HighBit            ,
    LowBit          => LowBit             
  );

  -- Lookup memory and data actions
  MemoryTableLookUp(  
    MemoryAction        => MemoryAction     ,
    DataAction          => DataAction       ,
    MemoryCorruptMask   => MemCorruptMask   ,
    DataCorruptMask     => DatCorruptMask   ,
    PrevControls        => PrevControls     ,
    Controls            => Controls         ,
    AddrFlag            => AddrFlag         ,
    DataFlag            => DataFlag         ,
    MemoryTable         => MemoryTable      ,
    PortName            => PortName         ,
    HeaderMsg           => HeaderMsg        ,
    MsgOn               => MsgOn            
  );

  -- Handle data action before memory action
  -- This allows reading previous memory contents
  HandleDataAction(
    DataOutBus      => DataOutTmp       ,
    MemoryData      => MemoryData       ,
    PortFlag        => PortFlagTmp      ,
    CorruptMask     => DatCorruptMask   ,
    DataInBus       => DataInBus        ,
    Address         => Address          ,
    HighBit         => HighBit          ,
    LowBit          => LowBit           ,
    MemoryTable     => MemoryTable      ,
    DataAction      => DataAction       ,
    CallerName      => MsgVMT           ,
    PortName        => PortName         ,
    HeaderMsg       => HeaderMsg        ,
    MsgOn           => MsgOn            
  );

  HandleMemoryAction(
    MemoryData      => MemoryData       ,
    PortFlag        => PortFlagTmp      ,
    CorruptMask     => MemCorruptMask   ,
    DataInBus       => DataInBus        ,
    Address         => Address          ,
    HighBit         => HighBit          ,
    LowBit          => LowBit           ,
    MemoryTable     => MemoryTable      ,
    MemoryAction    => MemoryAction     ,
    CallerName      => MsgVMT           ,
    PortName        => PortName         ,
    HeaderMsg       => HeaderMsg        ,
    MsgOn           => MsgOn            
  );

  -- Set the output PortFlag(0) value 
  IF (DataAction = 'S') THEN
    PortFlagTmp.OutputDisable := TRUE;
  END IF;
  IF (PortFlagTmp.DataCurrent = PortFlagTmp.DataPrevious
      AND PortFlagTmp.DataCurrent = HIGHZ) THEN
    PortFlagTmp.OutputDisable := TRUE;
  END IF;
  PortFlag(0) := PortFlagTmp;

  -- Set previous values for subsequent edge detection
  PrevControls := Controls;
  PrevDataInBus := DataInBus;
  PrevAddressBus := AddressBus;

  -- Set the candidate zero delay return value
  DataOutBus := DataOutTmp;

  -- Set the output AddressValue for VitalMemoryCrossPorts
  AddressValue := Address;

END VitalMemoryTable;

-- ----------------------------------------------------------------------------
PROCEDURE VitalMemoryTable (
  VARIABLE DataOutBus     : INOUT std_logic_vector;
  VARIABLE MemoryData     : INOUT VitalMemoryDataType;
  VARIABLE PrevControls   : INOUT std_logic_vector;
  VARIABLE PrevEnableBus  : INOUT std_logic_vector;
  VARIABLE PrevDataInBus  : INOUT std_logic_vector;
  VARIABLE PrevAddressBus : INOUT std_logic_vector;
  VARIABLE PortFlagArray  : INOUT VitalPortFlagVectorType;
  CONSTANT Controls       : IN std_logic_vector;
  CONSTANT EnableBus      : IN std_logic_vector;
  CONSTANT DataInBus      : IN std_logic_vector;
  CONSTANT AddressBus     : IN std_logic_vector;
  VARIABLE AddressValue   : INOUT VitalAddressValueType;
  CONSTANT MemoryTable    : IN VitalMemoryTableType;
  CONSTANT PortType       : IN VitalPortType := UNDEF;
  CONSTANT PortName       : IN STRING := "";
  CONSTANT HeaderMsg      : IN STRING := "";
  CONSTANT MsgOn          : IN BOOLEAN := TRUE;
  CONSTANT MsgSeverity    : IN SEVERITY_LEVEL := WARNING
) IS

  VARIABLE BitsPerWord    : NATURAL := MemoryData.NoOfBitsPerWord;
  VARIABLE BitsPerSubWord : NATURAL := MemoryData.NoOfBitsPerSubWord;
  VARIABLE BitsPerEnable  : NATURAL := MemoryData.NoOfBitsPerEnable;
  VARIABLE DataOutTmp     : std_logic_vector(DataOutBus'RANGE)
  := DataOutBus;
  VARIABLE MemoryAction   : VitalMemorySymbolType;
  VARIABLE DataAction     : VitalMemorySymbolType;
  VARIABLE HighBit        : NATURAL := BitsPerSubWord-1;
  VARIABLE LowBit         : NATURAL := 0;
  VARIABLE Address        : INTEGER := 0;
  VARIABLE PortFlagTmp    : VitalPortFlagType;
  VARIABLE AddrFlag       : VitalMemorySymbolType := 'g';  -- good addr
  VARIABLE DataFlag       : VitalMemorySymbolType := 'g';  -- good data
  VARIABLE MemCorruptMask : std_logic_vector (DataOutBus'RANGE);
  VARIABLE DatCorruptMask : std_logic_vector (DataOutBus'RANGE);

BEGIN

  -- Optimize for case when all current inputs are same as previous
  IF (PrevDataInBus = DataInBus 
      AND PrevAddressBus = AddressBus 
      AND PrevControls = Controls) THEN
    CheckFlags:
    FOR i IN 0 TO BitsPerEnable-1 LOOP
      IF (PortFlagArray(i).MemoryCurrent /= PortFlagArray(i).MemoryPrevious
          OR  PortFlagArray(i).DataCurrent /= PortFlagArray(i).DataPrevious) THEN
        EXIT CheckFlags;
      END IF;
      IF (i = BitsPerEnable-1) THEN
        FOR j IN 0 TO BitsPerEnable-1 LOOP
          PortFlagArray(j).OutputDisable := TRUE;
        END LOOP;
        RETURN;
      END IF;
    END LOOP;
  END IF;

  -- Convert address bus to integer value and table lookup flag
  DecodeAddress(
    Address         => Address,
    AddrFlag        => AddrFlag,
    MemoryData      => MemoryData,
    PrevAddressBus  => PrevAddressBus,
    AddressBus      => AddressBus     
  );

  -- Perform independent operations for each sub-word
  FOR i IN 0 TO BitsPerEnable-1 LOOP

    -- Set the output PortFlag(i) value
    PortFlagArray(i).DataPrevious := PortFlagArray(i).DataCurrent;
    PortFlagArray(i).MemoryPrevious := PortFlagArray(i).MemoryCurrent;
    PortFlagArray(i).OutputDisable := FALSE;
    PortFlagTmp := PortFlagArray(i);

    -- Interpret data bus as a table lookup flag
    DecodeData (
      DataFlag        => DataFlag         ,
      PrevDataInBus   => PrevDataInBus    ,
      DataInBus       => DataInBus        ,
      HighBit         => HighBit          ,
      LowBit          => LowBit           
    );

    -- Lookup memory and data actions
    MemoryTableLookUp(  
      MemoryAction        => MemoryAction     ,
      DataAction          => DataAction       ,
      MemoryCorruptMask   => MemCorruptMask   ,
      DataCorruptMask     => DatCorruptMask   ,
      PrevControls        => PrevControls     ,
      PrevEnableBus       => PrevEnableBus    ,
      Controls            => Controls         ,
      EnableBus           => EnableBus        ,
      EnableIndex         => i                ,
      BitsPerWord         => BitsPerWord      ,
      BitsPerSubWord      => BitsPerSubWord   ,
      BitsPerEnable       => BitsPerEnable    ,
      AddrFlag            => AddrFlag         ,
      DataFlag            => DataFlag         ,
      MemoryTable         => MemoryTable      ,
      PortName            => PortName         ,
      HeaderMsg           => HeaderMsg        ,
      MsgOn               => MsgOn            
    );

    -- Handle data action before memory action
    -- This allows reading previous memory contents
    HandleDataAction(
      DataOutBus      => DataOutTmp       ,
      MemoryData      => MemoryData       ,
      PortFlag        => PortFlagTmp      ,
      CorruptMask     => DatCorruptMask   ,
      DataInBus       => DataInBus        ,
      Address         => Address          ,
      HighBit         => HighBit          ,
      LowBit          => LowBit           ,
      MemoryTable     => MemoryTable      ,
      DataAction      => DataAction       ,
      CallerName      => MsgVMT           ,
      PortName        => PortName         ,
      HeaderMsg       => HeaderMsg        ,
      MsgOn           => MsgOn            
    );

    HandleMemoryAction(
      MemoryData      => MemoryData       ,
      PortFlag        => PortFlagTmp      ,
      CorruptMask     => MemCorruptMask   ,
      DataInBus       => DataInBus        ,
      Address         => Address          ,
      HighBit         => HighBit          ,
      LowBit          => LowBit           ,
      MemoryTable     => MemoryTable      ,
      MemoryAction    => MemoryAction     ,
      CallerName      => MsgVMT           ,
      PortName        => PortName         ,
      HeaderMsg       => HeaderMsg        ,
      MsgOn           => MsgOn            
    );

    -- Set the output PortFlag(i) value 
    IF (DataAction = 'S') THEN
      PortFlagTmp.OutputDisable := TRUE;
    END IF;
    IF (PortFlagTmp.DataCurrent = PortFlagTmp.DataPrevious
        AND PortFlagTmp.DataCurrent = HIGHZ) THEN
      PortFlagTmp.OutputDisable := TRUE;
    END IF;
    PortFlagArray(i) := PortFlagTmp;

    IF (i < BitsPerEnable-1) THEN
      -- Calculate HighBit and LowBit
      LowBit := LowBit + BitsPerSubWord;
      IF (LowBit > BitsPerWord) THEN
        LowBit := BitsPerWord;
      END IF;
      HighBit := LowBit + BitsPerSubWord;
      IF (HighBit > BitsPerWord) THEN
        HighBit := BitsPerWord;
      ELSE
        HighBit := HighBit - 1;
      END IF;
    END IF;

  END LOOP;

  -- Set previous values for subsequent edge detection
  PrevControls := Controls;
  PrevEnableBus := EnableBus;
  PrevDataInBus := DataInBus;
  PrevAddressBus := AddressBus;

  -- Set the candidate zero delay return value
  DataOutBus := DataOutTmp;

  -- Set the output AddressValue for VitalMemoryCrossPorts
  AddressValue := Address;

END VitalMemoryTable;

-- ----------------------------------------------------------------------------
-- Procedure:   VitalMemoryCrossPorts
-- Parameters:  DataOutBus     - Output candidate zero delay data bus out
--              MemoryData     - Pointer to memory data structure
--              SamePortFlag   - Operating mode for same port
--              SamePortAddressValue  - Operating modes for cross ports
--              CrossPortAddressArray - Decoded AddressBus for cross ports
--              CrossPortMode  - Write contention and crossport read control
--              PortName       - Port name string for messages
--              HeaderMsg      - Header string for messages
--              MsgOn          - Control the generation of messages
-- Description: These procedures control the effect of memory operations
--              on a given port due to operations on other ports in a
--              multi-port memory.
--              This includes data write through when reading and writing 
--              to the same address, as well as write contention when
--              there are multiple write to the same address.
--              If addresses do not match then data bus is unchanged. 
--              The DataOutBus can be diabled with 'Z' value.
--              If the WritePortFlag is 'CORRUPT', that would mean
--              that the whole memory is corrupted. So, for corrupting
--              the Read port, the Addresses need not be compared. 
--
--     CrossPortMode Enum            Description
-- 1.  CpRead                        Allows Cross Port Read Only
--                                   No contention checking.
-- 2.  WriteContention               Allows for write contention checks 
--                                   only between multiple write ports
-- 3.  ReadWriteContention           Allows contention between read and 
--                                   write ports. The action is to corrupt 
--                                   the memory and the output bus.
-- 4.  CpReadAndWriteContention      Is a combination of 1 & 2
-- 5.  CpReadAndReadContention       Allows contention between read and
--                                   write ports. The action is to corrupt 
--                                   the dataout bus only. The cp read is 
--                                   performed if not contending.
-- ----------------------------------------------------------------------------
PROCEDURE VitalMemoryCrossPorts (
  VARIABLE DataOutBus             : INOUT std_logic_vector;
  VARIABLE MemoryData             : INOUT VitalMemoryDataType;
  VARIABLE SamePortFlag           : INOUT VitalPortFlagVectorType;
  CONSTANT SamePortAddressValue   : IN VitalAddressValueType;
  CONSTANT CrossPortFlagArray     : IN VitalPortFlagVectorType;
  CONSTANT CrossPortAddressArray  : IN VitalAddressValueVectorType;
  CONSTANT CrossPortMode          : IN VitalCrossPortModeType 
                                        := CpReadAndWriteContention;
  CONSTANT PortName               : IN STRING := "";
  CONSTANT HeaderMsg              : IN STRING := "";
  CONSTANT MsgOn                  : IN BOOLEAN := TRUE
) IS

  VARIABLE BitsPerWord    : NATURAL := MemoryData.NoOfBitsPerWord;
  VARIABLE BitsPerSubWord : NATURAL := MemoryData.NoOfBitsPerSubWord;
  VARIABLE BitsPerEnable  : NATURAL := MemoryData.NoOfBitsPerEnable;
  VARIABLE DataOutTmp : std_logic_vector(DataOutBus'RANGE) := (OTHERS => 'Z');
  VARIABLE MemoryTmp  : std_logic_vector(DataOutBus'RANGE);
  VARIABLE CrossPorts : NATURAL := CrossPortAddressArray'LENGTH;
  VARIABLE LowBit     : NATURAL := 0;
  VARIABLE HighBit    : NATURAL := BitsPerSubWord-1;
  VARIABLE Address    : VitalAddressValueType := SamePortAddressValue;
  VARIABLE AddressJ   : VitalAddressValueType;
  VARIABLE AddressK   : VitalAddressValueType;
  VARIABLE PortFlagI  : VitalPortFlagType;
  VARIABLE PortFlagIJ : VitalPortFlagType;
  VARIABLE PortFlagIK : VitalPortFlagType;
  VARIABLE DoCpRead   : BOOLEAN := FALSE;
  VARIABLE DoWrCont   : BOOLEAN := FALSE;
  VARIABLE DoCpCont   : BOOLEAN := FALSE;
  VARIABLE DoRdWrCont : BOOLEAN := FALSE;
  VARIABLE CpWrCont   : BOOLEAN := FALSE;
  VARIABLE ModeWrCont : BOOLEAN := 
                          (CrossPortMode=WriteContention) OR  
                          (CrossPortMode=CpReadAndWriteContention);
  VARIABLE ModeCpRead : BOOLEAN := 
                          (CrossPortMode=CpRead) OR   
                          (CrossPortMode=CpReadAndWriteContention);
  VARIABLE ModeCpCont : BOOLEAN := (CrossPortMode=ReadWriteContention);
  VARIABLE ModeRdWrCont : BOOLEAN := (CrossPortMode=CpReadAndReadContention);

BEGIN

  -- Check for disabled port (i.e. OTHERS => 'Z')
  IF (DataOutBus = DataOutTmp) THEN
    RETURN;
  ELSE
    DataOutTmp := DataOutBus;
  END IF;

  -- Check for error in address
  IF (Address < 0) THEN
    RETURN;
  END IF;

  ReadMemory(MemoryData,MemoryTmp,Address);

  SubWordLoop: -- For each slice of the sub-word I
  FOR i IN 0 TO BitsPerEnable-1 LOOP
    PortFlagI := SamePortFlag(i);

    -- For each cross port J: check with same port address
    FOR j IN 0 TO CrossPorts-1 LOOP
      PortFlagIJ := CrossPortFlagArray(i+j*BitsPerEnable);
      AddressJ := CrossPortAddressArray(j);
      IF (AddressJ < 0) THEN
        NEXT;
      END IF;
      DoWrCont :=  (Address    = AddressJ)  AND
                   (ModeWrCont = TRUE)      AND
                  ((PortFlagI.MemoryCurrent  = WRITE)     OR 
                   (PortFlagI.MemoryCurrent  = CORRUPT))  AND
                  ((PortFlagIJ.MemoryCurrent = WRITE)     OR
                   (PortFlagIJ.MemoryCurrent = CORRUPT))  ;
      DoCpRead :=  (Address    = AddressJ)  AND
                   (ModeCpRead = TRUE)      AND
                  ((PortFlagI.MemoryCurrent  = READ)      OR 
                   (PortFlagI.OutputDisable  = TRUE))     AND 
                  ((PortFlagIJ.MemoryCurrent = WRITE)     OR
                   (PortFlagIJ.MemoryCurrent = CORRUPT))  ;
      DoCpCont :=  (Address    = AddressJ)  AND
                   (ModeCpCont = TRUE)      AND
                  ((PortFlagI.MemoryCurrent  = READ)      OR 
                   (PortFlagI.OutputDisable  = TRUE))     AND 
                  ((PortFlagIJ.MemoryCurrent = WRITE)     OR
                   (PortFlagIJ.MemoryCurrent = CORRUPT))  ;
      DoRdWrCont:= (Address    = AddressJ)  AND
                   (ModeRdWrCont = TRUE)    AND
                  ((PortFlagI.MemoryCurrent  = READ)      OR 
                   (PortFlagI.OutputDisable  = TRUE))     AND 
                  ((PortFlagIJ.MemoryCurrent = WRITE)     OR
                   (PortFlagIJ.MemoryCurrent = CORRUPT))  ;
      IF (DoWrCont OR DoCpCont) THEN
        -- Corrupt dataout and memory
        MemoryTmp(HighBit DOWNTO LowBit) := (OTHERS => 'X');
        DataOutTmp(HighBit DOWNTO LowBit) := (OTHERS => 'X');
        SamePortFlag(i).MemoryCurrent := CORRUPT;
        SamePortFlag(i).DataCurrent := CORRUPT;
        SamePortFlag(i).OutputDisable := FALSE;
        EXIT;
      END IF;
      IF (DoCpRead) THEN
        -- Update dataout with memory
        DataOutTmp(HighBit DOWNTO LowBit) :=
        MemoryTmp(HighBit DOWNTO LowBit);
        SamePortFlag(i).MemoryCurrent := READ;
        SamePortFlag(i).DataCurrent := READ;
        SamePortFlag(i).OutputDisable := FALSE;
        EXIT;
      END IF;
      IF (DoRdWrCont) THEN
        -- Corrupt dataout only
        DataOutTmp(HighBit DOWNTO LowBit) := (OTHERS => 'X');
        SamePortFlag(i).DataCurrent := CORRUPT;
        SamePortFlag(i).OutputDisable := FALSE;
        EXIT;
      END IF;
    END LOOP;

    IF (i < BitsPerEnable-1) THEN
      -- Calculate HighBit and LowBit
      LowBit := LowBit + BitsPerSubWord;
      IF (LowBit > BitsPerWord) THEN
        LowBit := BitsPerWord;
      END IF;
      HighBit := LowBit + BitsPerSubWord;
      IF (HighBit > BitsPerWord) THEN
        HighBit := BitsPerWord;
      ELSE
        HighBit := HighBit - 1;
      END IF;
    END IF;

  END LOOP;   -- SubWordLoop

  DataOutBus := DataOutTmp;

  IF (DoWrCont) THEN
    IF (MsgOn) THEN
      PrintMemoryMessage(MsgVMCP,ErrMcpWrCont,HeaderMsg,PortName);
    END IF;
    WriteMemory(MemoryData,MemoryTmp,Address);
  END IF;

  IF (DoCpCont) THEN
    IF (MsgOn) THEN
      PrintMemoryMessage(MsgVMCP,ErrMcpCpCont,HeaderMsg,PortName);
    END IF;
    WriteMemory(MemoryData,MemoryTmp,Address);
  END IF;

  IF (DoCpRead) THEN
    IF (MsgOn) THEN
      PrintMemoryMessage(MsgVMCP,ErrMcpCpRead,HeaderMsg,PortName);
    END IF;
  END IF;

  IF (DoRdWrCont) THEN
    IF (MsgOn) THEN
      PrintMemoryMessage(MsgVMCP,ErrMcpRdWrCo,HeaderMsg,PortName);
    END IF;
  END IF;

END VitalMemoryCrossPorts;

-- ----------------------------------------------------------------------------
PROCEDURE VitalMemoryCrossPorts (
  VARIABLE MemoryData             : INOUT VitalMemoryDataType;
  CONSTANT CrossPortFlagArray     : IN VitalPortFlagVectorType;
  CONSTANT CrossPortAddressArray  : IN VitalAddressValueVectorType;
  CONSTANT HeaderMsg              : IN STRING := "";
  CONSTANT MsgOn                  : IN BOOLEAN := TRUE
) IS

  VARIABLE BitsPerWord    : NATURAL := MemoryData.NoOfBitsPerWord;
  VARIABLE BitsPerSubWord : NATURAL := MemoryData.NoOfBitsPerSubWord;
  VARIABLE BitsPerEnable  : NATURAL := MemoryData.NoOfBitsPerEnable;
  VARIABLE MemoryTmp  : std_logic_vector(BitsPerWord-1 DOWNTO 0);
  VARIABLE CrossPorts : NATURAL := CrossPortAddressArray'LENGTH;
  VARIABLE LowBit     : NATURAL := 0;
  VARIABLE HighBit    : NATURAL := BitsPerSubWord-1;
  VARIABLE AddressJ   : VitalAddressValueType;
  VARIABLE AddressK   : VitalAddressValueType;
  VARIABLE PortFlagIJ : VitalPortFlagType;
  VARIABLE PortFlagIK : VitalPortFlagType;
  VARIABLE CpWrCont   : BOOLEAN := FALSE;

BEGIN

  SubWordLoop: -- For each slice of the sub-word I
  FOR i IN 0 TO BitsPerEnable-1 LOOP

    -- For each cross port J: check with each cross port K
    FOR j IN 0 TO CrossPorts-1 LOOP
      PortFlagIJ := CrossPortFlagArray(i+j*BitsPerEnable);
      AddressJ := CrossPortAddressArray(j);
      -- Check for error in address
      IF (AddressJ < 0) THEN
        NEXT;
      END IF;
      ReadMemory(MemoryData,MemoryTmp,AddressJ);
      -- For each cross port K
      FOR k IN 0 TO CrossPorts-1 LOOP
        IF (k <= j) THEN
          NEXT;
        END IF;
        PortFlagIK := CrossPortFlagArray(i+k*BitsPerEnable);
        AddressK := CrossPortAddressArray(k);
        -- Check for error in address
        IF (AddressK < 0) THEN
          NEXT;
        END IF;
        CpWrCont := ( (AddressJ   = AddressK) AND
                      (PortFlagIJ.MemoryCurrent = WRITE)    AND 
                      (PortFlagIK.MemoryCurrent = WRITE) ) OR
                    ( (PortFlagIJ.MemoryCurrent = WRITE)    AND
                      (PortFlagIK.MemoryCurrent = CORRUPT) ) OR
                    ( (PortFlagIJ.MemoryCurrent = CORRUPT) AND
                      (PortFlagIK.MemoryCurrent = WRITE) ) OR
                    ( (PortFlagIJ.MemoryCurrent = CORRUPT) AND
                      (PortFlagIK.MemoryCurrent = CORRUPT) ) ;
        IF (CpWrCont) THEN
          -- Corrupt memory only
          MemoryTmp(HighBit DOWNTO LowBit) := (OTHERS => 'X');
          EXIT;
        END IF;
      END LOOP;   -- FOR k IN 0 TO CrossPorts-1 LOOP
      IF (CpWrCont = TRUE) THEN
        IF (MsgOn) THEN
          PrintMemoryMessage(MsgVMCP,ErrMcpCpWrCont,HeaderMsg);
        END IF;
        WriteMemory(MemoryData,MemoryTmp,AddressJ);
      END IF;
    END LOOP;   -- FOR j IN 0 TO CrossPorts-1 LOOP

    IF (i < BitsPerEnable-1) THEN
      -- Calculate HighBit and LowBit
      LowBit := LowBit + BitsPerSubWord;
      IF (LowBit > BitsPerWord) THEN
        LowBit := BitsPerWord;
      END IF;
      HighBit := LowBit + BitsPerSubWord;
      IF (HighBit > BitsPerWord) THEN
        HighBit := BitsPerWord;
      ELSE
        HighBit := HighBit - 1;
      END IF;
    END IF;
  END LOOP;   -- SubWordLoop

END VitalMemoryCrossPorts;


-- ----------------------------------------------------------------------------
-- Procedure:   VitalMemoryViolation
-- Parameters:  DataOutBus        - Output zero delay data bus out
--              MemoryData        - Pointer to memory data structure
--              PortFlag          - Indicates port operating mode
--              TimingDataArray   - This is currently not used (comment out) 
--              ViolationArray    - Aggregation of violation variables
--              DataInBus         - Input value of data bus in
--              AddressBus        - Input value of address bus in
--              AddressValue      - Decoded value of the AddressBus
--              ViolationTable    - Input memory violation table
--              PortName          - Port name string for messages
--              HeaderMsg         - Header string for messages
--              MsgOn             - Control the generation of messages
--              MsgSeverity       - Control level of message generation
-- Description: This procedure is intended to implement all actions on the
--              memory contents and data out bus as a result of timing viols.
--              It uses the memory action table to perform various corruption
--              policies specified by the user. 
-- ----------------------------------------------------------------------------

PROCEDURE VitalMemoryViolation (
  VARIABLE DataOutBus           : INOUT std_logic_vector;
  VARIABLE MemoryData           : INOUT VitalMemoryDataType;
  VARIABLE PortFlag             : INOUT VitalPortFlagVectorType;
  CONSTANT DataInBus            : IN std_logic_vector;
  CONSTANT AddressValue         : IN VitalAddressValueType;
  CONSTANT ViolationFlags       : IN std_logic_vector;
  CONSTANT ViolationFlagsArray  : IN X01ArrayT;
  CONSTANT ViolationSizesArray  : IN VitalMemoryViolFlagSizeType;
  CONSTANT ViolationTable       : IN VitalMemoryTableType;
  CONSTANT PortType             : IN VitalPortType; 
  CONSTANT PortName             : IN STRING := "";
  CONSTANT HeaderMsg            : IN STRING := "";
  CONSTANT MsgOn                : IN BOOLEAN := TRUE;
  CONSTANT MsgSeverity          : IN SEVERITY_LEVEL := WARNING
) IS

  VARIABLE BitsPerWord    : NATURAL := MemoryData.NoOfBitsPerWord;
  VARIABLE BitsPerSubWord : NATURAL := MemoryData.NoOfBitsPerSubWord;
  VARIABLE BitsPerEnable  : NATURAL := MemoryData.NoOfBitsPerEnable;
  VARIABLE DataOutTmp     : std_logic_vector(DataOutBus'RANGE)
                            := DataOutBus;
  VARIABLE MemoryAction   : VitalMemorySymbolType;
  VARIABLE DataAction     : VitalMemorySymbolType;
  -- VMT relies on the corrupt masks so HighBit/LowBit are full word
  VARIABLE HighBit        : NATURAL := BitsPerWord-1;
  VARIABLE LowBit         : NATURAL := 0;
  VARIABLE PortFlagTmp    : VitalPortFlagType;
  VARIABLE VFlagArrayTmp  : std_logic_vector 
                            (0 TO ViolationFlagsArray'LENGTH-1);
  VARIABLE MemCorruptMask : std_logic_vector (DataOutBus'RANGE);
  VARIABLE DatCorruptMask : std_logic_vector (DataOutBus'RANGE);

BEGIN

  -- Don't do anything if given an error address
  IF (AddressValue < 0) THEN
    RETURN;
  END IF;

  FOR i IN ViolationFlagsArray'RANGE LOOP
    VFlagArrayTmp(i) := ViolationFlagsArray(i);
  END LOOP;

  -- Lookup memory and data actions
  ViolationTableLookUp(  
    MemoryAction        => MemoryAction         ,
    DataAction          => DataAction           ,
    MemoryCorruptMask   => MemCorruptMask       ,
    DataCorruptMask     => DatCorruptMask       ,
    ViolationFlags      => ViolationFlags       ,
    ViolationFlagsArray => VFlagArrayTmp        ,
    ViolationSizesArray => ViolationSizesArray  ,
    ViolationTable      => ViolationTable       ,
    BitsPerWord         => BitsPerWord          ,
    BitsPerSubWord      => BitsPerSubWord       ,
    BitsPerEnable       => BitsPerEnable        ,
    PortName            => PortName             ,
    HeaderMsg           => HeaderMsg            ,
    MsgOn               => MsgOn            
  );

  -- Need to read incoming PF value (was not before)
  PortFlagTmp := PortFlag(0);

  IF (PortType = READ OR PortType = RDNWR) THEN
    -- Handle data action before memory action
    -- This allows reading previous memory contents
    HandleDataAction(
      DataOutBus      => DataOutTmp       ,
      MemoryData      => MemoryData       ,
      PortFlag        => PortFlagTmp      ,
      CorruptMask     => DatCorruptMask   ,
      DataInBus       => DataInBus        ,
      Address         => AddressValue     ,
      HighBit         => HighBit          ,
      LowBit          => LowBit           ,
      MemoryTable     => ViolationTable   ,
      DataAction      => DataAction       ,
      CallerName      => MsgVMV           ,
      PortName        => PortName         ,
      HeaderMsg       => HeaderMsg        ,
      MsgOn           => MsgOn            
    );
  END IF;

  IF (PortType = WRITE OR PortType = RDNWR) THEN
    HandleMemoryAction(
      MemoryData      => MemoryData       ,
      PortFlag        => PortFlagTmp      ,
      CorruptMask     => MemCorruptMask   ,
      DataInBus       => DataInBus        ,
      Address         => AddressValue     ,
      HighBit         => HighBit          ,
      LowBit          => LowBit           ,
      MemoryTable     => ViolationTable   ,
      MemoryAction    => MemoryAction     ,
      CallerName      => MsgVMV           ,
      PortName        => PortName         ,
      HeaderMsg       => HeaderMsg        ,
      MsgOn           => MsgOn            
    );
  END IF;

  -- Check if we need to turn off PF.OutputDisable
  IF (DataAction /= 'S') THEN
    PortFlagTmp.OutputDisable := FALSE;
    -- Set the output PortFlag(0) value 
    -- Note that all bits of PortFlag get PortFlagTmp
    FOR i IN PortFlag'RANGE LOOP
      PortFlag(i) := PortFlagTmp;
    END LOOP;
  END IF;

  -- Set the candidate zero delay return value
  DataOutBus := DataOutTmp;

END;

PROCEDURE VitalMemoryViolation (
  VARIABLE DataOutBus           : INOUT std_logic_vector;
  VARIABLE MemoryData           : INOUT VitalMemoryDataType;
  VARIABLE PortFlag             : INOUT VitalPortFlagVectorType;
  CONSTANT DataInBus            : IN std_logic_vector;
  CONSTANT AddressValue         : IN VitalAddressValueType;
  CONSTANT ViolationFlags       : IN std_logic_vector;
  CONSTANT ViolationTable       : IN VitalMemoryTableType;
  CONSTANT PortType             : IN VitalPortType; 
  CONSTANT PortName             : IN STRING := "";
  CONSTANT HeaderMsg            : IN STRING := "";
  CONSTANT MsgOn                : IN BOOLEAN := TRUE;
  CONSTANT MsgSeverity          : IN SEVERITY_LEVEL := WARNING
) IS

  VARIABLE VFlagArrayTmp  : X01ArrayT (0 TO 0);

BEGIN

  VitalMemoryViolation (
    DataOutBus              => DataOutBus       ,
    MemoryData              => MemoryData       ,
    PortFlag                => PortFlag         ,
    DataInBus               => DataInBus        ,
    AddressValue            => AddressValue     ,
    ViolationFlags          => ViolationFlags   ,
    ViolationFlagsArray     => VFlagArrayTmp    ,
    ViolationSizesArray     => ( 0 => 0 )       ,
    ViolationTable          => ViolationTable   ,
    PortType                => PortType         ,
    PortName                => PortName         ,
    HeaderMsg               => HeaderMsg        ,
    MsgOn                   => MsgOn            ,
    MsgSeverity             => MsgSeverity
  );

END;

END Vital_Memory ;