--
--  File Name:         MemoryPkg.vhd
--  Design Unit Name:  MemoryPkg
--  Revision:          STANDARD VERSION
--
--  Maintainer:        Jim Lewis      email:  jim@synthworks.com 
--  Contributor(s):            
--     Jim Lewis      email:  jim@synthworks.com   
--
--  Description
--      Package defines a protected type, MemoryPType, and methods  
--      for efficiently implementing memory data structures
--    
--  Developed for: 
--        SynthWorks Design Inc. 
--        VHDL Training Classes
--        11898 SW 128th Ave.  Tigard, Or  97223
--        http://www.SynthWorks.com
--
--  Revision History:
--    Date      Version    Description
--    05/2005:  0.1        Initial revision
--    06/2015:  2015.06    Updated for Alerts, ...
--                         Numerous revisions for VHDL Testbenches and Verification
--    01/2016:  2016.01    Update for buf.all(buf'left)
--    11/2016:  2016.11    Refinement to MemRead to return value, X (if X), U (if not initialized)
--
--
--  Copyright (c) 2005 - 2016 by SynthWorks Design Inc.  All rights reserved.
--
--  Verbatim copies of this source file may be used and 
--  distributed without restriction.   
-- 								 
--  This source file is free software; you can redistribute it  
--  and/or modify it under the terms of the ARTISTIC License 
--  as published by The Perl Foundation; either version 2.0 of 
--  the License, or (at your option) any later version. 						 
-- 								 
--  This source is distributed in the hope that it will be 	 
--  useful, but WITHOUT ANY WARRANTY; without even the implied  
--  warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR 	 
--  PURPOSE. See the Artistic License for details. 							 
-- 								 
--  You should have received a copy of the license with this source.
--  If not download it from, 
--     http://www.perlfoundation.org/artistic_license_2_0
--
use std.textio.all ;
library IEEE ; 
  use IEEE.std_logic_1164.all ; 
  use IEEE.numeric_std.all ; 
  use IEEE.numeric_std_unsigned.all ; 
  use IEEE.math_real.all ;
  
use work.TextUtilPkg.all ;
use work.TranscriptPkg.all ;  
use work.AlertLogPkg.all ;

package MemoryPkg is
  type MemoryPType is protected 
    ------------------------------------------------------------
    procedure MemInit ( AddrWidth, DataWidth  : in  integer ) ;
    
    ------------------------------------------------------------
    procedure MemWrite ( Addr, Data  : in  std_logic_vector ) ; 

    ------------------------------------------------------------
    procedure MemRead (  
      Addr  : in  std_logic_vector ;
      Data  : out std_logic_vector 
    ) ; 
    impure function MemRead ( Addr  : std_logic_vector ) return std_logic_vector ; 

    ------------------------------------------------------------
    procedure MemErase ; 
    procedure deallocate ; 
    
    ------------------------------------------------------------
    procedure SetAlertLogID (A : AlertLogIDType) ;
    procedure SetAlertLogID (Name : string ; ParentID : AlertLogIDType := ALERTLOG_BASE_ID ; CreateHierarchy : Boolean := TRUE) ;    
    impure function GetAlertLogID return AlertLogIDType ;
    
    ------------------------------------------------------------
    procedure FileReadH (    -- Hexadecimal File Read 
      FileName     : string ; 
      StartAddr    : std_logic_vector ; 
      EndAddr      : std_logic_vector
    ) ;
    procedure FileReadH (FileName : string ;  StartAddr : std_logic_vector) ;
    procedure FileReadH (FileName : string) ; 

    ------------------------------------------------------------
    procedure FileReadB (    -- Binary File Read 
      FileName     : string ; 
      StartAddr    : std_logic_vector ; 
      EndAddr      : std_logic_vector
    ) ;
    procedure FileReadB (FileName : string ;  StartAddr : std_logic_vector) ;
    procedure FileReadB (FileName : string) ; 

    ------------------------------------------------------------
    procedure FileWriteH (    -- Hexadecimal File Write 
      FileName     : string ; 
      StartAddr    : std_logic_vector ; 
      EndAddr      : std_logic_vector
    ) ;
    procedure FileWriteH (FileName : string ;  StartAddr : std_logic_vector) ;
    procedure FileWriteH (FileName : string) ; 

    ------------------------------------------------------------
    procedure FileWriteB (    -- Binary File Write 
      FileName     : string ; 
      StartAddr    : std_logic_vector ; 
      EndAddr      : std_logic_vector
    ) ;
    procedure FileWriteB (FileName : string ;  StartAddr : std_logic_vector) ;
    procedure FileWriteB (FileName : string) ; 

  end protected MemoryPType ;

end MemoryPkg ;

package body MemoryPkg is 
  constant BLOCK_WIDTH : integer := 10 ; 

  type MemoryPType is protected body

    type MemBlockType    is array (integer range <>) of integer ;
    type MemBlockPtrType is access MemBlockType ;
    type MemArrayType    is array (integer range <>) of MemBlockPtrType ;
    type ArrayPtrVarType is access MemArrayType ; 

    variable ArrayPtrVar     : ArrayPtrVarType := NULL ; 
    variable AddrWidthVar    : integer := -1 ;  -- set by MemInit - merges addr length and initialized checks.
    variable DataWidthVar    : natural := 1 ;   -- set by MemInit
    variable BlockkWidthVar  : natural := 0 ;   -- set by MemInit
    
    variable AlertLogIDVar : AlertLogIDType := OSVVM_ALERTLOG_ID ;
    
    type FileFormatType is (BINARY, HEX) ; 
    
    ------------------------------------------------------------
    procedure MemInit ( AddrWidth, DataWidth  : In  integer ) is
    ------------------------------------------------------------
    begin
      if AddrWidth <= 0 then 
        Alert(AlertLogIDVar, "MemoryPType.MemInit.  AddrWidth = " & to_string(AddrWidth) & " must be > 0.", FAILURE) ; 
        return ; 
      end if ; 
      if DataWidth <= 0 then 
        Alert(AlertLogIDVar, "MemoryPType.MemInit.  DataWidth = " & to_string(DataWidth) & " must be > 0.", FAILURE) ; 
        return ; 
      end if ; 

      AddrWidthVar   := AddrWidth ; 
      DataWidthVar   := DataWidth ; 
      BlockkWidthVar := minimum(BLOCK_WIDTH, AddrWidth) ;
      ArrayPtrVar    := new MemArrayType(0 to 2**(AddrWidth-BlockkWidthVar)-1) ;  
    end procedure MemInit ;
    
    ------------------------------------------------------------
    procedure MemWrite (  Addr, Data  : in  std_logic_vector ) is 
    ------------------------------------------------------------
      variable BlockAddr, WordAddr  : integer ;
      alias aAddr : std_logic_vector (Addr'length-1 downto 0) is Addr ; 
    begin
    
      -- Check Bounds of Address and if memory is initialized
      if Addr'length /= AddrWidthVar then
        if (ArrayPtrVar = NULL) then 
          Alert(AlertLogIDVar, "MemoryPType.MemWrite:  Memory not initialized, Write Ignored.", FAILURE) ; 
        else
          Alert(AlertLogIDVar, "MemoryPType.MemWrite:  Addr'length: " & to_string(Addr'length) & " /= Memory Address Width: " & to_string(AddrWidthVar), FAILURE) ; 
        end if ; 
        return ; 
      end if ; 
      
      -- Check Bounds on Data
      if Data'length /= DataWidthVar then
        Alert(AlertLogIDVar, "MemoryPType.MemWrite:  Data'length: " & to_string(Data'length) & " /= Memory Data Width: " & to_string(DataWidthVar), FAILURE) ; 
        return ; 
      end if ; 

      if is_X( Addr ) then
        Alert(AlertLogIDVar, "MemoryPType.MemWrite:  Address X, Write Ignored.") ; 
        return ;
      end if ; 

      -- Slice out upper address to form block address
      if aAddr'high >= BlockkWidthVar then
        BlockAddr := to_integer(aAddr(aAddr'high downto BlockkWidthVar)) ;
      else
        BlockAddr  := 0 ; 
      end if ; 

      -- If empty, allocate a memory block
      if (ArrayPtrVar(BlockAddr) = NULL) then 
          ArrayPtrVar(BlockAddr) := new MemBlockType(0 to 2**BlockkWidthVar-1) ;
      end if ; 

      -- Address of a word within a block
      WordAddr  := to_integer(aAddr(BlockkWidthVar -1 downto 0)) ;

      -- Write to BlockAddr, WordAddr
      if (Is_X(Data)) then 
        ArrayPtrVar(BlockAddr)(WordAddr) := -1 ;
      else
        ArrayPtrVar(BlockAddr)(WordAddr) := to_integer( Data ) ;
      end if ;
    end procedure MemWrite ; 

    ------------------------------------------------------------
    procedure MemRead (  
    ------------------------------------------------------------
      Addr  : In   std_logic_vector ;
      Data  : Out  std_logic_vector 
    ) is
      variable BlockAddr, WordAddr  : integer ;
      alias aAddr : std_logic_vector (Addr'length-1 downto 0) is Addr ; 
    begin
      -- Check Bounds of Address and if memory is initialized
      if Addr'length /= AddrWidthVar then
        if (ArrayPtrVar = NULL) then 
          Alert(AlertLogIDVar, "MemoryPType.MemRead:  Memory not initialized. Returning U", FAILURE) ; 
        else
          Alert(AlertLogIDVar, "MemoryPType.MemRead:  Addr'length: " & to_string(Addr'length) & " /= Memory Address Width: " & to_string(AddrWidthVar), FAILURE) ; 
        end if ; 
        Data := (Data'range => 'U') ; 
        return ; 
      end if ; 
      
      -- Check Bounds on Data
      if Data'length /= DataWidthVar then
        Alert(AlertLogIDVar, "MemoryPType.MemRead:  Data'length: " & to_string(Data'length) & " /= Memory Data Width: " & to_string(DataWidthVar), FAILURE) ; 
        Data := (Data'range => 'U') ; 
        return ; 
      end if ; 

      -- If Addr X, data = X
      if is_X( aAddr ) then
        Data := (Data'range => 'X') ; 
        return ; 
      end if ; 

      -- Slice out upper address to form block address
      if aAddr'high >= BlockkWidthVar then
        BlockAddr := to_integer(aAddr(aAddr'high downto BlockkWidthVar)) ;
      else
        BlockAddr  := 0 ; 
      end if ; 
      
      -- Empty Block, return all U
      if (ArrayPtrVar(BlockAddr) = NULL) then 
        Data := (Data'range => 'U') ; 
        return ; 
      end if ; 

      -- Address of a word within a block
      WordAddr := to_integer(aAddr(BlockkWidthVar -1 downto 0)) ;

      if ArrayPtrVar(BlockAddr)(WordAddr) >= 0 then 
        -- Get the Word from the Array
        Data := to_slv(ArrayPtrVar(BlockAddr)(WordAddr), Data'length) ;

      elsif ArrayPtrVar(BlockAddr)(WordAddr) = -1 then 
       -- X in Word, return all X
        Data := (Data'range => 'X') ;

      else 
       -- Location Uninitialized, return all X
        Data := (Data'range => 'U') ;

      end if ;
    end procedure MemRead ; 

    ------------------------------------------------------------
    impure function MemRead ( Addr  : std_logic_vector ) return std_logic_vector is
    ------------------------------------------------------------
      variable BlockAddr, WordAddr  : integer ;
      alias    aAddr : std_logic_vector (Addr'length-1 downto 0) is Addr ; 
      variable Data  : std_logic_vector(DataWidthVar-1 downto 0) ; 
    begin
      MemRead(Addr, Data) ; 
      return Data ; 
    end function MemRead ; 

    ------------------------------------------------------------
    procedure MemErase is 
    -- Deallocate the memory, but not the array of pointers
    ------------------------------------------------------------
    begin
      for BlockAddr in ArrayPtrVar'range loop 
        if (ArrayPtrVar(BlockAddr) /= NULL) then 
          deallocate (ArrayPtrVar(BlockAddr)) ; 
        end if ; 
      end loop ; 
    end procedure ; 
    
    ------------------------------------------------------------
    procedure deallocate is 
    -- Deallocate all allocated memory
    ------------------------------------------------------------
    begin
      MemErase ; 
      deallocate(ArrayPtrVar) ; 
      AddrWidthVar   := -1 ;
      DataWidthVar   := 1 ;
      BlockkWidthVar  := 0 ;
    end procedure ; 

    ------------------------------------------------------------
    procedure SetAlertLogID (A : AlertLogIDType) is
    ------------------------------------------------------------
    begin
      AlertLogIDVar := A ;
    end procedure SetAlertLogID ;

    ------------------------------------------------------------
    procedure SetAlertLogID(Name : string ; ParentID : AlertLogIDType := ALERTLOG_BASE_ID ; CreateHierarchy : Boolean := TRUE) is
    ------------------------------------------------------------
    begin
      AlertLogIDVar := GetAlertLogID(Name, ParentID, CreateHierarchy) ;
    end procedure SetAlertLogID ;
    
    ------------------------------------------------------------
    impure function GetAlertLogID return AlertLogIDType is
    ------------------------------------------------------------
    begin
      return AlertLogIDVar ; 
    end function GetAlertLogID ;
    
    ------------------------------------------------------------
    -- PT Local
    procedure FileReadX (
    -- Hexadecimal or Binary File Read 
    ------------------------------------------------------------
      FileName     : string ;
      DataFormat   : FileFormatType ; 
      StartAddr    : std_logic_vector ; 
      EndAddr      : std_logic_vector
    ) is
      -- Format:  
      --  @hh..h     -- Address in hex
      --  hhh_XX_ZZ  -- data values in hex - space delimited 
      --  "--" or "//" -- comments
     file MemFile : text open READ_MODE is FileName ;

      variable Addr             : std_logic_vector(AddrWidthVar - 1 downto 0) ;
      variable SmallAddr        : std_logic_vector(AddrWidthVar - 1 downto 0) ;
      variable BigAddr          : std_logic_vector(AddrWidthVar - 1 downto 0) ;
      variable Data             : std_logic_vector(DataWidthVar - 1 downto 0) ;
      variable LineNum          : natural ; 
      variable ItemNum          : natural ; 
      variable AddrInc          : std_logic_vector(AddrWidthVar - 1 downto 0) ; 
      variable buf              : line ;
      variable ReadValid        : boolean ;
      variable Empty            : boolean ; 
      variable MultiLineComment : boolean ; 
      variable NextChar         : character ; 
      variable StrLen           : integer ; 
    begin
      MultiLineComment := FALSE ; 
      if StartAddr'length /= AddrWidthVar and EndAddr'length /= AddrWidthVar then
        if (ArrayPtrVar = NULL) then 
          Alert(AlertLogIDVar, "MemoryPType.FileReadX:  Memory not initialized, FileRead Ignored.", FAILURE) ; 
        else
          Alert(AlertLogIDVar, "MemoryPType.FileReadX:  Addr'length: " & to_string(Addr'length) & " /= Memory Address Width: " & to_string(AddrWidthVar), FAILURE) ; 
        end if ; 
        return ; 
      end if ; 

      Addr    := StartAddr ; 
      LineNum := 0 ; 
      
      if StartAddr <= EndAddr then 
        SmallAddr := StartAddr ; 
        BigAddr   := EndAddr ; 
        AddrInc   := (AddrWidthVar -1 downto 0 => '0') + 1 ;  
      else
        SmallAddr := EndAddr ; 
        BigAddr   := StartAddr ; 
        AddrInc   := (others => '1') ;  -- -1
      end if; 
      
      ReadLineLoop : while not EndFile(MemFile) loop
        ReadLine(MemFile, buf) ;
        LineNum := LineNum + 1 ; 
        ItemNum := 0 ; 
        
        ItemLoop : loop 
          EmptyOrCommentLine(buf, Empty, MultiLineComment) ; 
          exit ItemLoop when Empty ; 
          ItemNum := ItemNum + 1 ; 
          NextChar := buf.all(buf'left) ;
          
          if (NextChar = '@') then 
          -- Get Address
            read(buf, NextChar) ; 
            ReadHexToken(buf, Addr, StrLen) ; 
            exit ReadLineLoop when AlertIf(AlertLogIDVar, StrLen = 0, "MemoryPType.FileReadX: Address length 0 on line: " & to_string(LineNum), FAILURE) ;
            exit ItemLoop when AlertIf(AlertLogIDVar, Addr < SmallAddr, 
                                           "MemoryPType.FileReadX: Address in file: " & to_hstring(Addr) & 
                                           " < StartAddr: " & to_hstring(StartAddr) & " on line: " & to_string(LineNum)) ; 
            exit ItemLoop when AlertIf(AlertLogIDVar, Addr > BigAddr, 
                                           "MemoryPType.FileReadX: Address in file: " & to_hstring(Addr) & 
                                           " > EndAddr: " & to_hstring(BigAddr) & " on line: " & to_string(LineNum)) ; 
          
          elsif DataFormat = HEX and ishex(NextChar) then 
          -- Get Hex Data
            ReadHexToken(buf, data, StrLen) ;
            exit ReadLineLoop when AlertIfNot(AlertLogIDVar, StrLen > 0, 
              "MemoryPType.FileReadH: Error while reading data on line: " & to_string(LineNum) &
              "  Item number: " & to_string(ItemNum), FAILURE) ;
            log("MemoryPType.FileReadX:  MemWrite(Addr => " & to_hstring(Addr) & ", Data => " & to_hstring(Data) & ")", DEBUG) ; 
            MemWrite(Addr, data) ; 
            Addr := Addr + AddrInc ; 
            
          elsif DataFormat = BINARY and isstd_logic(NextChar) then 
          -- Get Binary Data
            -- read(buf, data, ReadValid) ;
            ReadBinaryToken(buf, data, StrLen) ;
            -- exit ReadLineLoop when AlertIfNot(AlertLogIDVar, ReadValid, 
            exit ReadLineLoop when AlertIfNot(AlertLogIDVar, StrLen > 0, 
              "MemoryPType.FileReadB: Error while reading data on line: " & to_string(LineNum) &
              "  Item number: " & to_string(ItemNum), FAILURE) ;
            log("MemoryPType.FileReadX:  MemWrite(Addr => " & to_hstring(Addr) & ", Data => " & to_string(Data) & ")", DEBUG) ; 
            MemWrite(Addr, data) ; 
            Addr := Addr + AddrInc ; 
          
          else
          -- Invalid Text, Issue Warning and skip it
            Alert(AlertLogIDVar,  
              "MemoryPType.FileReadX: Invalid text on line: " & to_string(LineNum) &
              "  Item: " & to_string(ItemNum) & ".  Skipping text: " & buf.all) ;
            exit ItemLoop ; 
          end if ; 
          
        end loop ItemLoop ; 
      end loop ReadLineLoop ; 
      
--      -- must read EndAddr-StartAddr number of words if both start and end specified
--      if (StartAddr /= 0 or (not EndAddr) /= 0) and (Addr /= EndAddr) then 
--        Alert("MemoryPType.FileReadH: insufficient data values", WARNING) ; 
--      end if ;       
      file_close(MemFile) ; 
    end FileReadX ;
    
    ------------------------------------------------------------
    procedure FileReadH (
    -- Hexadecimal File Read 
    ------------------------------------------------------------
      FileName     : string ; 
      StartAddr    : std_logic_vector ; 
      EndAddr      : std_logic_vector
    ) is
    begin
      FileReadX(FileName, HEX, StartAddr, EndAddr) ; 
    end FileReadH ;
    
    ------------------------------------------------------------
    procedure FileReadH (FileName : string ;  StartAddr : std_logic_vector) is
    -- Hexadecimal File Read 
    ------------------------------------------------------------
      constant EndAddr : std_logic_vector(AddrWidthVar - 1 downto 0) := (others => '1') ;
    begin
      FileReadX(FileName, HEX, StartAddr, EndAddr) ; 
    end FileReadH ;

    ------------------------------------------------------------
    procedure FileReadH (FileName : string) is 
    -- Hexadecimal File Read 
    ------------------------------------------------------------
      constant StartAddr : std_logic_vector(AddrWidthVar - 1 downto 0) := (others => '0') ;
      constant EndAddr   : std_logic_vector(AddrWidthVar - 1 downto 0) := (others => '1') ;
    begin
      FileReadX(FileName, HEX, StartAddr, EndAddr) ; 
    end FileReadH ;    
    
     ------------------------------------------------------------
    procedure FileReadB (
    -- Binary File Read 
    ------------------------------------------------------------
      FileName     : string ; 
      StartAddr    : std_logic_vector ; 
      EndAddr      : std_logic_vector
    ) is
    begin
      FileReadX(FileName, BINARY, StartAddr, EndAddr) ; 
    end FileReadB ;
    
    ------------------------------------------------------------
    procedure FileReadB (FileName : string ;  StartAddr : std_logic_vector) is
    -- Binary File Read 
    ------------------------------------------------------------
      constant EndAddr : std_logic_vector(AddrWidthVar - 1 downto 0) := (others => '1') ;
    begin
      FileReadX(FileName, BINARY, StartAddr, EndAddr) ; 
    end FileReadB ;

    ------------------------------------------------------------
    procedure FileReadB (FileName : string) is 
    -- Binary File Read 
    ------------------------------------------------------------
      constant StartAddr : std_logic_vector(AddrWidthVar - 1 downto 0) := (others => '0') ;
      constant EndAddr   : std_logic_vector(AddrWidthVar - 1 downto 0) := (others => '1') ;
    begin
      FileReadX(FileName, BINARY, StartAddr, EndAddr) ; 
    end FileReadB ;    

    ------------------------------------------------------------
    -- PT Local
    procedure FileWriteX (
    -- Hexadecimal or Binary File Write 
    ------------------------------------------------------------
      FileName     : string ; 
      DataFormat   : FileFormatType ; 
      StartAddr    : std_logic_vector ; 
      EndAddr      : std_logic_vector
    ) is
      -- Format:  
      --  @hh..h     -- Address in hex
      --  hhhhh      -- data one per line in either hex or binary as specified 
      file MemFile : text open WRITE_MODE is FileName ;
      alias normStartAddr     : std_logic_vector(StartAddr'length-1 downto 0) is StartAddr ; 
      alias normEndAddr       : std_logic_vector(EndAddr'length-1 downto 0) is EndAddr ; 
      variable StartBlockAddr : natural ;
      variable EndBlockAddr   : natural ;
      variable StartWordAddr  : natural ; 
      variable EndWordAddr    : natural ; 
      variable Data           : std_logic_vector(DataWidthVar - 1 downto 0) ;
      variable FoundData      : boolean ; 
      variable buf            : line ;
    begin
      if StartAddr'length /= AddrWidthVar and EndAddr'length /= AddrWidthVar then
      -- Check StartAddr and EndAddr Widths and Memory not initialized
        if (ArrayPtrVar = NULL) then 
          Alert(AlertLogIDVar, "MemoryPType.FileWriteX:  Memory not initialized, FileRead Ignored.", FAILURE) ; 
        else
          AlertIf(AlertLogIDVar, StartAddr'length /= AddrWidthVar, "MemoryPType.FileWriteX:  StartAddr'length: " 
                               & to_string(StartAddr'length) & 
                               " /= Memory Address Width: " & to_string(AddrWidthVar), FAILURE) ; 
          AlertIf(AlertLogIDVar, EndAddr'length /= AddrWidthVar, "MemoryPType.FileWriteX:  EndAddr'length: " 
                               & to_string(EndAddr'length) & 
                               " /= Memory Address Width: " & to_string(AddrWidthVar), FAILURE) ; 
        end if ; 
        return ; 
      end if ; 

      if StartAddr > EndAddr then 
      -- Only support ascending addresses
        Alert(AlertLogIDVar, "MemoryPType.FileWriteX:  StartAddr: " & to_hstring(StartAddr) & 
                             " > EndAddr: " & to_hstring(EndAddr), FAILURE) ;
        return ; 
      end if ; 
            
      -- Slice out upper address to form block address
      if AddrWidthVar >= BlockkWidthVar then
        StartBlockAddr := to_integer(normStartAddr(AddrWidthVar-1 downto BlockkWidthVar)) ;
        EndBlockAddr   := to_integer(  normEndAddr(AddrWidthVar-1 downto BlockkWidthVar)) ;
      else
        StartBlockAddr  := 0 ; 
        EndBlockAddr  := 0 ; 
      end if ; 
            
      BlockAddrLoop : for BlockAddr in StartBlockAddr to EndBlockAddr loop 
        next BlockAddrLoop when ArrayPtrVar(BlockAddr) = NULL ;  
        if BlockAddr = StartBlockAddr then 
          StartWordAddr := to_integer(normStartAddr(BlockkWidthVar-1 downto 0)) ; 
        else
          StartWordAddr := 0 ;
        end if ; 
        if BlockAddr = EndBlockAddr then 
          EndWordAddr := to_integer(normEndAddr(BlockkWidthVar-1 downto 0)) ; 
        else 
          EndWordAddr := 2**BlockkWidthVar-1 ;
        end if ; 
        FoundData := FALSE ; 
        WordAddrLoop : for WordAddr in StartWordAddr to EndWordAddr loop 
          if (ArrayPtrVar(BlockAddr)(WordAddr) < 0) then 
            -- X in Word, return all X
            Data := (Data'range => 'X') ;
            FoundData := FALSE ;
          else 
            -- Get the Word from the Array
            Data := to_slv(ArrayPtrVar(BlockAddr)(WordAddr), Data'length) ;
            if not FoundData then
              -- Write Address
              write(buf, '@') ; 
              hwrite(buf, to_slv(BlockAddr, AddrWidthVar-BlockkWidthVar) & to_slv(WordAddr, BlockkWidthVar)) ; 
              writeline(MemFile, buf) ; 
            end if ; 
            FoundData := TRUE ; 
          end if ;
          if FoundData then  -- Write Data
            if DataFormat = HEX then
              hwrite(buf, Data) ; 
              writeline(MemFile, buf) ; 
            else
              write(buf, Data) ; 
              writeline(MemFile, buf) ; 
            end if; 
          end if ;                
        end loop WordAddrLoop ; 
      end loop BlockAddrLoop ;       
      file_close(MemFile) ; 
    end FileWriteX ;
    
    ------------------------------------------------------------
    procedure FileWriteH (
    -- Hexadecimal File Write 
    ------------------------------------------------------------
      FileName     : string ; 
      StartAddr    : std_logic_vector ; 
      EndAddr      : std_logic_vector
    ) is
    begin
      FileWriteX(FileName, HEX, StartAddr, EndAddr) ; 
    end FileWriteH ;
    
    ------------------------------------------------------------
    procedure FileWriteH (FileName : string ;  StartAddr : std_logic_vector) is
    -- Hexadecimal File Write 
    ------------------------------------------------------------
      constant EndAddr : std_logic_vector(AddrWidthVar - 1 downto 0) := (others => '1') ;
    begin
      FileWriteX(FileName, HEX, StartAddr, EndAddr) ; 
    end FileWriteH ;

    ------------------------------------------------------------
    procedure FileWriteH (FileName : string) is 
    -- Hexadecimal File Write 
    ------------------------------------------------------------
      constant StartAddr : std_logic_vector(AddrWidthVar - 1 downto 0) := (others => '0') ;
      constant EndAddr   : std_logic_vector(AddrWidthVar - 1 downto 0) := (others => '1') ;
    begin
      FileWriteX(FileName, HEX, StartAddr, EndAddr) ; 
    end FileWriteH ;    
    
     ------------------------------------------------------------
    procedure FileWriteB (
    -- Binary File Write 
    ------------------------------------------------------------
      FileName     : string ; 
      StartAddr    : std_logic_vector ; 
      EndAddr      : std_logic_vector
    ) is
    begin
      FileWriteX(FileName, BINARY, StartAddr, EndAddr) ; 
    end FileWriteB ;
    
    ------------------------------------------------------------
    procedure FileWriteB (FileName : string ;  StartAddr : std_logic_vector) is
    -- Binary File Write 
    ------------------------------------------------------------
      constant EndAddr : std_logic_vector(AddrWidthVar - 1 downto 0) := (others => '1') ;
    begin
      FileWriteX(FileName, BINARY, StartAddr, EndAddr) ; 
    end FileWriteB ;

    ------------------------------------------------------------
    procedure FileWriteB (FileName : string) is 
    -- Binary File Write 
    ------------------------------------------------------------
      constant StartAddr : std_logic_vector(AddrWidthVar - 1 downto 0) := (others => '0') ;
      constant EndAddr   : std_logic_vector(AddrWidthVar - 1 downto 0) := (others => '1') ;
    begin
      FileWriteX(FileName, BINARY, StartAddr, EndAddr) ; 
    end FileWriteB ;    
    
  end protected body MemoryPType ;
 
end MemoryPkg ;