diff --git a/hdl/gn4124core/sim/gn4124_bfm/Manifest.py b/hdl/gn4124core/sim/gn4124_bfm/Manifest.py
new file mode 100644
index 0000000000000000000000000000000000000000..a254ae992a97dc33da90aa07d86019e9d234ee52
--- /dev/null
+++ b/hdl/gn4124core/sim/gn4124_bfm/Manifest.py
@@ -0,0 +1,3 @@
+files = ["mem_model.vhd", "textutil.vhd", "gn412x_bfm.vhd", "util.vhd"]
+
+
diff --git a/hdl/gn4124core/sim/gn4124_bfm/gn4124_bfm.svh b/hdl/gn4124core/sim/gn4124_bfm/gn4124_bfm.svh
new file mode 100644
index 0000000000000000000000000000000000000000..518e3993c09b350d89fee59fac3e1cf00d6edbed
--- /dev/null
+++ b/hdl/gn4124core/sim/gn4124_bfm/gn4124_bfm.svh
@@ -0,0 +1,261 @@
+/* Crude wrapper for Gennum-provided GN4124x BFM. Supports only single CSR reads/writes so far. */
+
+`ifndef __GN4124_BFM_SVH
+ `define __GN4124_BFM_SVH 1
+
+`include "simdrv_defs.svh"
+
+interface IGN4124PCIMaster;
+
+   int cmd_str_int[256];
+   reg    cmd_req = 0;
+   wire cmd_ack;
+
+   reg  internal_rstn = 0;
+
+   wire lclk_p, lclk_n, l2p_clk_p, l2p_clk_n, p2l_clk_p, p2l_clk_n;
+   wire [15:0] l2p_data, p2l_data;
+   wire        l2p_dframe, l2p_valid, l2p_edb;
+   wire        p2l_dframe, p2l_valid, p2l_rdy;
+   
+
+   wire [1:0] l_wr_rdy, p_rd_d_rdy;
+   wire [1:0] p_wr_req, p_wr_rdy, vc_rdy;
+   wire       l2p_rdy, tx_error, rx_error;
+   wire [15:0] gpio;
+   
+ //  Local bus to Gennum
+   modport L2P
+     (
+
+      input  l2p_clk_n,
+      input  l2p_clk_p,
+      input  l2p_data,
+      input  l2p_dframe,
+      input  l2p_valid,
+      input  l2p_edb,
+
+      output l_wr_rdy, 
+      output p_rd_d_rdy,
+      output l2p_rdy, 
+      output tx_error
+      );
+   
+// Gennum to local bus
+   modport P2L 
+     (
+      output p2l_clk_p,
+      output p2l_clk_n,
+      output p2l_dframe,
+      output p2l_data,
+      output p2l_valid,
+      input  p2l_rdy,
+      output p_wr_req,
+      input  p_wr_rdy,
+      input  rx_error,
+      output vc_rdy
+      );
+
+   wire      rst_n;
+   
+   modport SYS
+     (
+      output lclk_p,
+      output lclk_n,
+      output rst_n,
+      inout  gpio);
+   
+   
+   wire [31:0] cmd_rddata;
+   wire        cmd_rddata_valid;
+   
+   
+GN412X_BFM 
+  U_BFM (
+         
+	 .CMD_INT                (cmd_str_int),
+	 .CMD_REQ            (cmd_req),
+	 .CMD_ACK            (cmd_ack),
+	 .CMD_CLOCK_EN       (1'b1),
+         .CMD_RD_DATA(cmd_rddata),
+         .CMD_RD_DATA_VALID(cmd_rddata_valid),
+
+	 .RSTINn             (internal_rstn),
+         .RSTOUT33n(rst_n),
+	 .LCLK (lclk_p),
+         .LCLKn (lclk_n),
+         
+	 .L2P_CLKp (l2p_clk_p),
+         .L2P_CLKn (l2p_clk_n),
+
+	 .L2P_DATA   (l2p_data),        
+	 .L2P_DFRAME (l2p_dframe),
+	 .L2P_VALID  (l2p_valid),     
+	 .L2P_EDB   (l2p_edb),
+
+
+	 .L_WR_RDY       (l_wr_rdy), 
+	 .P_RD_D_RDY    (p_rd_d_rdy),
+	 .L2P_RDY        (l2p_rdy),
+	 .TX_ERROR       (tx_error),
+
+
+	 .P2L_CLKp (p2l_clk_p),
+         .P2L_CLKn (p2l_clk_n),
+
+	 .P2L_DATA         (p2l_data),
+	 .P2L_DFRAME       (p2l_dframe),
+	 .P2L_VALID        (p2l_valid),
+	 .P2L_RDY	   (p2l_rdy),
+	 .P_WR_REQ         (p_wr_req),
+	 .P_WR_RDY         (p_wr_rdy),
+	 .RX_ERROR         (rx_error),
+	 .VC_RDY           (vc_rdy),
+
+	 .GPIO             (gpio)
+	);
+
+   int       line_no = 1;
+   
+   task send_cmd(string cmd);
+      int i;
+      string cmd_2;
+
+      $sformat(cmd_2, "%-1d %s", line_no++, cmd);
+      
+//      $display("SendCmd '%s'", cmd_2);
+      
+      for(i=0;i<cmd_2.len(); i++)
+        cmd_str_int[i] = int'(cmd_2[i]);
+      cmd_str_int[i] = 0;
+      
+      #10ns;
+      cmd_req = 1;
+      while(!cmd_ack) #1ns;
+      cmd_req = 0;
+      while(cmd_ack) #1ns;
+      #10ns;
+      
+      
+   endtask // send_cmd
+
+   bit       ready = 0;
+   
+
+   task init();
+      #100ns;
+      internal_rstn <= 1;
+      #100ns;
+      
+      send_cmd("init");
+      send_cmd("reset %d16");
+      send_cmd("bar 0 FF00000000000000 08000000 0 7 0");
+      send_cmd("bfm_bar 0 0000000040000000 20000000");
+      send_cmd("bfm_bar 1 0000000020000000 20000000");
+      send_cmd("wait %d64");
+      ready = 1;
+      
+    //  send_cmd("wr FF000000000A0004 F 007C0270");
+//      send_cmd("rd FF000000000A0004 F");
+   endtask // init
+
+   initial init();
+
+   task automatic readback(ref uint64_t value);
+      @(posedge cmd_rddata_valid);
+      value = cmd_rddata;
+      @(negedge cmd_rddata_valid);
+     endtask // readback
+   
+
+   
+class CBusAccessor_Gennum extends CBusAccessor;
+
+   function new();
+      
+   endfunction // new
+
+   task writem(uint64_t addr[], uint64_t data[], input int size, ref int result);
+      string cmd;
+      int    i;
+
+
+      if(size != 4)
+        size = 4;
+      
+      //  $fatal("CBusAccessor_Gennum: only size=4 supported");
+      
+      for(i=0;i<addr.size();i++)
+        begin
+           $sformat(cmd,"wr FF000000%08X F %08X", addr[i], data[i]);
+           send_cmd(cmd);
+        end
+   endtask // writem
+   
+   task readm(uint64_t addr[], ref uint64_t data[], input int size, ref int result);
+      string cmd;
+      int    i;
+      uint64_t tmp;
+      
+      
+
+      if(size != 4)
+        size = 4;
+      
+      //  $fatal("CBusAccessor_Gennum: only size=4 supported");
+      
+      for(i=0;i<addr.size();i++)
+        begin
+           $sformat(cmd,"rd FF000000%08X F", addr[i]);
+           fork
+           send_cmd(cmd);
+           readback(tmp);
+           join
+           
+           data[i] = tmp;
+           
+        end
+   endtask // readm
+   
+
+endclass // CBusAccessor_Gennum
+
+   function CBusAccessor get_accessor();
+      CBusAccessor_Gennum g = new();
+      return g;
+   endfunction
+
+     
+   
+   
+endinterface
+
+/* Helper macro for wiring Gennum-Xilinx ports in spec_top */
+
+`define GENNUM_WIRE_SPEC_PINS(IF_NAME) \
+  .L_RST_N   (IF_NAME.SYS.rst_n),\
+//  .L_CLKp (IF_NAME.SYS.lclk_p),\
+//  .L_CLKn (IF_NAME.SYS.lclk_n),\
+  .p2l_clkp  (IF_NAME.P2L.p2l_clk_p),\
+  .p2l_clkn  (IF_NAME.P2L.p2l_clk_n),\
+  .p2l_data   (IF_NAME.P2L.p2l_data),\
+  .p2l_dframe (IF_NAME.P2L.p2l_dframe),\
+  .p2l_valid  (IF_NAME.P2L.p2l_valid),\
+  .p2l_rdy    (IF_NAME.P2L.p2l_rdy),\
+  .p_wr_req  (IF_NAME.P2L.p_wr_req),\
+  .p_wr_rdy  (IF_NAME.P2L.p_wr_rdy),\
+  .rx_error  (IF_NAME.P2L.rx_error),\
+  .l2p_clkp  (IF_NAME.L2P.l2p_clk_p),\
+  .l2p_clkn  (IF_NAME.L2P.l2p_clk_n),\
+  .l2p_data   (IF_NAME.L2P.l2p_data),\
+  .l2p_dframe (IF_NAME.L2P.l2p_dframe),\
+  .l2p_valid  (IF_NAME.L2P.l2p_valid),\
+  .l2p_edb    (IF_NAME.L2P.l2p_edb),\
+  .l2p_rdy    (IF_NAME.L2P.l2p_rdy),\
+  .l_wr_rdy   (IF_NAME.L2P.l_wr_rdy),\
+  .p_rd_d_rdy (IF_NAME.L2P.p_rd_d_rdy),\
+  .tx_error   (IF_NAME.L2P.tx_error),\
+  .vc_rdy     (IF_NAME.P2L.vc_rdy)
+
+`endif //  `ifndef __GN4124_BFM_SVH
+    
diff --git a/hdl/gn4124core/sim/gn4124_bfm/gn412x_bfm.vhd b/hdl/gn4124core/sim/gn4124_bfm/gn412x_bfm.vhd
new file mode 100644
index 0000000000000000000000000000000000000000..578a7470bc240eb873416db8c125ddc15e75b399
--- /dev/null
+++ b/hdl/gn4124core/sim/gn4124_bfm/gn412x_bfm.vhd
@@ -0,0 +1,2922 @@
+library ieee;
+use ieee.std_logic_1164.all;
+use ieee.numeric_std.all;
+use std.textio.all;
+
+use work.util.all;
+use work.textutil.all;
+use work.mem_model.all;
+
+
+
+--==========================================================================--
+--
+-- MODULE << gn412x_bfm >>
+--
+-- Description : This module generates local bus signals from a text file
+--
+-- History: 
+--
+--==========================================================================--
+--
+-- To Do:
+--      Implement rd_outstanding_in
+--      Implement gpio for interrupts
+--      Implement response_delay
+
+entity GN412X_BFM is
+  generic
+    (
+      STRING_MAX     : integer := 256;  -- Command string maximum length
+      T_LCLK         : time    := 10 ns;         -- Local Bus Clock Period
+      T_P2L_CLK_DLY  : time    := 2 ns;          -- Delay from LCLK to P2L_CLK
+      INSTANCE_LABEL : string  := "GN412X_BFM";  -- Label string to be used as a prefix for messages from the model
+      MODE_PRIMARY   : boolean := true  -- TRUE for BFM acting as GN412x, FALSE for BFM acting as the DUT
+      );
+  port
+    (
+      --=========================================================--
+      -------------------------------------------------------------
+      -- CMD_ROUTER Interface
+      --
+      CMD_INT            : in    t_cmd_array;
+      CMD_REQ            : in    bit;
+      CMD_ACK            : out   bit;
+      CMD_CLOCK_EN       : in    boolean;
+      CMD_RD_DATA: out std_ulogic_vector(31 downto 0);
+      CMD_RD_DATA_VALID: out std_logic;
+      --=========================================================--
+      -------------------------------------------------------------
+      -- GN412x Signal I/O
+      -------------------------------------------------------------
+      -- This is the reset input to the BFM
+      --
+      RSTINn             : in    std_logic;
+      -------------------------------------------------------------
+      -- Reset outputs to DUT
+      --
+      RSTOUT18n          : out   std_logic;
+      RSTOUT33n          : out   std_logic;
+      -------------------------------------------------------------
+      ----------------- Local Bus Clock ---------------------------
+      -------------------------------------------------------------  __ Direction for primary mode
+      --                                                            / \
+      LCLK, LCLKn        : inout std_logic;      -- Out
+      -------------------------------------------------------------
+      ----------------- Local-to-PCI Dataflow ---------------------
+      -------------------------------------------------------------
+      -- Transmitter Source Synchronous Clock.
+      --
+      L2P_CLKp, L2P_CLKn : inout std_logic;      -- In  
+      -------------------------------------------------------------
+      -- L2P DDR Link
+      --
+      L2P_DATA           : inout std_logic_vector(15 downto 0);  -- In  -- Parallel Transmit Data.
+      L2P_DFRAME         : inout std_logic;  -- In  -- Transmit Data Frame.
+      L2P_VALID          : inout std_logic;  -- In  -- Transmit Data Valid. 
+      L2P_EDB            : inout std_logic;  -- In  -- End-of-Packet Bad Flag.
+      -------------------------------------------------------------
+      -- L2P SDR Controls
+      --
+      L_WR_RDY           : inout std_logic_vector(1 downto 0);  -- Out -- Local-to-PCIe Write.
+      P_RD_D_RDY         : inout std_logic_vector(1 downto 0);  -- Out -- PCIe-to-Local Read Response Data Ready.
+      L2P_RDY            : inout std_logic;  -- Out -- Tx Buffer Full Flag.
+      TX_ERROR           : inout std_logic;  -- Out -- Transmit Error.
+      -------------------------------------------------------------
+      ----------------- PCIe-to-Local Dataflow ---------------------
+      -------------------------------------------------------------
+      -- Transmitter Source Synchronous Clock.
+      --
+      P2L_CLKp, P2L_CLKn : inout std_logic;  -- Out -- P2L Source Synchronous Clock.
+      -------------------------------------------------------------
+      -- P2L DDR Link
+      --
+      P2L_DATA           : inout std_logic_vector(15 downto 0);  -- Out -- Parallel Receive Data.
+      P2L_DFRAME         : inout std_logic;  -- Out -- Receive Frame.
+      P2L_VALID          : inout std_logic;  -- Out -- Receive Data Valid.
+      -------------------------------------------------------------
+      -- P2L SDR Controls
+      --
+      P2L_RDY            : inout std_logic;  -- In  -- Rx Buffer Full Flag.
+      P_WR_REQ           : inout std_logic_vector(1 downto 0);  -- Out -- PCIe Write Request.
+      P_WR_RDY           : inout std_logic_vector(1 downto 0);  -- In  -- PCIe Write Ready.
+      RX_ERROR           : inout std_logic;  -- In  -- Receive Error.
+      VC_RDY             : inout std_logic_vector(1 downto 0);  -- Out -- Virtual Channel Ready Status.
+      -------------------------------------------------------------
+      -- GPIO signals
+      --
+      GPIO               : inout std_logic_vector(15 downto 0)
+      );
+end GN412X_BFM;
+
+architecture MODEL of GN412X_BFM is
+
+
+  function f_cmd_to_string (s : t_cmd_array) return string is
+    variable str : string(1 to STRING_MAX);
+    variable tmp : integer;
+  begin
+    for i in 1 to 256 loop
+      tmp := s(i);
+      if(tmp < 0) then
+        tmp := 0;
+      elsif(tmp > 255) then
+        tmp :=  255;
+      end if;
+      str(i) := character'val(tmp);
+    end loop;  -- i
+    return str;
+  end f_cmd_to_string;
+
+
+  signal CMD : string(1 to STRING_MAX);
+--=========================================================================--
+-- Routine for Data-Comparison
+--=========================================================================--
+
+  procedure read_cmp(L : inout line; PREFIX : in string; DATA, CMP_VAL, MASK : in std_ulogic_vector; ERR : out integer) is
+    variable X   : std_ulogic_vector(DATA'range);
+    variable STR : string(1 to 32);
+  begin
+    X                  := (others => 'X');
+    X                  := (not MASK and X) or (MASK and CMP_VAL);
+    STR(1 to X'length) := to_str(X);
+    if((DATA and MASK) /= (CMP_VAL and MASK)) then
+      write(L, PREFIX & string'("ERROR @ "));
+      write(L, now);
+      write(L, string'(" : "));
+      write(L, LF);
+      write(L, string'("       Expected      => "));
+      write(L, STR(1 to X'length));
+
+      write(L, string'(" (0x"));
+      write_hex_vector(L, X);
+      write(L, string'(")"));
+
+      STR(1 to X'length) := to_str(DATA);
+      write(L, LF);
+      write(L, string'("       Actually Read => "));
+      write(L, STR(1 to X'length));
+
+      write(L, string'(" (0x"));
+      write_hex_vector(L, DATA);
+      write(L, string'(")"));
+
+      ERR := 1;
+    else
+                      write(L, string'("Read OK @ "));
+                      write(L, now);
+                      write(L, string'(" : "));
+                      write(L, LF);
+                      write(L, string'("       Expected      => "));
+                      write(L, STR(1 to X'length));
+      ERR := 0;
+    end if;
+  end read_cmp;
+
+--=========================================================================--
+-- Routine to read a boolean from a string
+--
+-- it looks for true, false, on, off, 1, 0
+--=========================================================================--
+  procedure sget_boolean(S : in string; P : inout integer; X : out boolean) is
+    variable char : character;
+    variable q    : integer;
+  begin
+    if(S'length > P) then
+      char := S(P);
+      while(char = ' ') loop            -- Skip spaces
+        P    := P + 1;
+        char := S(P);
+      end loop;
+      if(S(P to P+4) = "true ") or (S(P to P+2) = "on ") or (S(P to P+1) = "1 ") then
+        X := true;
+      elsif(S(P to P+5) = "false ") or (S(P to P+3) = "off ") or (S(P to P+1) = "0 ") then
+        X := false;
+      else
+        assert false report "ERROR: Couldn't read a boolean" severity error;
+      end if;
+
+      -- skip over token
+      while((char /= ' ') and (P < S'length)) loop
+        char := S(P);
+        P    := P + 1;
+      end loop;
+
+    else
+      assert false report "ERROR: Couldn't read a boolean" severity error;
+
+    end if;
+
+  end sget_boolean;
+
+--=========================================================================--
+-- Produce Another Random Number
+--
+-- RNDOUT is a number between MIN and MAX.  SR must be stored and fed back each call.
+--=========================================================================--
+  procedure next_random(SR : inout integer) is
+    variable SRV : std_ulogic_vector(22 downto 0);
+    variable SRI : integer;
+  begin
+    SRV := To_Vector(SR, SRV'length);
+    SRV := SRV(21 downto 0) & (SRV(22) xor SRV(17));
+    SRI := conv_integer(SRV);
+    SR  := SRI;
+  end next_random;
+
+--      function next_random(SR : integer) return integer is
+--              variable  SRV : std_ulogic_vector(22 downto 0);
+--              variable  SRI : integer;
+--      begin
+--              SRV := To_Vector(SR, SRV'length);
+--              SRV := SRV(21 downto 0) & (SRV(22) xor SRV(17));
+--              SRI := conv_integer(SRV);
+--              return(SRI);
+--      end next_random;
+
+--=========================================================================--
+-- Produce Another Random Number
+--
+-- RNDOUT is a number between MIN and MAX.  SR must be stored and fed back each call.
+--=========================================================================--
+  procedure get_random(SR : inout integer; min : in integer; MAX : in integer; RNDOUT : out integer) is
+    variable SRV : std_ulogic_vector(22 downto 0);
+    variable SRI : integer;
+  begin
+    SRV    := To_Vector(SR, SRV'length);
+    SRV    := SRV(21 downto 0) & (SRV(22) xor SRV(17));
+    SRI    := conv_integer(SRV);
+    RNDOUT := min + (SRI mod (MAX - min + 1));
+    SR     := SRI;
+  end get_random;
+
+--=========================================================================--
+-- Test a Random Number and test it
+--
+-- RNDOUT is a number between MIN and MAX.  SR must be stored and fed back each call.
+--=========================================================================--
+  function test_random(SR : integer; PROBABILITY : integer) return boolean is
+  begin
+    return((SR mod 101) < PROBABILITY);
+  end test_random;
+
+
+--=========================================================================--
+-- Produce a Random Number between MIN and MAX
+--=========================================================================--
+  function range_random(SR : integer; min : integer; MAX : integer) return integer is
+  begin
+    return(min + (SR mod (MAX - min + 1)));
+  end range_random;
+
+
+--=========================================================================--
+-- Signal Declarations
+--=========================================================================--
+-----------------------------------------------------------------------------
+-- Global Settings
+-----------------------------------------------------------------------------
+
+  constant N_BARS    : integer := 2;
+  constant N_RAM_MAX : integer := 30;  -- Maximim size of BFM RAM address = 2**N_RAM_MAX
+
+  constant N_OUTBOUND_RD_OUTSTANDING : integer := 3;  -- Maximim number of outstanding reads the BFM will generate
+  constant N_INBOUND_RD_OUTSTANDING  : integer := 3;  -- Maximim number of outstanding reads the BFM will accept
+  constant N_COMPLETION_ID           : integer := 4;  -- Maximim number of completion IDs
+
+
+  -------------------------------------------------------------------------
+  -- Settable versions of the generic constants
+  --
+  signal T_LCLKi                 : time    := T_LCLK;  -- Local Bus Clock Period
+  signal T_P2L_CLK_DLYi          : time    := T_P2L_CLK_DLY;  -- Delay from LCLK to P2L_CLK
+  --
+  -------------------------------------------------------------------------
+  -- BFM Mode settings
+  --
+  signal PRIMARY                 : boolean;
+  signal SECONDARY               : boolean;
+  signal GENERATE_X              : boolean := false;
+  signal EXPECT_ERROR            : boolean := false;
+  signal OUTBOUND_RD_OUTSTANDING : integer := N_OUTBOUND_RD_OUTSTANDING;
+  signal RESPONSE_DELAY          : integer := 0;
+--      signal  BURST_LENGTH       : integer := 0;
+--      signal  BURST_MODULO       : integer := 0;
+  -- Acceptable values for TYPE are: "0000" for 32-bit memory read
+  -- Acceptable values for TYPE are: "0001" for 64-bit memory read
+  -- Acceptable values for TYPE are: "0010" for 32-bit memory write
+  -- Acceptable values for TYPE are: "0011" for 64-bit memory write
+  -- Acceptable values for TYPE are: "0100" for Completions without data
+  -- Acceptable values for TYPE are: "0101" for Completions with data
+
+  signal GPIOi : std_ulogic_vector(GPIO'range);
+  signal GPIOo : std_ulogic_vector(GPIO'range);
+
+-----------------------------------------------------------------------------
+-- Top Level I/O signals PRIMARY MODE
+-----------------------------------------------------------------------------
+  -------------------------------------------------------------
+  ----------------- Local Bus Clock ---------------------------
+  -------------------------------------------------------------
+  --
+  signal LCLKo, LCLKno        : std_ulogic;
+  -------------------------------------------------------------
+  ----------------- Local-to-PCI Dataflow ---------------------
+  -------------------------------------------------------------
+  -- Transmitter Source Synchronous Clock.
+  --
+  signal L2P_CLKpi, L2P_CLKni : std_ulogic;
+  signal L2P_CLKi_90          : std_ulogic;
+  -------------------------------------------------------------
+  -- L2P DDR Link
+  --
+  signal L2P_DATAi            : std_ulogic_vector(15 downto 0);  -- Parallel Transmit Data.
+  signal L2P_DFRAMEi          : std_ulogic;  -- Transmit Data Frame.
+  signal L2P_VALIDi           : std_ulogic;  -- Transmit Data Valid. 
+  signal L2P_EDBi             : std_ulogic;  -- End-of-Packet Bad Flag.
+  -------------------------------------------------------------
+  -- L2P SDR Controls
+  --
+  signal L_WR_RDYo            : std_ulogic_vector(1 downto 0);  -- Local-to-PCIe Write.
+  signal P_RD_D_RDYo          : std_ulogic_vector(1 downto 0);  -- PCIe-to-Local Read Response Data Ready.
+  signal L2P_RDYo             : std_ulogic;  -- Tx Buffer Full Flag.
+  signal TX_ERRORo            : std_ulogic;  -- Transmit Error.
+  -------------------------------------------------------------
+  ----------------- PCIe-to-Local Dataflow ---------------------
+  -------------------------------------------------------------
+  -- Transmitter Source Synchronous Clock.
+  --
+  signal P2L_CLKpo, P2L_CLKno : std_ulogic;  -- P2L Source Synchronous Clock.
+  -------------------------------------------------------------
+  -- P2L DDR Link
+  --
+  signal P2L_DATAo            : std_ulogic_vector(15 downto 0);  -- Parallel Receive Data.
+  signal P2L_DFRAMEo          : std_ulogic;  -- Receive Frame.
+  signal P2L_VALIDo           : std_ulogic;  -- Receive Data Valid.
+  -------------------------------------------------------------
+  -- P2L SDR Controls
+  --
+  signal P2L_RDYi             : std_ulogic;  -- Rx Buffer Full Flag.
+  signal P_WR_REQo            : std_ulogic_vector(1 downto 0);  -- PCIe Write Request.
+  signal P_WR_RDYi            : std_ulogic_vector(1 downto 0);  -- PCIe Write Ready.
+  signal RX_ERRORi            : std_ulogic;  -- Receive Error.
+  signal VC_RDYo              : std_ulogic_vector(1 downto 0);  -- Virtual Channel Ready Status.
+
+-----------------------------------------------------------------------------
+-- Top Level I/O signals SECONDARY MODE
+-----------------------------------------------------------------------------
+  -------------------------------------------------------------
+  ----------------- Local Bus Clock ---------------------------
+  -------------------------------------------------------------
+  --
+  signal LCLKi, LCLKni        : std_ulogic;
+  -------------------------------------------------------------
+  ----------------- Local-to-PCI Dataflow ---------------------
+  -------------------------------------------------------------
+  -- Transmitter Source Synchronous Clock.
+  --
+  signal L2P_CLKpo, L2P_CLKno : std_ulogic;
+  -------------------------------------------------------------
+  -- L2P DDR Link
+  --
+  signal L2P_DATAo            : std_ulogic_vector(15 downto 0);  -- Parallel Transmit Data.
+  signal L2P_DFRAMEo          : std_ulogic;  -- Transmit Data Frame.
+  signal L2P_VALIDo           : std_ulogic;  -- Transmit Data Valid. 
+  signal L2P_EDBo             : std_ulogic;  -- End-of-Packet Bad Flag.
+  -------------------------------------------------------------
+  -- L2P SDR Controls
+  --
+  signal L_WR_RDYi            : std_ulogic_vector(1 downto 0);  -- Local-to-PCIe Write.
+  signal P_RD_D_RDYi          : std_ulogic_vector(1 downto 0);  -- PCIe-to-Local Read Response Data Ready.
+  signal L2P_RDYi             : std_ulogic;  -- Tx Buffer Full Flag.
+  signal TX_ERRORi            : std_ulogic;  -- Transmit Error.
+  -------------------------------------------------------------
+  ----------------- PCIe-to-Local Dataflow ---------------------
+  -------------------------------------------------------------
+  -- Transmitter Source Synchronous Clock.
+  --
+  signal P2L_CLKpi, P2L_CLKni : std_ulogic;  -- P2L Source Synchronous Clock.
+  -------------------------------------------------------------
+  -- P2L DDR Link
+  --
+  signal P2L_DATAi            : std_ulogic_vector(15 downto 0);  -- Parallel Receive Data.
+  signal P2L_DFRAMEi          : std_ulogic;  -- Receive Frame.
+  signal P2L_VALIDi           : std_ulogic;  -- Receive Data Valid.
+  -------------------------------------------------------------
+  -- P2L SDR Controls
+  --
+  signal P2L_RDYo             : std_ulogic;  -- Rx Buffer Full Flag.
+  signal P_WR_REQi            : std_ulogic_vector(1 downto 0);  -- PCIe Write Request.
+  signal P_WR_RDYo            : std_ulogic_vector(1 downto 0);  -- PCIe Write Ready.
+  signal RX_ERRORo            : std_ulogic;  -- Receive Error.
+  signal VC_RDYi              : std_ulogic_vector(1 downto 0);  -- Virtual Channel Ready Status.
+
+  -------------------------------------------------------------
+  -- Used by the Inbound State Machine
+  --
+  signal ICLK        : std_ulogic;      -- Internal Inbound clock
+  signal LCLK_PERIOD : time := T_LCLK;
+
+-----------------------------------------------------------------------------
+-- Internal Model Signals
+-----------------------------------------------------------------------------
+  type     integer_vector is array (natural range <>) of integer;
+  type     boolean_vector is array (natural range <>) of boolean;
+  -------------------------------------------------------------------------
+  -- Largest Cache line size supported
+  --
+  constant N_MAX_LENGTH : integer    := 12;
+  constant MAX_LENGTH   : integer    := 2**N_MAX_LENGTH;
+  -------------------------------------------------------------------------
+  -- An array of MAX_CACHE * 32 bits
+  --
+  type     ARRAY_OF_32 is array (natural range <>) of std_ulogic_vector(31 downto 0);
+  type     ARRAY_OF_2 is array (natural range <>) of std_ulogic_vector(1 downto 0);
+--      signal  CACHE_BUFFER       : ARRAY_OF_32(MAX_CACHE-1 downto 0);
+  -------------------------------------------------------------------------
+  --
+  signal   CLK0o        : std_ulogic;
+  signal   CLK90o       : std_ulogic;
+  signal   CLK          : std_ulogic;   -- This one is used by the main loop
+  signal   RSTOUTo      : std_ulogic := '0';
+
+
+  constant T_HOLD_OUT  : time := 1 ns;
+  signal   T_HOLD_OUTi : time := T_HOLD_OUT;
+
+
+  constant WR_FLUSH_POST      : integer := 0;
+  constant WR_POST            : integer := 1;
+  constant WR_POST_FLUSH      : integer := 2;
+  constant WR_FLUSH_POST_LAST : integer := 3;
+
+
+
+  constant RD_POST          : integer := 0;
+  constant RD_POST_IPR      : integer := 1;
+  constant RD_IPR_POST      : integer := 2;
+  constant RD_IPR_POST_LAST : integer := 3;
+
+
+
+  constant MAX_DW_PACKET_SIZE : integer := 1024;  -- Maximum packet size in units of DW (32 bits)
+
+  type INT_VECTOR is array (natural range <>) of integer;
+  type DATA32 is array (natural range <>) of std_ulogic_vector(31 downto 0);
+
+  --=======================================================================
+  --==
+  --== Data Structures for INBOUND Read Requests that generate Outbound Completions
+  --==
+  --=======================================================================
+  -------------------------------------------------------------------------
+  -- Data Structure used to store inbound read requests
+  --  note: there are N_COMPLETION_ID size for this data structure rather 
+  --        than N_INBOUND_RD_OUTSTANDING.  Only up to N_INBOUND_RD_OUTSTANDING can be active at a time
+  type INBOUND_READ_REQUEST_TYPE is
+  record
+    ADDRESS : std_ulogic_vector(63 downto 0);  -- BAR base address
+    BAR_HIT : std_ulogic_vector(1 downto 0);   -- This is for secondary mode
+    TC      : std_ulogic_vector(2 downto 0);   -- Trafic class
+    V       : std_ulogic;               -- Virtual channel
+    LENGTH  : integer;                  -- Transfer length in DW
+    STATE   : boolean;  -- Used to indicate if the entry is in use or not
+  end record;
+  type   INBOUND_READ_REQUEST_ARRAY_TYPE is array (N_COMPLETION_ID-1 downto 0) of INBOUND_READ_REQUEST_TYPE;
+  signal INBOUND_READ_REQUEST_ARRAY : INBOUND_READ_REQUEST_ARRAY_TYPE;
+
+  -------------------------------------------------------------------------
+  -- This is the state of the CID for inbound read requests. 
+  -- Used to signal back to the inbound process which completions have been serviced
+  -- When INBOUND_READ_REQUEST_ARRAY(CID).STATE = INBOUND_READ_REQUEST_CPL_STATE(CID) then the CID may be re-used
+  signal INBOUND_READ_REQUEST_CPL_STATE : boolean_vector(N_COMPLETION_ID-1 downto 0);
+
+  signal CURRENT_INBOUND_RD_IPR : integer;  -- Indicates how many inbound reads are currently outstanding
+
+
+  --=======================================================================
+  --==
+  --== Data Structures for OUTBOUND Read Requests that result in Inbound Completions
+  --==
+  --=======================================================================
+  -- This data structure is written by the outbound process and 
+  -- read by the inbound process to verify completion packets
+  type RD_BUFFER_TYPE is
+  record
+    ADDRESS : std_ulogic_vector(63 downto 0);
+    BAR_HIT : std_ulogic_vector(1 downto 0);
+    FBE     : std_ulogic_vector(3 downto 0);
+    LBE     : std_ulogic_vector(3 downto 0);
+    STATE   : boolean;
+    DATA    : DATA32(MAX_DW_PACKET_SIZE-1 downto 0);
+    MASK    : DATA32(MAX_DW_PACKET_SIZE-1 downto 0);
+  end record;
+
+  type   RD_BUFFER_ARRAY_TYPE is array (N_COMPLETION_ID-1 downto 0) of RD_BUFFER_TYPE;
+  signal RD_BUFFER : RD_BUFFER_ARRAY_TYPE;
+
+  -------------------------------------------------------------------------
+  -- Used to signal back to the inbound process which completions have been serviced
+  -- An entry is in use when OUTBOUND_READ_COMPLETION_STATE(i) /= INBOUND_READ_REQUEST_ARRAY(i).STATE
+  signal OUTBOUND_READ_REQUEST_CPL_STATE : boolean_vector(N_COMPLETION_ID-1 downto 0);
+
+  signal CURRENT_OUTBOUND_RD_IPR : integer;  -- Indicates how many inbound reads are currently outstanding
+
+
+  --=======================================================================
+  --==
+  --== Data Structures for OUTBOUND Write Requests
+  --==
+  --=======================================================================
+
+  -- There is only a single write buffer and it lives as a variable in the outbound process
+  -- since the inbound process doesn't need to know anything about it
+  type WR_BUFFER_TYPE is
+  record
+    ADDRESS : std_ulogic_vector(63 downto 0);
+    BE      : std_ulogic_vector(3 downto 0);
+    DATA    : std_ulogic_vector(31 downto 0);
+  end record;
+
+
+  type WR_BUFFER_ARRAY_TYPE is array (MAX_DW_PACKET_SIZE-1 downto 0) of WR_BUFFER_TYPE;
+
+--      signal WR_BUFFER : WR_BUFFER_ARRAY_TYPE;
+
+  signal RANDOM_NUMBER : integer := 0;
+
+
+  -------------------------------------------------------------------------
+  -- Data Structure used to store info about BARs that generate outbound
+  -- read/write packets
+  --
+  type BAR_ATTRIBUTE_TYPE is
+  record
+    BASE : std_ulogic_vector(63 downto 0);  -- BAR base address
+    MASK : std_ulogic_vector(31 downto 0);  -- BAR mask
+    VC   : std_ulogic;                      -- Virtual Channel
+    TC   : std_ulogic_vector(2 downto 0);   -- Trafic class
+    S    : std_ulogic;                      -- No Snoop Bit
+  end record;
+  type   BAR_ATTRIBUTE_ARRAY_TYPE is array (1 downto 0) of BAR_ATTRIBUTE_TYPE;
+  signal BAR_ATTRIBUTE_ARRAY : BAR_ATTRIBUTE_ARRAY_TYPE;
+
+  -------------------------------------------------------------------------
+  -- Data Structure used to store info about memory BARs for internal BFM
+  --
+  type BFM_BAR_ATTRIBUTE_TYPE is
+  record
+    BASE : std_ulogic_vector(63 downto 0);  -- BAR base address
+    MASK : std_ulogic_vector(31 downto 0);  -- BAR mask
+  end record;
+  type   BFM_BAR_ATTRIBUTE_ARRAY_TYPE is array (1 downto 0) of BFM_BAR_ATTRIBUTE_TYPE;
+  signal BFM_BAR_ATTRIBUTE_ARRAY : BFM_BAR_ATTRIBUTE_ARRAY_TYPE;
+
+
+
+
+
+--      type VC_SIZE_VECTOR is array (natural range <>) of std_ulogic_vector(1 downto 0);
+
+
+-----------------------------------------------------------------------------
+-- Signals used by the outbound and inbound process to read the BFM RAM
+-----------------------------------------------------------------------------
+  signal RAM_REQ0, RAM_REQ1         : boolean := false;  -- Request
+  signal RAM_WR0, RAM_WR1           : boolean;           -- Write mode
+  signal RAM_ACK0, RAM_ACK1         : boolean;           -- Acknowledge
+  signal RAM_ADDR0, RAM_ADDR1       : std_ulogic_vector(N_RAM_MAX-1 downto 0);
+  signal RAM_WR_DATA0, RAM_WR_DATA1 : std_ulogic_vector(7 downto 0);
+  signal RAM_RD_DATA0, RAM_RD_DATA1 : std_ulogic_vector(7 downto 0);
+
+-----------------------------------------------------------------------------
+-- Signals Related to the InBound Process
+-----------------------------------------------------------------------------
+  signal IN_DATA                  : std_ulogic_vector(31 downto 0);
+  signal IN_DATA_LOW              : std_ulogic_vector(15 downto 0);
+  signal IN_DFRAME                : std_ulogic;
+  signal IN_VALID                 : std_ulogic;
+  signal Q_IN_DFRAME              : std_ulogic;
+-----------------------------------------------------------------------------
+-- Signals Related to the OutBound Process
+-----------------------------------------------------------------------------
+  signal Q_OUT_DATA, OUT_DATA     : std_ulogic_vector(31 downto 0);
+  signal Q_OUT_DFRAME, OUT_DFRAME : std_ulogic;
+  signal Q_OUT_VALID, OUT_VALID   : std_ulogic;
+  signal OUT_WR_REQ               : std_ulogic_vector(P_WR_REQ'range);
+  signal OUT_WR_RDY               : std_ulogic_vector(P_WR_RDY'range);
+  signal Q_OUT_WR_RDY             : std_ulogic_vector(P_WR_RDY'range);
+
+
+
+--#########################################################################--
+--########################## Start of Code ################################--
+--#########################################################################--
+
+begin
+CMD <= f_cmd_to_string(CMD_INT);
+--=========================================================================--
+-- Reset Outputs
+--=========================================================================--
+  RSTOUT18n <= not RSTOUTo when PRIMARY else 'Z';
+  RSTOUT33n <= not RSTOUTo when PRIMARY else 'Z';
+
+--=========================================================================--
+-- Generate the Internal LCLK
+--=========================================================================--
+  process
+  begin
+    CLK0o  <= '0';
+    CLK90o <= '0';
+    loop
+      if (CMD_CLOCK_EN and PRIMARY) then
+        CLK0o <= '1';
+        CLK0o <= transport '0' after (T_LCLKi/2);
+
+        CLK90o <= transport '1' after (T_LCLKi/4);
+        CLK90o <= transport '0' after ((T_LCLKi*3)/4);
+
+        wait for T_LCLKi;
+      else
+        wait until(CMD_CLOCK_EN'event and CMD_CLOCK_EN and PRIMARY);
+      end if;
+    end loop;
+  end process;
+
+  PRIMARY   <= MODE_PRIMARY;
+  SECONDARY <= not MODE_PRIMARY;
+
+--=========================================================================--
+-- Top Level I/O signals with timing information
+--=========================================================================--
+
+--*************************************************************************--
+-- Top Level I/O signals PRIMARY MODE
+--*************************************************************************--
+  -------------------------------------------------------------
+  ----------------- Local Bus Clock ---------------------------
+  -------------------------------------------------------------
+  --
+  LCLKo       <= CLK0o;
+  LCLKno      <= not CLK0o;
+  LCLK        <= LCLKo                                                       when PRIMARY else 'Z';
+  LCLKn       <= LCLKno                                                      when PRIMARY else 'Z';
+  -------------------------------------------------------------
+  ----------------- Local-to-PCI Dataflow ---------------------
+  -----------------  (GN412x is an input) ---------------------
+  -------------------------------------------------------------
+  -- Receiver Source Synchronous Clock.
+  --
+  L2P_CLKpi   <= To_X01(L2P_CLKp);
+  L2P_CLKni   <= To_X01(L2P_CLKn);
+  -------------------------------------------------------------
+  -- L2P DDR Link
+  --
+  L2P_DATAi   <= To_X01(To_StdULogicVector(L2P_DATA));
+  L2P_DFRAMEi <= To_X01(L2P_DFRAME);
+  L2P_VALIDi  <= To_X01(L2P_VALID);
+  L2P_EDBi    <= To_X01(L2P_EDB);
+  -------------------------------------------------------------
+  -- L2P SDR Controls
+  --
+  L_WR_RDY    <= To_StdLogicVector(L_WR_RDYo)           after T_HOLD_OUTi    when PRIMARY else (others => 'Z');
+  P_RD_D_RDY  <= To_StdLogicVector(P_RD_D_RDYo)         after T_HOLD_OUTi    when PRIMARY else (others => 'Z');
+  L2P_RDY     <= L2P_RDYo                               after T_HOLD_OUTi    when PRIMARY else 'Z';
+  TX_ERROR    <= TX_ERRORo                              after T_HOLD_OUTi    when PRIMARY else 'Z';
+  -------------------------------------------------------------
+  ----------------- PCIe-to-Local Dataflow ---------------------
+  ----------------- (GN412x is an output) ---------------------
+  -------------------------------------------------------------
+  -- Transmitter Source Synchronous Clock.
+  --
+  P2L_CLKp    <= P2L_CLKpo                                                   when PRIMARY else 'Z';
+  P2L_CLKn    <= P2L_CLKno                                                   when PRIMARY else 'Z';
+  P2L_CLKpo   <= transport not CLK0o                    after T_P2L_CLK_DLYi when PRIMARY else 'Z';  -- Note that P2L_CLK is effectively inverted
+  P2L_CLKno   <= transport CLK0o                        after T_P2L_CLK_DLYi when PRIMARY else 'Z';
+  -------------------------------------------------------------
+  -- P2L DDR Link
+  --
+  P2L_DATA    <= transport To_StdLogicVector(P2L_DATAo) after T_P2L_CLK_DLYi when PRIMARY else (others => 'Z');
+  P2L_DFRAME  <= transport P2L_DFRAMEo                  after T_P2L_CLK_DLYi when PRIMARY else 'Z';
+  P2L_VALID   <= transport P2L_VALIDo                   after T_P2L_CLK_DLYi when PRIMARY else 'Z';
+  -------------------------------------------------------------
+  -- P2L SDR Controls
+  --
+  P2L_RDYi    <= To_X01(P2L_RDY);
+  P_WR_REQ    <= To_StdLogicVector(P_WR_REQo)                                when PRIMARY else (others => 'Z');
+  P_WR_RDYi   <= To_X01(To_StdULogicVector(P_WR_RDY));
+  RX_ERRORi   <= To_X01(RX_ERROR);
+  VC_RDY      <= To_StdLogicVector(VC_RDYo)                                  when PRIMARY else (others => 'Z');
+
+  CLK <= CLK0o when PRIMARY else LCLK;
+
+-----------------------------------------------------------------------------
+-- PRIMARY MODE Internal Signals
+-----------------------------------------------------------------------------
+  process
+  begin
+    wait until(CLK90o'event and (CLK90o = '1'));
+    P2L_DATAo   <= Q_OUT_DATA(15 downto 0);
+    P2L_DFRAMEo <= Q_OUT_DFRAME;
+    P2L_VALIDo  <= Q_OUT_VALID;
+    P_WR_REQo   <= OUT_WR_REQ;
+    if(RSTOUTo = '1') then
+      VC_RDYo <= (others => '0');
+    else
+      VC_RDYo <= (others => '1');
+    end if;
+
+    wait until(CLK90o'event and (CLK90o = '0'));
+    P2L_DATAo <= Q_OUT_DATA(31 downto 16);
+  end process;
+
+
+  L_WR_RDYo   <= (others => '1');
+  P_RD_D_RDYo <= (others => '1');
+  L2P_RDYo    <= '1';
+  TX_ERRORo   <= '0';
+
+  GPIO  <= To_StdLogicVector(GPIOo);
+  GPIOo <= (others => 'Z');
+  GPIOi <= To_StdULogicVector(GPIO);
+
+
+--*************************************************************************--
+-- Top Level I/O signals SECONDARY MODE
+--*************************************************************************--
+  -------------------------------------------------------------
+  ----------------- Local-to-PCI Dataflow ---------------------
+  ----------------- (GN412x is an output) ---------------------
+  -------------------------------------------------------------
+  -- Transmitter Source Synchronous Clock.
+  --
+  L2P_CLKp    <= transport L2P_CLKpo                    after T_P2L_CLK_DLYi when SECONDARY else 'Z';
+  L2P_CLKn    <= transport L2P_CLKno                    after T_P2L_CLK_DLYi when SECONDARY else 'Z';
+  -------------------------------------------------------------
+  -- L2P DDR Link
+  --
+  L2P_DATA    <= transport To_StdLogicVector(L2P_DATAo) after T_P2L_CLK_DLYi when SECONDARY else (others => 'Z');
+  L2P_DFRAME  <= transport L2P_DFRAMEo                  after T_P2L_CLK_DLYi when SECONDARY else 'Z';
+  L2P_VALID   <= transport L2P_VALIDo                   after T_P2L_CLK_DLYi when SECONDARY else 'Z';
+  L2P_EDB     <= transport L2P_EDBo                     after T_P2L_CLK_DLYi when SECONDARY else 'Z';
+  -------------------------------------------------------------
+  -- L2P SDR Controls
+  --
+  L_WR_RDYi   <= To_X01(To_StdULogicVector(L_WR_RDY));
+  P_RD_D_RDYi <= To_X01(To_StdULogicVector(P_RD_D_RDY));
+  L2P_RDYi    <= To_X01(L2P_RDY);
+  TX_ERRORi   <= To_X01(TX_ERROR);
+
+  -------------------------------------------------------------
+  ----------------- PCIe-to-Local Dataflow ---------------------
+  -----------------  (GN412x is an input) ---------------------
+  -------------------------------------------------------------
+  -- Receiver Source Synchronous Clock.
+  --
+  P2L_CLKpi   <= To_X01(P2L_CLKp);
+  P2L_CLKni   <= To_X01(P2L_CLKn);
+  -------------------------------------------------------------
+  -- P2L DDR Link
+  --
+  P2L_DATAi   <= To_X01(To_StdULogicVector(P2L_DATA));
+  P2L_DFRAMEi <= To_X01(P2L_DFRAME);
+  P2L_VALIDi  <= To_X01(P2L_VALID);
+  -------------------------------------------------------------
+  -- P2L SDR Controls
+  --
+  P2L_RDY     <= 'Z'             when PRIMARY else P2L_RDYo                                 after T_HOLD_OUTi;
+  P_WR_REQi   <= To_X01(To_StdULogicVector(P_WR_REQ));  -- INPUT
+  P_WR_RDY    <= (others => 'Z') when PRIMARY else To_StdLogicVector(P_WR_RDYo) after T_HOLD_OUTi;  -- OUTPUT
+  VC_RDYi     <= To_X01(To_StdULogicVector(VC_RDY));    -- INPUT
+  RX_ERROR    <= 'Z'             when PRIMARY else RX_ERRORo                                after T_HOLD_OUTi;  -- OUTPUT
+
+-----------------------------------------------------------------------------
+-- SECONDARY MODE Internal Signals
+-----------------------------------------------------------------------------
+  L2P_CLKpo <= CLK;
+  L2P_CLKno <= not CLK;
+
+  L2P_DATAo   <= OUT_DATA(15 downto 0) when (L2P_CLKpo = '1') else OUT_DATA(31 downto 16);
+  L2P_DFRAMEo <= OUT_DFRAME;
+  L2P_VALIDo  <= OUT_VALID;
+  L2P_EDBo    <= '0';                   -- CHANGE: Add L2P_EDB functionality
+
+
+
+
+--*************************************************************************--
+-- Signals Related to the OutBound Process
+--*************************************************************************--
+  process
+  begin
+    wait until(CLK0o'event and (CLK0o = '1'));
+    Q_OUT_DATA   <= OUT_DATA;
+    Q_OUT_DFRAME <= OUT_DFRAME;
+    Q_OUT_VALID  <= OUT_VALID;
+  end process;
+
+  process
+  begin
+    wait until(CLK'event and (CLK = '1'));
+    if(PRIMARY) then
+      Q_OUT_WR_RDY <= P_WR_RDYi;
+    else
+      Q_OUT_WR_RDY <= L_WR_RDYi;
+    end if;
+    OUT_WR_RDY <= Q_OUT_WR_RDY;
+  end process;
+
+
+
+--*************************************************************************--
+-- Signals Related to the InBound Process
+--*************************************************************************--
+  ICLK <= not L2P_CLKpi after T_LCLKi/4 when PRIMARY else not P2L_CLKpi;
+
+--      process -- Determine LCLK period for when in secondary mode
+--              variable DELTA : time;
+--      begin
+--              wait until(LCLKi'event and (LCLKi = '1'));
+--              DELTA := NOW;
+--              wait until(LCLKi'event and (LCLKi = '1'));
+--              DELTA := NOW - DELTA;
+--              LCLK_PERIOD <= DELTA;
+--      end process;
+
+  process
+  begin
+    wait until(ICLK'event and (ICLK = '0'));
+    if(PRIMARY) then
+      IN_DATA_LOW <= L2P_DATAi;
+    else
+      IN_DATA_LOW <= P2L_DATAi;
+    end if;
+    wait until(ICLK'event and (ICLK = '1'));
+    if(IN_VALID = '1') then
+      Q_IN_DFRAME <= IN_DFRAME;
+    end if;
+  end process;
+
+  IN_DATA(31 downto 16) <= L2P_DATAi when PRIMARY else P2L_DATAi;
+  IN_DATA(15 downto 0)  <= IN_DATA_LOW;
+
+  IN_DFRAME <= L2P_DFRAMEi when PRIMARY else P2L_DFRAMEi;
+  IN_VALID  <= L2P_VALIDi  when PRIMARY else P2L_VALIDi;
+
+-- CHANGE: add ability to de-assert RDY
+
+  P_WR_RDYo <= P_WR_REQi;
+
+--*************************************************************************--
+-- Signals involving interaction between InBound/Outbound Processes
+--*************************************************************************--
+-----------------------------------------------------------------------------
+-- Keep track of how many inbound reads requests are in progress
+-----------------------------------------------------------------------------
+  process(INBOUND_READ_REQUEST_ARRAY, INBOUND_READ_REQUEST_CPL_STATE)
+    variable COUNT : integer;
+  begin
+    COUNT := 0;
+    for I in 0 to N_COMPLETION_ID-1 loop
+      if(INBOUND_READ_REQUEST_ARRAY(I).STATE /= INBOUND_READ_REQUEST_CPL_STATE(I)) then
+        COUNT := COUNT + 1;
+      end if;
+    end loop;
+    CURRENT_INBOUND_RD_IPR <= COUNT;
+  end process;
+
+
+-----------------------------------------------------------------------------
+-- Keep track of how many outbound reads requests are in progress
+-----------------------------------------------------------------------------
+  process(RD_BUFFER, OUTBOUND_READ_REQUEST_CPL_STATE)
+    variable COUNT : integer;
+  begin
+    COUNT := 0;
+    for I in 0 to N_COMPLETION_ID-1 loop
+      if(RD_BUFFER(I).STATE /= OUTBOUND_READ_REQUEST_CPL_STATE(I)) then
+        COUNT := COUNT + 1;
+      end if;
+    end loop;
+    CURRENT_OUTBOUND_RD_IPR <= COUNT;
+  end process;
+
+
+--#########################################################################--
+--
+-- OutBound State Machine 
+--
+-- (Handles TX from the BFM: P2L for Primary and L2P for secondary)
+--
+--#########################################################################--
+  process
+    --file      OUT_FILE : text is out "STD_OUTPUT";
+    file OUT_FILE           : text open write_mode is "NullFile";
+    variable OUTPUT_LINE    : line;
+    variable ERR_CNT        : integer;
+    variable L_CMD          : string(1 to 80);
+    variable TMP_STR        : string(1 to 80);
+    variable QCMD           : string(CMD'range);
+    variable L_NUM          : integer;
+    variable L_ADDR         : std_ulogic_vector(63 downto 0);
+    variable L_BE           : std_ulogic_vector(3 downto 0);
+    variable L_DATA         : std_ulogic_vector(31 downto 0);
+    variable MODULO_MASK    : std_ulogic_vector(31 downto 2);
+    variable L_MASK         : std_ulogic_vector(31 downto 0);
+    variable AMASK_TMP      : std_ulogic_vector(31 downto 0);  -- Will become 63:0
+    variable CHAR_PTR       : integer;
+    variable I_TMP          : integer;
+    variable I_TMP2         : integer;
+    variable I_TMP3         : integer;
+    variable I_TMP4         : integer;
+    variable I_TMP5         : integer;
+    variable vERR           : integer;
+    variable B_TMP          : boolean;
+    variable START          : time;
+    variable N_BURST_MODULO : INT_VECTOR(0 to N_BARS-1);
+    variable BURST_LENGTH   : INT_VECTOR(0 to N_BARS-1);
+    variable CURRENT_BAR    : integer;
+    variable CURRENT_VC     : integer;
+    variable IWAIT_RANDOM   : integer;
+    variable IWAIT_RANDOM_N : integer;
+    variable RNDNUM         : integer;
+    variable BAR_HIT        : boolean_vector(1 downto 0);
+    variable BFM_BAR_HIT    : boolean_vector(1 downto 0);
+    variable DATA_TMP8      : std_ulogic_vector(7 downto 0);
+    variable DATA_TMP32     : std_ulogic_vector(31 downto 0);
+    variable A_TMP          : std_ulogic_vector(L_ADDR'range);
+    variable RD_ADDRESS     : std_ulogic_vector(L_ADDR'range);
+
+    --
+    -- Write Buffer Management
+    variable WR_BUFFER_COUNT : integer;
+    variable WR_BUFFER_PTR   : integer;
+    variable WR_CASE         : integer;
+    variable RW_SEQUENTIAL   : boolean;
+    variable WR_BUFFER       : WR_BUFFER_ARRAY_TYPE;
+    variable WR_TYPE         : std_ulogic_vector(3 downto 0);
+
+    --
+    -- Read Request Buffer Management
+--              variable  RD_BUFFER_COUNT : integer;
+    variable RD_BUFFER_PTR : integer;
+    variable CURRENT_CID   : integer;   -- Current read completion ID
+    variable NEXT_CID      : integer;   -- Next read completion ID
+    variable RD_CASE       : integer;
+    variable RD_TYPE       : std_ulogic_vector(3 downto 0);
+
+    variable RW_BLAST    : boolean;
+    --
+    -- OUT variables
+    variable OUT_NOSNOOP : std_ulogic;
+--              variable  OUT_VC          : std_ulogic;
+    variable OUT_TC      : std_ulogic_vector(2 downto 0);
+
+    --
+    -- Read Completion Management
+    variable CPL_MODULO  : integer;     -- Modulo boundary for 
+    variable CPL_ORDER   : boolean;
+    variable CPL_LENGTH  : integer;
+    variable CPL_LAST    : std_ulogic;
+    variable CPL_CID     : integer;
+    variable CPL_POP_PTR : INT_VECTOR(N_COMPLETION_ID-1 downto 0);  -- DW pointer for each CID
+
+    variable IWAIT_RND_SEED : integer;
+
+
+    variable DEBUG : integer;
+
+
+  begin
+
+    -- Signal Initialization
+    OUT_DATA   <= (others => 'Z');
+    OUT_DFRAME <= '0';
+    OUT_VALID  <= '0';
+    OUT_WR_REQ <= (others => '0');
+
+
+    -- Variable Initialization
+    ERR_CNT                        := 0;
+    WR_BUFFER_COUNT                := 0;
+    WR_BUFFER_PTR                  := 0;
+    RD_BUFFER_PTR                  := 0;
+    CURRENT_BAR                    := 0;
+    BURST_LENGTH                   := (others => 512);
+    NEXT_CID                       := N_COMPLETION_ID - 1;
+    CURRENT_CID                    := N_COMPLETION_ID - 1;
+    DEBUG                          := 0;
+    CPL_POP_PTR                    := (others => 0);
+    CPL_CID                        := 0;
+    CPL_MODULO                     := 64;
+    INBOUND_READ_REQUEST_CPL_STATE <= (others => false);
+
+    loop
+      CMD_ACK <= '0';
+
+      wait on CMD_REQ, CLK;
+
+      --============================================================--
+      -- Load in a command
+      --============================================================--
+      if(CMD_REQ = '1') then
+
+        START := now;
+--                              if (CMD_REQ /= '1') then
+--                                      wait until (CMD_REQ'EVENT and CMD_REQ = '1');
+--                              end if;
+
+        QCMD    := CMD;
+        CMD_ACK <= '1';
+        if (CMD_REQ /= '0') then
+          wait until (CMD_REQ'event and CMD_REQ = '0');
+        end if;
+
+        if (START /= now) then
+          wait until(CLK'event and (CLK = '1'));
+        end if;
+
+
+        CHAR_PTR := 1;                  -- Point to beginning of line
+
+--        report "Q_CMD " & QCMD;
+        
+        SGET_TOKEN(QCMD, CHAR_PTR, L_CMD);  -- Strip off line number
+
+--        report "Line " & L_CMD;
+
+        SGET_TOKEN(QCMD, CHAR_PTR, L_CMD);  -- Load the command
+
+--        report "Command " & L_CMD;
+
+        
+        if(not MODE_PRIMARY) then
+          DEBUG := DEBUG + 1;
+        end if;
+
+        --============================================================--
+        -- Command Decode --
+        --============================================================--
+
+        --*-------------------------------------------------------------
+        --* wr ADDR BE DATA
+        --*
+        --*   - handles the wr/wrb command
+        --*
+        --* Write Cases:
+        --*
+        --*    Single Buffer   Write    Action
+        --*    Write  Empty  Burstable  Taken
+        --*
+        --*      N      N        N        FLUSH, POST      : WR_CASE=WR_FLUSH_POST
+        --*      N      N        Y        POST             : WR_CASE=WR_POST
+        --*      N      Y        -        POST             : WR_CASE=WR_POST
+        --*      Y      N        N        FLUSH, POST, LAST: WR_CASE=WR_FLUSH_POST_LAST
+        --*      Y      N        Y        POST, FLUSH      : WR_CASE=WR_POST_FLUSH
+        --*      Y      Y        -        POST, FLUSH      : WR_CASE=WR_POST_FLUSH
+        --*
+        --*
+        --*-------------------------------------------------------------
+
+--        report "L_CMD" & L_CMD;
+        
+        if (L_CMD(1 to 2) = "wr") or (L_CMD(1 to 3) = "rd ") or (L_CMD(1 to 3) = "rdb") then
+          SGET_VECTOR_64(QCMD, CHAR_PTR, L_ADDR);
+          SGET_VECTOR(QCMD, CHAR_PTR, L_BE);
+          SGET_VECTOR(QCMD, CHAR_PTR, L_DATA);
+          if(L_CMD(1 to 2) = "rd") then
+            while(QCMD(CHAR_PTR) = ' ') loop  -- Skip spaces
+              CHAR_PTR := CHAR_PTR + 1;
+              if(CHAR_PTR > QCMD'length) then
+                exit;
+              end if;
+            end loop;
+            if(CHAR_PTR > QCMD'length) then
+              L_MASK := (others => '0');
+            else
+              SGET_VECTOR(QCMD, CHAR_PTR, L_MASK);
+            end if;
+          end if;
+
+          BAR_HIT(0) := (((L_ADDR(31 downto 0) and BAR_ATTRIBUTE_ARRAY(0).MASK) = BAR_ATTRIBUTE_ARRAY(0).BASE(31 downto 0))
+                             and (L_ADDR(63 downto 32) = BAR_ATTRIBUTE_ARRAY(0).BASE(63 downto 32)));
+          BAR_HIT(1) := (((L_ADDR(31 downto 0) and BAR_ATTRIBUTE_ARRAY(1).MASK) = BAR_ATTRIBUTE_ARRAY(1).BASE(31 downto 0))
+                             and (L_ADDR(63 downto 32) = BAR_ATTRIBUTE_ARRAY(1).BASE(63 downto 32)));
+
+          BFM_BAR_HIT(0) := (((L_ADDR(31 downto 0) and BFM_BAR_ATTRIBUTE_ARRAY(0).MASK) = BFM_BAR_ATTRIBUTE_ARRAY(0).BASE(31 downto 0))
+                             and (L_ADDR(63 downto 32) = BFM_BAR_ATTRIBUTE_ARRAY(0).BASE(63 downto 32)));
+          BFM_BAR_HIT(1) := (((L_ADDR(31 downto 0) and BFM_BAR_ATTRIBUTE_ARRAY(1).MASK) = BFM_BAR_ATTRIBUTE_ARRAY(1).BASE(31 downto 0))
+                             and (L_ADDR(63 downto 32) = BFM_BAR_ATTRIBUTE_ARRAY(1).BASE(63 downto 32)));
+
+          if(BAR_HIT(1)) then
+            CURRENT_BAR := 1;
+          else
+            CURRENT_BAR := 0;
+          end if;
+
+          CURRENT_VC := to_int(BAR_ATTRIBUTE_ARRAY(CURRENT_BAR).VC);
+
+
+          if(BAR_HIT(0) or BAR_HIT(1)) then  -- Address hits one of the external BARs
+
+                                        -- Burst-last
+
+            MODULO_MASK := to_vector((((2**N_BURST_MODULO(CURRENT_BAR))/4) - 1), MODULO_MASK'length);
+
+            RW_BLAST := (L_BE /= "1111")
+                        or ((WR_BUFFER_COUNT-1) = (BURST_LENGTH(CURRENT_VC)/4))
+                        or ((MODULO_MASK and L_ADDR(31 downto 2)) = MODULO_MASK)
+                        or (L_CMD(3) /= 'b');
+
+
+
+                                            --========================================================--
+                                            -- Do Write Cycles 
+                                            --========================================================--
+            if (L_CMD(1 to 2) = "wr") then  -- Do Write Cycles
+
+              if(WR_BUFFER_COUNT > 0) then
+                I_TMP := To_Int(L_ADDR(31 downto 2));
+
+-- CHANGE to do proper 64 bit address
+
+                RW_SEQUENTIAL := ((To_Int(WR_BUFFER(WR_BUFFER_PTR-1).ADDRESS(31 downto 2)) + 1) = I_TMP);
+
+              else
+                RW_SEQUENTIAL := false;
+              end if;
+
+              if (L_CMD(3) = 'b') then  -- See if it will go into the write cache
+                if (WR_BUFFER_COUNT = 0) then    -- Buffer Empty
+                  WR_CASE := WR_POST;
+                else                    -- Buffer Not Empty
+                  if RW_SEQUENTIAL and (WR_CASE /= WR_FLUSH_POST_LAST) then
+                    if RW_BLAST then
+                      WR_CASE := WR_POST_FLUSH;  -- POST, FLUSH
+                    else
+                      WR_CASE := WR_POST;        -- POST
+                    end if;
+                  else
+                    WR_CASE := WR_FLUSH_POST;    -- FLUSH, POST
+                  end if;
+                end if;
+              else                      -- Single Write
+                if(WR_BUFFER_COUNT = 0) then
+                  WR_CASE := WR_POST_FLUSH;
+                elsif RW_SEQUENTIAL then  -- Buffer not Empty and burstable
+                  WR_CASE := WR_POST_FLUSH;
+                else
+                  WR_CASE := WR_FLUSH_POST_LAST;
+                end if;
+              end if;
+
+                                                                       --========================================================--
+                                                                       -- Actualy generate the bus cycles
+                                                                       --========================================================--
+                                                                       ------------------------------------------------------------
+                                                                       -- Post to the buffer
+                                                                       ------------------------------------------------------------
+              if(WR_CASE = WR_POST) or (WR_CASE = WR_POST_FLUSH) then  -- POST
+                WR_BUFFER(WR_BUFFER_PTR).ADDRESS := L_ADDR;
+                WR_BUFFER(WR_BUFFER_PTR).BE      := L_BE;
+                WR_BUFFER(WR_BUFFER_PTR).DATA    := L_DATA;
+                WR_BUFFER_COUNT                  := WR_BUFFER_COUNT + 1;
+                WR_BUFFER_PTR                    := WR_BUFFER_PTR + 1;
+              end if;
+
+                                                                                                               ------------------------------------------------------------
+                                                                                                               -- Flush the buffer (write it out to the interface)
+                                                                                                               ------------------------------------------------------------
+              if(WR_CASE = WR_FLUSH_POST) or (WR_CASE = WR_POST_FLUSH) or (WR_CASE = WR_FLUSH_POST_LAST) then  -- FLUSH
+
+                                        -- CHANGE: ADD CHECK FOR PROPER RDY
+
+
+                loop
+
+                  if(or_reduce(WR_BUFFER(0).ADDRESS(63 downto 32)) = '0') then  -- 32 bit address
+                    WR_TYPE := "0010";
+                  elsif(PRIMARY) then  -- Primary mode addresses are always 32 bit
+                    WR_TYPE := "0010";
+                  else                  -- 64 bit address
+                    WR_TYPE := "0011";
+                  end if;
+
+                                                   --------------------------------------------------------
+                                                   -- Header Phase
+                                                   --------------------------------------------------------
+                  OUT_DATA <= BAR_ATTRIBUTE_ARRAY(CURRENT_BAR).TC  -- Bits 31:29
+                                & BAR_ATTRIBUTE_ARRAY(CURRENT_BAR).S  -- Bit  28
+                                & WR_TYPE          -- Bits 27:24
+                                & WR_BUFFER(WR_BUFFER_COUNT-1).BE  -- Bits 23:20
+                                & WR_BUFFER(0).BE  -- Bits 19:16
+                                & "000"            -- Bits 15:13 
+                                & BAR_ATTRIBUTE_ARRAY(CURRENT_BAR).VC  -- Bit  12
+                                & "00"  -- Bits 11:10
+                                & to_vector(WR_BUFFER_COUNT, 10);  -- Bits 9:0
+                  OUT_VALID  <= '1';
+                  OUT_DFRAME <= '1';
+                  OUT_WR_REQ <= BAR_ATTRIBUTE_ARRAY(CURRENT_BAR).VC & (not BAR_ATTRIBUTE_ARRAY(CURRENT_BAR).VC);
+
+                  wait until(CLK'event and (CLK = '1'));
+
+                                             -- See if we want to insert random wait states
+                  if(IWAIT_RANDOM > 0) then  -- Insert wait states
+                    next_random(IWAIT_RND_SEED);
+                    if(test_random(IWAIT_RND_SEED, IWAIT_RANDOM)) then
+                      OUT_VALID <= '0';
+                      OUT_DATA  <= (others => 'X');
+                      next_random(IWAIT_RND_SEED);
+                      I_TMP     := range_random(IWAIT_RND_SEED, 1, IWAIT_RANDOM_N);
+                      for J in 1 to I_TMP loop
+                        wait until(CLK'event and (CLK = '1'));
+                      end loop;
+                      OUT_VALID <= '1';
+                    end if;
+                  end if;
+
+
+
+                                             --------------------------------------------------------
+                                             -- Address Phase
+                                             --------------------------------------------------------
+                  if(WR_TYPE = "0011") then  -- Do upper 32 bits of the address
+                    OUT_DATA <= WR_BUFFER(0).ADDRESS(63 downto 32);
+                    wait until(CLK'event and (CLK = '1'));
+
+
+                                               -- See if we want to insert random wait states
+                    if(IWAIT_RANDOM > 0) then  -- Insert wait states
+                      next_random(IWAIT_RND_SEED);
+                      if(test_random(IWAIT_RND_SEED, IWAIT_RANDOM)) then
+                        OUT_VALID <= '0';
+                        OUT_DATA  <= (others => 'X');
+                        next_random(IWAIT_RND_SEED);
+                        I_TMP     := range_random(IWAIT_RND_SEED, 1, IWAIT_RANDOM_N);
+                        for J in 1 to I_TMP loop
+                          wait until(CLK'event and (CLK = '1'));
+                        end loop;
+                        OUT_VALID <= '1';
+                      end if;
+                    end if;
+
+
+                  end if;
+
+                  if(PRIMARY) then
+                    OUT_DATA <= WR_BUFFER(0).ADDRESS(31 downto 2) & '0' & to_mvl(CURRENT_BAR);
+                  else
+                    OUT_DATA <= WR_BUFFER(0).ADDRESS(31 downto 2) & "00";
+                  end if;
+
+                  wait until(CLK'event and (CLK = '1'));
+
+                                        --------------------------------------------------------
+                                        -- Data Phase
+                                        --------------------------------------------------------
+                  for I in 0 to WR_BUFFER_COUNT-1 loop
+
+                                               -- See if we want to insert random wait states
+                    if(IWAIT_RANDOM > 0) then  -- Insert wait states
+                      next_random(IWAIT_RND_SEED);
+                      if(test_random(IWAIT_RND_SEED, IWAIT_RANDOM)) then
+                        OUT_VALID <= '0';
+                        OUT_DATA  <= (others => 'X');
+                        next_random(IWAIT_RND_SEED);
+                        I_TMP     := range_random(IWAIT_RND_SEED, 1, IWAIT_RANDOM_N);
+                        for J in 1 to I_TMP loop
+                          wait until(CLK'event and (CLK = '1'));
+                        end loop;
+                        OUT_VALID <= '1';
+                      end if;
+                    end if;
+
+                    OUT_DATA <= WR_BUFFER(I).DATA;
+                    if (I = (WR_BUFFER_COUNT-1)) then
+                      OUT_DFRAME <= '0';
+                    end if;
+                    wait until(CLK'event and (CLK = '1'));
+
+                  end loop;
+                  OUT_VALID  <= '0';
+                  OUT_DFRAME <= '0';
+                  OUT_DATA   <= (others => 'Z');
+                  OUT_WR_REQ <= (others => '0');
+
+                  WR_BUFFER_COUNT := 0;
+                  WR_BUFFER_PTR   := 0;
+
+                  if(WR_CASE = WR_FLUSH_POST_LAST) then  -- The flush must be executed twice
+                    WR_CASE                          := 99;
+                    WR_BUFFER(WR_BUFFER_PTR).ADDRESS := L_ADDR;
+                    WR_BUFFER(WR_BUFFER_PTR).BE      := L_BE;
+                    WR_BUFFER(WR_BUFFER_PTR).DATA    := L_DATA;
+                    WR_BUFFER_COUNT                  := WR_BUFFER_COUNT + 1;
+                    WR_BUFFER_PTR                    := WR_BUFFER_PTR + 1;
+                  else
+                    exit;
+                  end if;
+
+                end loop;
+
+              end if;
+
+                                                ------------------------------------------------------------
+                                                -- Post to the buffer
+                                                ------------------------------------------------------------
+              if(WR_CASE = WR_FLUSH_POST) then  --POST
+                WR_BUFFER(WR_BUFFER_PTR).ADDRESS := L_ADDR;
+                WR_BUFFER(WR_BUFFER_PTR).BE      := L_BE;
+                WR_BUFFER(WR_BUFFER_PTR).DATA    := L_DATA;
+                WR_BUFFER_COUNT                  := WR_BUFFER_COUNT + 1;
+                WR_BUFFER_PTR                    := WR_BUFFER_PTR + 1;
+              end if;
+
+                                        --========================================================--
+                                        -- Do Read Request Cycles 
+                                        --========================================================--
+                                        --*-------------------------------------------------------------
+                                        --* rd ADDR BE DATA MASK
+                                        --*
+                                        --*   - handles the rd/rdb command
+                                        --*
+                                        --* Read Cases:
+                                        --*
+                                        --*     rdb   Buffer   Read    Action
+                                        --*   command Empty  Burstable  Taken
+                                        --*
+                                        --*      N      Y        -      POST, IPR        : RD_CASE=RD_POST_IPR*
+                                        --*      N      N        Y      POST, IPR        : RD_CASE=RD_POST_IPR*
+                                        --*      N      N        N      IPR, POST_LAST   : RD_CASE=RD_IPR_POST_LAST*
+                                        --*      Y      Y        -      POST             : RD_CASE=RD_POST*
+                                        --*      Y      N        Y      POST             : RD_CASE=RD_POST*
+                                        --*      Y      N        N      IPR, POST        : RD_CASE=RD_IPR_POST*
+                                        --*
+                                        --* Note: POST means store the given read into the current CID buffer
+                                        --*       POST_LAST store the given read into the current CID buffer and mark as LAST (cannot be combined) 
+                                        --*       IPR means set the current buffer state to "In Progress" and load the next CID as current CID
+                                        --*       NEXT_CID means allocate the next CID and set as current CID
+                                        --*
+                                        --*-------------------------------------------------------------
+            else                        -- do read cycles
+
+              write(OUTPUT_LINE, string'("-- rd("));
+              write_hex_vector(OUTPUT_LINE, L_ADDR(63 downto 32));
+              write_hex_vector(OUTPUT_LINE, L_ADDR(31 downto 0));
+              write(OUTPUT_LINE, string'(", "));
+              write_hex_vector(OUTPUT_LINE, L_BE);
+              write(OUTPUT_LINE, string'(", "));
+              write_hex_vector(OUTPUT_LINE, L_DATA);
+              write(OUTPUT_LINE, string'(", "));
+              write_hex_vector(OUTPUT_LINE, L_MASK);
+              write(OUTPUT_LINE, string'(")"));
+              writeline(OUT_FILE, OUTPUT_LINE);
+
+              
+
+              if(RD_BUFFER_PTR > 0) then  -- current buffer has data
+
+                I_TMP         := To_Int(L_ADDR(31 downto 2));
+                RW_SEQUENTIAL := (To_Int(RD_ADDRESS(31 downto 2)) = I_TMP);
+              else
+
+                RD_ADDRESS    := L_ADDR;
+                RW_SEQUENTIAL := false;
+              end if;
+
+
+
+
+
+              if (L_CMD(3) = 'b') then  -- command is for a burst
+
+                if (RD_BUFFER_PTR = 0) then  -- Buffer Empty
+                  RD_CASE := RD_POST;
+                else                         -- Buffer Not Empty
+                  if(RW_SEQUENTIAL and not RW_BLAST) then
+                    RD_CASE := RD_POST;
+                  else
+                    RD_CASE := RD_IPR_POST;
+                  end if;
+                end if;
+              else                           -- Single Write
+
+                if(RD_BUFFER_PTR = 0) then  -- Buffer Empty
+                  RD_CASE := RD_POST_IPR;
+                elsif RW_SEQUENTIAL and (RD_CASE /= RD_IPR_POST_LAST) then  -- Buffer not Empty and burstable
+                  RD_CASE := RD_POST_IPR;
+                else
+                  RD_CASE := RD_IPR_POST_LAST;
+                end if;
+              end if;
+
+
+                                        ------------------------------------------------------------
+                                        -- Stall until it is safe to post another buffer
+                                        ------------------------------------------------------------
+              if(RD_BUFFER_PTR = 0) then
+
+                while(CURRENT_OUTBOUND_RD_IPR >= OUTBOUND_RD_OUTSTANDING) loop  -- we must stall
+                  wait until(CLK'event and (CLK = '1'));
+                end loop;
+
+                                        -- Allocate a new CID
+                NEXT_CID := CURRENT_CID;
+                for I in 0 to N_COMPLETION_ID-1 loop
+                  NEXT_CID := NEXT_CID + 1;
+                  if(NEXT_CID > (N_COMPLETION_ID-1)) then
+                    NEXT_CID := 0;
+                  end if;
+                  if(RD_BUFFER(NEXT_CID).STATE = OUTBOUND_READ_REQUEST_CPL_STATE(NEXT_CID)) then
+                    exit;
+                  end if;
+                end loop;
+                CURRENT_CID := NEXT_CID;
+
+              end if;
+
+
+                                                                     ------------------------------------------------------------
+                                                                     -- Post to the buffer
+                                                                     ------------------------------------------------------------
+              if(RD_CASE = RD_POST_IPR) or (RD_CASE = RD_POST) then  -- POST
+
+                if(RD_BUFFER_PTR = 0) then  -- buffer empty
+                  RD_BUFFER(CURRENT_CID).ADDRESS <= L_ADDR;
+                  RD_BUFFER(CURRENT_CID).BAR_HIT <= to_mvl(BAR_HIT(1)) & to_mvl(BAR_HIT(1));
+                  RD_BUFFER(CURRENT_CID).FBE     <= L_BE;
+                end if;
+
+                RD_BUFFER(CURRENT_CID).LBE                 <= L_BE;
+                RD_BUFFER(CURRENT_CID).DATA(RD_BUFFER_PTR) <= L_DATA;
+                RD_BUFFER(CURRENT_CID).MASK(RD_BUFFER_PTR) <= L_MASK;
+                RD_BUFFER_PTR                              := RD_BUFFER_PTR + 1;
+                RD_ADDRESS(RD_ADDRESS'high downto 2)       := L_ADDR(RD_ADDRESS'high downto 2) + '1';
+
+                wait on RD_BUFFER;
+
+              end if;
+
+                                                                                                         ------------------------------------------------------------
+                                                                                                         -- Flush the read buffer (send request packet and set the buffer state to in-progress)
+                                                                                                         ------------------------------------------------------------
+              if(RD_CASE = RD_POST_IPR) or (RD_CASE = RD_IPR_POST_LAST) or (RD_CASE = RD_IPR_POST) then  -- IPR
+                RD_BUFFER(CURRENT_CID).STATE <= not RD_BUFFER(CURRENT_CID).STATE;
+
+                                     --------------------------------------------------------
+                                     -- Do the Read Request
+                                     --------------------------------------------------------
+                if(or_reduce(RD_BUFFER(CURRENT_CID).ADDRESS(63 downto 32)) = '0') then  -- 32 bit address
+                  RD_TYPE := "0000";
+                elsif(PRIMARY) then  -- Primary mode addresses are always 32 bit
+                  RD_TYPE := "0000";
+                else                    -- 64 bit address
+                  RD_TYPE := "0001";
+                end if;
+
+
+                                                                 --------------------------------------------------------
+                                        -- Header Phase
+                                                                 --------------------------------------------------------
+                OUT_DATA <= BAR_ATTRIBUTE_ARRAY(CURRENT_BAR).TC  -- Bits 31:29
+                              & BAR_ATTRIBUTE_ARRAY(CURRENT_BAR).S   -- Bit  28
+                              & RD_TYPE                          -- Bits 27:24
+                              & RD_BUFFER(CURRENT_CID).LBE       -- Bits 23:20
+                              & RD_BUFFER(CURRENT_CID).FBE       -- Bits 19:16
+                              & "000"   -- Bits 15:13 
+                              & BAR_ATTRIBUTE_ARRAY(CURRENT_BAR).VC  -- Bit  12
+                              & to_vector(CURRENT_CID, 2)        -- Bits 11:10
+                              & to_vector(RD_BUFFER_PTR, 10);    -- Bits 9:0
+                OUT_VALID  <= '1';
+                OUT_DFRAME <= '1';
+
+                wait until(CLK'event and (CLK = '1'));
+
+
+                                           -- See if we want to insert random wait states
+                if(IWAIT_RANDOM > 0) then  -- Insert wait states
+                  next_random(IWAIT_RND_SEED);
+                  if(test_random(IWAIT_RND_SEED, IWAIT_RANDOM)) then
+                    OUT_VALID <= '0';
+                    OUT_DATA  <= (others => 'X');
+                    next_random(IWAIT_RND_SEED);
+                    I_TMP     := range_random(IWAIT_RND_SEED, 1, IWAIT_RANDOM_N);
+                    for J in 1 to I_TMP loop
+                      wait until(CLK'event and (CLK = '1'));
+                    end loop;
+                    OUT_VALID <= '1';
+                  end if;
+                end if;
+
+
+                                           --------------------------------------------------------
+                                           -- Address Phase
+                                           --------------------------------------------------------
+                if(RD_TYPE = "0001") then  -- Do upper 32 bits of the address
+                  OUT_DATA <= RD_BUFFER(CURRENT_CID).ADDRESS(63 downto 32);
+                  wait until(CLK'event and (CLK = '1'));
+
+
+                                             -- See if we want to insert random wait states
+                  if(IWAIT_RANDOM > 0) then  -- Insert wait states
+                    next_random(IWAIT_RND_SEED);
+                    if(test_random(IWAIT_RND_SEED, IWAIT_RANDOM)) then
+                      OUT_VALID <= '0';
+                      OUT_DATA  <= (others => 'X');
+                      next_random(IWAIT_RND_SEED);
+                      I_TMP     := range_random(IWAIT_RND_SEED, 1, IWAIT_RANDOM_N);
+                      for J in 1 to I_TMP loop
+                        wait until(CLK'event and (CLK = '1'));
+                      end loop;
+                      OUT_VALID <= '1';
+                    end if;
+                  end if;
+
+                end if;
+
+                if(PRIMARY) then
+                  OUT_DATA <= RD_BUFFER(CURRENT_CID).ADDRESS(31 downto 2) & '0' & to_mvl(CURRENT_BAR);
+                else
+                  OUT_DATA <= RD_BUFFER(CURRENT_CID).ADDRESS(31 downto 2) & "00";
+                end if;
+                OUT_DFRAME <= '0';
+
+                wait until(CLK'event and (CLK = '1'));
+                OUT_VALID <= '0';
+                OUT_DATA  <= (others => 'Z');
+
+
+                RD_BUFFER_PTR := 0;
+
+              end if;
+
+                                                                              ------------------------------------------------------------
+                                        -- Post to the buffer
+                                                                              ------------------------------------------------------------
+           
+              if(RD_CASE = RD_IPR_POST_LAST) or (RD_CASE = RD_IPR_POST) then  -- POST
+
+                if(RD_BUFFER_PTR = 0) then  -- buffer empty
+                  RD_BUFFER(CURRENT_CID).ADDRESS <= L_ADDR;
+                  RD_BUFFER(CURRENT_CID).FBE     <= L_BE;
+                end if;
+
+                RD_BUFFER(CURRENT_CID).LBE                 <= L_BE;
+                RD_BUFFER(CURRENT_CID).DATA(RD_BUFFER_PTR) <= L_DATA;
+                RD_BUFFER(CURRENT_CID).MASK(RD_BUFFER_PTR) <= L_MASK;
+                RD_BUFFER_PTR                              := RD_BUFFER_PTR + 1;
+                RD_ADDRESS(RD_ADDRESS'high downto 2)       := L_ADDR(RD_ADDRESS'high downto 2) + '1';
+
+              end if;
+
+
+            end if;
+
+            
+          elsif(BFM_BAR_HIT(0) or BFM_BAR_HIT(1)) then  -- Address hits one of the internal BFM RAM BARs
+
+             
+                                        ------------------------------------------------------------
+                                        -- Write data to the local BFM memory
+                                        ------------------------------------------------------------
+            A_TMP := L_ADDR(L_ADDR'high downto 2) & "00";
+            if (L_CMD(1 to 2) = "wr") then
+              for i in 0 to L_BE'high loop
+               
+                if (L_BE(i) = '1') then
+                  DATA_TMP8 := L_DATA(8*i+7 downto 8*i);
+
+
+write(OUTPUT_LINE, string'("-- Mem_Write("));
+write_hex_vector(OUTPUT_LINE, A_TMP(29 downto 0));
+write(OUTPUT_LINE, string'(", "));
+write_hex_vector(OUTPUT_LINE, DATA_TMP8);
+write(OUTPUT_LINE, string'(")"));
+writeline(OUT_FILE, OUTPUT_LINE);
+
+                  RAM_WR0      <= true;
+                  RAM_ADDR0    <= A_TMP(N_RAM_MAX-1 downto 0);
+                  RAM_WR_DATA0 <= DATA_TMP8;
+                  RAM_REQ0     <= not RAM_REQ0;
+                  wait on RAM_ACK0;
+
+                end if;
+                A_TMP := A_TMP + '1';
+              end loop;
+--              report "ENDLOOP";
+                                        ------------------------------------------------------------
+                                        -- Read data from the local BFM memory
+                                        ------------------------------------------------------------
+            else
+              for i in 0 to L_BE'high loop
+                if (L_BE(i) = '1') then
+                  RAM_WR0   <= false;
+                  RAM_ADDR0 <= A_TMP(N_RAM_MAX-1 downto 0);
+                  RAM_REQ0  <= not RAM_REQ0;
+                  wait on RAM_ACK0;
+                  DATA_TMP8 := RAM_RD_DATA0;
+
+--write(OUTPUT_LINE, string'("-- Mem_Read("));
+--write_hex_vector(OUTPUT_LINE, A_TMP(29 downto 0));
+--write(OUTPUT_LINE, string'(", "));
+--write_hex_vector(OUTPUT_LINE, DATA_TMP8);
+--write(OUTPUT_LINE, string'(")"));
+--writeline(OUT_FILE, OUTPUT_LINE);
+
+                  DATA_TMP32(8*i+7 downto 8*i) := DATA_TMP8;
+                  A_TMP                        := A_TMP + '1';
+                end if;
+              end loop;
+              read_cmp(OUTPUT_LINE, (INSTANCE_LABEL & "BFM Local Readback "), DATA_TMP32, L_DATA, L_MASK, vERR);
+
+
+            
+              if(vERR = 1) then writeline(OUT_FILE, OUTPUT_LINE); end if;
+              
+            end if;
+
+          else
+            write(OUTPUT_LINE, "-- ERROR: " & INSTANCE_LABEL);
+            write(OUTPUT_LINE, NOW, left, 0, ps);
+            if (L_CMD(1 to 2) = "wr") then
+              write(OUTPUT_LINE, string'(" The write command didn't match any BAR address :"));
+              write_hex_vector(OUTPUT_LINE, L_ADDR);
+            else
+              write(OUTPUT_LINE, string'(" The read command didn't match any BAR address :"));
+              write_hex_vector(OUTPUT_LINE, L_ADDR);
+            end if;
+            writeline(OUT_FILE, OUTPUT_LINE);
+          end if;
+
+          --*-------------------------------------------------------------
+          --* init: Set the model to all the default values
+          --*-------------------------------------------------------------
+        elsif (L_CMD(1 to 4) = "init") then
+          GENERATE_X              <= false;
+          EXPECT_ERROR            <= false;
+          T_LCLKi                 <= T_LCLK;
+          T_P2L_CLK_DLYi          <= T_P2L_CLK_DLY;
+          T_HOLD_OUTi             <= T_HOLD_OUT;
+          OUTBOUND_RD_OUTSTANDING <= 1;
+          RESPONSE_DELAY          <= 0;
+          BURST_LENGTH            := (others => 512);
+          N_BURST_MODULO          := (others => log2(512));
+          CURRENT_VC              := 0;
+          CPL_MODULO              := 64;
+          IWAIT_RANDOM            := 0;
+          IWAIT_RND_SEED          := 5000;
+                                        --CURRENT_BAR     := 0;
+
+          --*-------------------------------------------------------------
+          --* reset N: Drive RSTOUT18n, RSTOUT33n for N clocks
+          --*-------------------------------------------------------------
+        elsif (L_CMD(1 to 5) = "reset") then
+          SGET_INT(QCMD, CHAR_PTR, I_TMP);
+
+          RSTOUTo <= '1';
+
+          if(CMD_CLOCK_EN) then
+            for i in 1 to I_TMP loop
+              wait until (CLK'event and CLK = '1');
+            end loop;
+          else
+            wait for I_TMP * T_LCLKi;
+          end if;
+
+          RSTOUTo <= '0';
+
+          --*-------------------------------------------------------------
+          --* expect_error on|off|true|false: Expect read error
+          --*
+          --* When true|on read results will expect a completor error
+          --* and an ERROR will be reported if it is not
+          --*
+          --* When false|off read results will expect the error bit to be 
+          --* off and an ERROR will be reported if it is on
+          --*-------------------------------------------------------------
+        elsif (L_CMD(1 to 12) = "expect_error") then
+          SGET_BOOLEAN(QCMD, CHAR_PTR, B_TMP);
+          EXPECT_ERROR <= B_TMP;
+
+          --*-------------------------------------------------------------
+          --* generate_x on|off|true|false: Generate X when signals change
+          --*
+          --* When true|on output signals will go X after T_HOLD_OUT from 
+          --* clock when the signal transitions.  It will return to a valid
+          --* logic level after T_CO.
+          --*
+          --* CLK  ______~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+          --*            |
+          --*            |<-----------T_CO--------->|
+          --*            |                          |
+          --*            |<--T_HOLD_OUT-->|         |
+          --*                             |         |
+          --* SIG  ________________________XXXXXXXXXX~~~~~~~
+          --*
+          --*-------------------------------------------------------------
+        elsif (L_CMD(1 to 10) = "generate_x") then
+          SGET_BOOLEAN(QCMD, CHAR_PTR, B_TMP);
+          GENERATE_X <= B_TMP;
+
+          --*-------------------------------------------------------------
+          --* clk N: set clock period to N ps
+          --*-------------------------------------------------------------
+        elsif (L_CMD(1 to 4) = "lclk") then
+          SGET_INT(QCMD, CHAR_PTR, I_TMP);
+          T_LCLKi <= I_TMP * 1 ps;
+
+          --*-------------------------------------------------------------
+          --* iwait_random P N: Initiator random wait state insertion. 
+          --*-------------------------------------------------------------
+        elsif (L_CMD(1 to 12) = "iwait_random") then
+          SGET_INT(QCMD, CHAR_PTR, I_TMP);
+          SGET_INT(QCMD, CHAR_PTR, I_TMP2);
+          if(I_TMP < 0) or (I_TMP > 100) then
+            assert false report "WARNING: Specified iwait_random probability is out of the acceptable range of 0-100: setting to 0";
+            I_TMP := 0;
+          end if;
+          if(I_TMP2 < 0) or (I_TMP2 > 99) then
+            assert false report "WARNING: Specified iwait_random max wait is out of the acceptable range of 1-32: setting to 1";
+            I_TMP2 := 1;
+          end if;
+          IWAIT_RANDOM   := I_TMP;
+          IWAIT_RANDOM_N := I_TMP2;
+
+
+
+          --*-------------------------------------------------------------
+          --* bfm_bar BAR ADDR: Used to allocate local memory inside
+          --*                      the BFM for an external master to R/W.
+          --*-------------------------------------------------------------
+        elsif (L_CMD(1 to 7) = "bfm_bar") then
+          SGET_INT(QCMD, CHAR_PTR, I_TMP);         -- BAR
+          SGET_VECTOR_64(QCMD, CHAR_PTR, L_ADDR);  -- ADDR
+          SGET_INT(QCMD, CHAR_PTR, I_TMP2);        -- SIZE
+                                                   -- Check BAR
+          if(I_TMP /= 0) then
+            I_TMP := 1;
+          end if;
+
+                                        -- Check SIZE
+          if(I_TMP2 /= (2**log2(I_TMP2))) then
+            assert false report "WARNING: Specified SIZE is not a power of 2, rounding down.";
+            I_TMP2 := (2**log2(I_TMP2));
+          end if;
+          I_TMP2                              := I_TMP2 - 1;
+          AMASK_TMP                           := to_vector(I_TMP2, AMASK_TMP'length);
+          AMASK_TMP                           := not AMASK_TMP;
+          BFM_BAR_ATTRIBUTE_ARRAY(I_TMP).MASK <= AMASK_TMP;
+
+                                        -- Check BASE
+          if(L_ADDR(AMASK_TMP'range) /= (L_ADDR(AMASK_TMP'range) and AMASK_TMP)) then
+            assert false report "WARNING: Specified Base Address will be alligned to match the BAR size.";
+            L_ADDR(AMASK_TMP'range) := (L_ADDR(AMASK_TMP'range) and AMASK_TMP);
+          end if;
+          BFM_BAR_ATTRIBUTE_ARRAY(I_TMP).BASE <= L_ADDR;
+
+                                        -- Make sure that BFM_BAR_ATTRIBUTE_ARRAY is updated before continuing
+          wait until (BFM_BAR_ATTRIBUTE_ARRAY'event);
+
+          write(OUTPUT_LINE, "-- " & INSTANCE_LABEL);
+          write(OUTPUT_LINE, string'("BFM BAR set to: BAR="));
+          write(OUTPUT_LINE, I_TMP);
+
+          write(OUTPUT_LINE, string'(", BASE=0x"));
+          write_hex_vector(OUTPUT_LINE, BFM_BAR_ATTRIBUTE_ARRAY(I_TMP).BASE);
+
+          write(OUTPUT_LINE, string'(", MASK=0x"));
+          write_hex_vector(OUTPUT_LINE, BFM_BAR_ATTRIBUTE_ARRAY(I_TMP).MASK);
+
+          writeline(OUT_FILE, OUTPUT_LINE);
+
+
+          --*-------------------------------------------------------------
+          --* bar BAR ADDR SIZE VC TC: Sets up one of the base address 
+          --*                    registers.  All read/write transactions 
+          --*                    that match a BAR will be routed external.
+          --*-------------------------------------------------------------
+        elsif (L_CMD(1 to 3) = "bar") then
+          SGET_INT(QCMD, CHAR_PTR, I_TMP);         -- BAR
+          SGET_VECTOR_64(QCMD, CHAR_PTR, L_ADDR);  -- ADDR
+          SGET_INT(QCMD, CHAR_PTR, I_TMP2);        -- SIZE
+          SGET_INT(QCMD, CHAR_PTR, I_TMP3);        -- VC
+          SGET_INT(QCMD, CHAR_PTR, I_TMP4);        -- TC
+          SGET_INT(QCMD, CHAR_PTR, I_TMP5);        -- S
+
+                                        -- Check BAR
+          if(I_TMP /= 0) then
+            I_TMP := 1;
+          end if;
+
+                                        -- Check SIZE
+          if(I_TMP2 /= (2**log2(I_TMP2))) then
+            assert false report "WARNING: Specified SIZE is not a power of 2, rounding down.";
+            I_TMP2 := (2**log2(I_TMP2));
+          end if;
+          I_TMP2                          := I_TMP2 - 1;
+          AMASK_TMP                       := to_vector(I_TMP2, AMASK_TMP'length);
+          AMASK_TMP                       := not AMASK_TMP;
+          BAR_ATTRIBUTE_ARRAY(I_TMP).MASK <= AMASK_TMP;
+
+                                        -- Check BASE
+          if(L_ADDR(AMASK_TMP'range) /= (L_ADDR(AMASK_TMP'range) and AMASK_TMP)) then
+            assert false report "WARNING: Specified Base Address will be alligned to match the BAR size.";
+            L_ADDR(AMASK_TMP'range) := (L_ADDR(AMASK_TMP'range) and AMASK_TMP);
+          end if;
+          BAR_ATTRIBUTE_ARRAY(I_TMP).BASE <= L_ADDR;
+
+                                        -- Check VC
+          if(I_TMP3 < 0) or (I_TMP3 > 1) then
+            assert false report "WARNING: Specified VC is out of range, will be set to 0.";
+            I_TMP3 := 0;
+          end if;
+          if(I_TMP3 /= 0) then
+            BAR_ATTRIBUTE_ARRAY(I_TMP).VC <= '1';
+          else
+            BAR_ATTRIBUTE_ARRAY(I_TMP).VC <= '0';
+          end if;
+
+                                        -- Check Trafic Class
+          if(I_TMP4 < 0) or (I_TMP4 > 7) then
+            assert false report "WARNING: Specified TC is out of range, will be set to 0.";
+            I_TMP4 := 0;
+          end if;
+          BAR_ATTRIBUTE_ARRAY(I_TMP).TC <= to_vector(I_TMP4, 3);
+
+                                        -- Check Trafic Class
+          if(I_TMP5 < 0) or (I_TMP5 > 1) then
+            assert false report "WARNING: Specified NoSnoop is out of range, will be set to 0.";
+            I_TMP5 := 0;
+          end if;
+          BAR_ATTRIBUTE_ARRAY(I_TMP).S <= to_mvl(I_TMP5);
+
+
+                                        -- Make sure that BAR_ATTRIBUTE_ARRAY is updated before continuing
+          wait until (BAR_ATTRIBUTE_ARRAY'event);
+
+          write(OUTPUT_LINE, "-- " & INSTANCE_LABEL);
+          write(OUTPUT_LINE, string'("BAR set to: BAR="));
+          write(OUTPUT_LINE, I_TMP);
+
+          write(OUTPUT_LINE, string'(", BASE=0x"));
+          write_hex_vector(OUTPUT_LINE, BAR_ATTRIBUTE_ARRAY(I_TMP).BASE);
+
+          write(OUTPUT_LINE, string'(", MASK=0x"));
+          write_hex_vector(OUTPUT_LINE, BAR_ATTRIBUTE_ARRAY(I_TMP).MASK);
+
+          write(OUTPUT_LINE, string'(", VC="));
+          write(OUTPUT_LINE, To_bit(BAR_ATTRIBUTE_ARRAY(I_TMP).VC));
+
+          write(OUTPUT_LINE, string'(", TC=0x"));
+          write_hex_vector(OUTPUT_LINE, BAR_ATTRIBUTE_ARRAY(I_TMP).TC);
+
+          write(OUTPUT_LINE, string'(", S="));
+          write(OUTPUT_LINE, To_bit(BAR_ATTRIBUTE_ARRAY(I_TMP).S));
+
+          writeline(OUT_FILE, OUTPUT_LINE);
+
+
+
+          --*-------------------------------------------------------------
+          --* rd_outstanding_out N: This determines how many outstanding  
+          --*               reads the BFM will generate before stalling.
+          --*-------------------------------------------------------------
+        elsif (L_CMD(1 to 18) = "rd_outstanding_out") then
+          SGET_INT(QCMD, CHAR_PTR, I_TMP);  -- N
+
+          if(I_TMP < 1) or (I_TMP > 4) then
+            assert false report "WARNING: Specified reads outstanding is out of the acceptable range of 1-4: setting to 1";
+            I_TMP := 1;
+          end if;
+
+          OUTBOUND_RD_OUTSTANDING <= I_TMP;
+
+          write(OUTPUT_LINE, "-- " & INSTANCE_LABEL);
+          write(OUTPUT_LINE, string'("rd_outstanding_out set to: "));
+          write(OUTPUT_LINE, I_TMP);
+          writeline(OUT_FILE, OUTPUT_LINE);
+
+          --*-------------------------------------------------------------
+          --* cpl_modulo N: Sets the burst modulo boundary that 
+          --*               read completion packets will get truncated to.
+          --*-------------------------------------------------------------
+        elsif (L_CMD(1 to 10) = "cpl_modulo") then
+          SGET_INT(QCMD, CHAR_PTR, I_TMP);  -- N
+
+          if(I_TMP < 8) or (I_TMP > 512) then
+            assert false report "WARNING: Specified completion modulo is out of the acceptable range of 8-512 bytes: setting to 512 bytes";
+            I_TMP := 512;
+          end if;
+
+          if((2**log2(I_TMP)) /= I_TMP) then
+            I_TMP := (2**log2(I_TMP));
+            write(OUTPUT_LINE, string'("-- WARNING: Specified completion modulo is not a power of 2: setting to "));
+            write(OUTPUT_LINE, I_TMP);
+            write(OUTPUT_LINE, string'(" bytes"));
+            writeline(OUT_FILE, OUTPUT_LINE);
+          end if;
+          CPL_MODULO := I_TMP;
+
+          write(OUTPUT_LINE, "-- " & INSTANCE_LABEL & "cpl_modulo set to: ");
+          write(OUTPUT_LINE, I_TMP);
+          write(OUTPUT_LINE, string'(" bytes"));
+          writeline(OUT_FILE, OUTPUT_LINE);
+
+          --*-------------------------------------------------------------
+          --* cpl_order N: Completion order: Determines the order of 
+          --*               completion packets put out by the BFM.
+          --*-------------------------------------------------------------
+        elsif (L_CMD(1 to 9) = "cpl_order") then
+          SGET_BOOLEAN(QCMD, CHAR_PTR, B_TMP);  -- N
+
+
+          CPL_ORDER := B_TMP;
+
+          write(OUTPUT_LINE, "-- " & INSTANCE_LABEL);
+          write(OUTPUT_LINE, string'("cpl_modulo set to: "));
+          write(OUTPUT_LINE, I_TMP);
+          write(OUTPUT_LINE, string'(" bytes"));
+          writeline(OUT_FILE, OUTPUT_LINE);
+
+          --*-------------------------------------------------------------
+          --* burst_modulo N BAR: Sets the burst modulo boundary that 
+          --*                    read/write packets will get truncated to.
+          --*-------------------------------------------------------------
+        elsif (L_CMD(1 to 12) = "burst_modulo") then
+          SGET_INT(QCMD, CHAR_PTR, I_TMP);   -- N
+          SGET_INT(QCMD, CHAR_PTR, I_TMP2);  -- BAR
+          if(I_TMP2 < 0) or (I_TMP2 > (N_BARS-1)) then
+            assert false report "WARNING: Specified Virtual Channel is out of range, defaulting to 1";
+            I_TMP2 := 1;
+          else
+            N_BURST_MODULO(I_TMP2) := log2(I_TMP);
+          end if;
+
+          if(I_TMP < 8) or (I_TMP > 4096) then
+            assert false report "WARNING: Specified burst modulo is out of the acceptable range of 8-4096 bytes: setting to 512 bytes";
+            I_TMP := 512;
+          end if;
+
+          if((2**log2(I_TMP)) /= I_TMP) then
+            I_TMP := (2**log2(I_TMP));
+            write(OUTPUT_LINE, string'("-- WARNING: Specified burst modulo is not a power of 2: setting to "));
+            write(OUTPUT_LINE, I_TMP);
+            write(OUTPUT_LINE, string'(" bytes"));
+            writeline(OUT_FILE, OUTPUT_LINE);
+          end if;
+          N_BURST_MODULO(I_TMP2) := log2(I_TMP);
+
+
+          write(OUTPUT_LINE, "-- " & INSTANCE_LABEL);
+          write(OUTPUT_LINE, string'("burst_modulo set to: ") & to_str(I_TMP) & " bytes for BAR" & to_str(I_TMP2));
+          writeline(OUT_FILE, OUTPUT_LINE);
+
+
+
+          --*-------------------------------------------------------------
+          --* burst_length N BAR: Sets the burst length that 
+          --*                    read/write packets will get truncated to.
+          --*-------------------------------------------------------------
+        elsif (L_CMD(1 to 12) = "burst_length") then
+          SGET_INT(QCMD, CHAR_PTR, I_TMP);   -- N
+          SGET_INT(QCMD, CHAR_PTR, I_TMP2);  -- BAR
+          if(I_TMP2 < 0) or (I_TMP2 > (N_BARS-1)) then
+            assert false report "WARNING: Specified Virtual Channel is out of range, defaulting to 1";
+            I_TMP2 := 1;
+          end if;
+
+          if(I_TMP < 0) or (I_TMP > 4096) then
+            assert false report "WARNING: Specified burst length is out of range: setting to 512 bytes";
+            BURST_LENGTH(I_TMP2) := 512;
+          else
+            BURST_LENGTH(I_TMP2) := I_TMP;
+          end if;
+
+          write(OUTPUT_LINE, "-- " & INSTANCE_LABEL);
+          write(OUTPUT_LINE, string'("burst_length set to: ") & to_str(BURST_LENGTH(I_TMP2)) & " for BAR" & to_str(I_TMP2));
+          writeline(OUT_FILE, OUTPUT_LINE);
+
+          --*-------------------------------------------------------------
+          --* wait N: Idle for N clocks
+          --*-------------------------------------------------------------
+        elsif (L_CMD(1 to 4) = "wait") then
+          SGET_INT(QCMD, CHAR_PTR, I_TMP);
+
+          for i in 1 to I_TMP loop
+            wait until (CLK'event and (CLK = '1'));
+          end loop;
+          --*-------------------------------------------------------------
+          --* gpio_wait N P MASK        Wait for N local bus clock intervals for GPIO to reach a defined state.  
+          --*-------------------------------------------------------------
+        elsif (L_CMD(1 to 9) = "gpio_wait") then
+          SGET_INT(QCMD, CHAR_PTR, I_TMP);
+          SGET_VECTOR(QCMD, CHAR_PTR, L_DATA);
+          SGET_VECTOR(QCMD, CHAR_PTR, L_MASK);
+
+          for i in 1 to I_TMP loop
+            wait until (CLK'event and (CLK = '1'));
+            I_TMP2 := i;
+                                        -- if(and_reduce(((GPIOi and L_MASK(GPIO'range)) xnor (L_DATA(GPIO'range) and L_MASK(GPIO'range)))) = 0) then
+            if(((GPIOi and L_MASK(GPIO'range)) = (L_DATA(GPIO'range) and L_MASK(GPIO'range)))) then
+              exit;
+            end if;
+          end loop;
+          if(I_TMP2 = I_TMP) then       -- command timed out
+            write(OUTPUT_LINE, "-- " & INSTANCE_LABEL & string'("ERROR: gpio_wait command timed out"));
+            writeline(OUT_FILE, OUTPUT_LINE);
+          end if;
+
+
+          --*-------------------------------------------------------------
+          --* flush N: Idle for N clocks
+          --*-------------------------------------------------------------
+        elsif (L_CMD(1 to 5) = "flush") then
+          SGET_INT(QCMD, CHAR_PTR, I_TMP);
+
+          for i in 1 to I_TMP loop
+            if(CURRENT_OUTBOUND_RD_IPR = 0) then
+              exit;
+            end if;
+            wait until (CLK'event and (CLK = '1'));
+          end loop;
+
+          if(CURRENT_OUTBOUND_RD_IPR /= 0) then
+            write(OUTPUT_LINE, "-- " & INSTANCE_LABEL);
+            write(OUTPUT_LINE, string'("ERROR: flush command timed out"));
+            writeline(OUT_FILE, OUTPUT_LINE);
+          end if;
+
+        elsif (L_CMD(1 to 2) /= "--") and (L_CMD(1 to 2) /= "//") and (L_CMD(1) /= ' ') then
+          assert false report "Unrecognized command";
+        end if;
+
+      end if;
+
+      --============================================================--
+      -- Send out a completion packet
+      --============================================================--
+      if(CURRENT_INBOUND_RD_IPR /= 0) then  -- Send out a read completion
+
+        ------------------------------------------------------------
+        -- Make sure we are alligned to the clock
+        ------------------------------------------------------------
+        if not(CLK'event and (CLK = '1')) then
+          wait until (CLK'event and (CLK = '1'));
+        end if;
+
+        ------------------------------------------------------------
+        -- Select the next CID to service (does a round robin and supports OoO)
+        ------------------------------------------------------------
+
+        for I in 0 to N_COMPLETION_ID-1 loop
+
+                                        -- Point to the next possible Completion ID
+          if(CPL_ORDER or (I /= 0)) then
+            CPL_CID := CPL_CID + 1;
+          end if;
+          if(CPL_CID >= N_COMPLETION_ID) then
+            CPL_CID := 0;
+          end if;
+
+          if(INBOUND_READ_REQUEST_ARRAY(CPL_CID).STATE /= INBOUND_READ_REQUEST_CPL_STATE(CPL_CID)) then
+            exit;
+          end if;
+        end loop;
+
+        ------------------------------------------------------------
+        -- Header Phase
+        ------------------------------------------------------------
+
+        if(INBOUND_READ_REQUEST_ARRAY(CPL_CID).LENGTH = 0) then
+          RD_TYPE := "0100";
+        else
+          RD_TYPE := "0101";
+        end if;
+
+
+-- Start Address = INBOUND_READ_REQUEST_ARRAY(CPL_CID).ADDRESS(31:2)
+
+        -- Start DW Address
+
+        A_TMP                            := (others => '0');
+        A_TMP(RAM_ADDR0'high)            := INBOUND_READ_REQUEST_ARRAY(CPL_CID).BAR_HIT(1);
+        A_TMP(RAM_ADDR0'high-1 downto 2) := INBOUND_READ_REQUEST_ARRAY(CPL_CID).ADDRESS(RAM_ADDR0'high-1 downto 2);
+
+        I_TMP := to_int(A_TMP(RAM_ADDR0'high downto 2));  -- This is the DW base address relative to the BFM internal RAM
+        I_TMP := I_TMP + CPL_POP_PTR(CPL_CID);  -- This is the DW base address + offset
+
+--                              I_TMP := to_int(INBOUND_READ_REQUEST_ARRAY(CPL_CID).BAR_HIT(1) 
+--                                              & INBOUND_READ_REQUEST_ARRAY(CPL_CID).ADDRESS(RAM_ADDR0'high-1 downto 2))
+--                                     + CPL_POP_PTR(CPL_CID);
+
+
+        A_TMP                  := (others => '0');
+        A_TMP(RAM_ADDR0'range) := to_vector(I_TMP, RAM_ADDR0'length-2) & "00";
+
+        CPL_LENGTH := (CPL_MODULO/4) - (I_TMP mod (CPL_MODULO/4));  -- This determines the modulo alligned length
+        CPL_LENGTH := minimum(INBOUND_READ_REQUEST_ARRAY(CPL_CID).LENGTH - CPL_POP_PTR(CPL_CID), CPL_LENGTH);
+
+        CPL_LAST := to_mvl((CPL_POP_PTR(CPL_CID) + CPL_LENGTH) >= INBOUND_READ_REQUEST_ARRAY(CPL_CID).LENGTH);
+
+
+        OUT_DATA <= INBOUND_READ_REQUEST_ARRAY(CPL_CID).TC   -- Bits 31:29 (TC)
+                      & '0'             -- Bit  28    (unused)
+                      & RD_TYPE         -- Bits 27:24 (TYPE)
+                      & "000000"        -- Bits 23:18 (unused)
+                      & "00"  -- Bits 17:16 (STAT) -- CHANGE to add exception cases
+                      & CPL_LAST        -- Bits 15    (L) 
+                      & "00"            -- Bits 14:13 (unused)
+                      & BAR_ATTRIBUTE_ARRAY(CURRENT_BAR).VC  -- Bit  12
+                      & to_vector(CPL_CID, 2)                -- Bits 11:10
+                      & to_vector(CPL_LENGTH, 10);           -- Bits 9:0
+        OUT_VALID  <= '1';
+        OUT_DFRAME <= '1';
+
+        wait until(CLK'event and (CLK = '1'));
+
+        -- See if we want to insert random wait states
+        if(IWAIT_RANDOM > 0) then       -- Insert wait states
+          next_random(IWAIT_RND_SEED);
+          if(test_random(IWAIT_RND_SEED, IWAIT_RANDOM)) then
+            OUT_VALID <= '0';
+            OUT_DATA  <= (others => 'X');
+            next_random(IWAIT_RND_SEED);
+            I_TMP     := range_random(IWAIT_RND_SEED, 1, IWAIT_RANDOM_N);
+            for J in 1 to I_TMP loop
+              wait until(CLK'event and (CLK = '1'));
+            end loop;
+            OUT_VALID <= '1';
+          end if;
+        end if;
+
+
+
+        ------------------------------------------------------------
+        -- Data Phase
+        ------------------------------------------------------------
+
+        if(INBOUND_READ_REQUEST_ARRAY(CPL_CID).LENGTH = 0) then  -- no data to return
+          OUT_DATA <= (others => 'X');
+          wait until(CLK'event and (CLK = '1'));
+
+        else                            -- send back data
+
+          RAM_WR0 <= false;
+
+          for I in 1 to CPL_LENGTH loop
+
+            for J in 0 to 3 loop        -- Do all 4 bytes of the read
+
+              RAM_ADDR0 <= A_TMP(RAM_ADDR0'range);
+              RAM_REQ0  <= not RAM_REQ0;
+
+              wait on RAM_ACK0;
+
+              OUT_DATA(J*8+7 downto J*8) <= RAM_RD_DATA0;
+
+              A_TMP := A_TMP + '1';
+            end loop;
+
+            if(I = CPL_LENGTH) then
+              OUT_DFRAME <= '0';
+            end if;
+
+            wait until(CLK'event and (CLK = '1'));
+
+            CPL_POP_PTR(CPL_CID) := CPL_POP_PTR(CPL_CID) + 1;
+
+                                        -- See if we want to insert random wait states
+            if(IWAIT_RANDOM > 0) then   -- Insert wait states
+              next_random(IWAIT_RND_SEED);
+              if(test_random(IWAIT_RND_SEED, IWAIT_RANDOM)) then
+                OUT_VALID <= '0';
+                OUT_DATA  <= (others => 'X');
+                next_random(IWAIT_RND_SEED);
+                I_TMP     := range_random(IWAIT_RND_SEED, 1, IWAIT_RANDOM_N);
+                for J in 1 to I_TMP loop
+                  wait until(CLK'event and (CLK = '1'));
+                end loop;
+                OUT_VALID <= '1';
+              end if;
+            end if;
+
+
+
+          end loop;
+
+        end if;
+
+        if(CPL_LAST = '1') then         -- Retire the CID
+          CPL_POP_PTR(CPL_CID)                    := 0;
+          INBOUND_READ_REQUEST_CPL_STATE(CPL_CID) <= INBOUND_READ_REQUEST_ARRAY(CPL_CID).STATE;
+          wait on INBOUND_READ_REQUEST_CPL_STATE;
+        end if;
+
+        OUT_VALID  <= '0';
+        OUT_DFRAME <= '0';
+        OUT_DATA   <= (others => 'Z');
+
+
+      end if;
+
+    end loop;
+  end process;
+
+--#########################################################################--
+--
+-- InBound State Machine 
+--
+-- (Handles RX to the BFM: L2P for Primary and P2L for secondary)
+--
+--#########################################################################--
+  process
+    --file      OUT_FILE : text is out "STD_OUTPUT";
+    file OUT_FILE        : text open write_mode is "NullFile";
+    variable OUTPUT_LINE : line;
+
+    variable HEADER_TC        : std_ulogic_vector(2 downto 0);
+    variable HEADER_S         : std_ulogic;
+    variable HEADER_TYPE      : std_ulogic_vector(3 downto 0);
+    variable HEADER_LBE       : std_ulogic_vector(3 downto 0);
+    variable HEADER_FBE       : std_ulogic_vector(3 downto 0);
+    variable HEADER_STAT      : std_ulogic_vector(1 downto 0);
+    variable HEADER_L         : std_ulogic;
+    variable HEADER_V         : std_ulogic;
+    variable HEADER_CID       : std_ulogic_vector(1 downto 0);
+    variable HEADER_LENGTH    : std_ulogic_vector(9 downto 0);
+    variable HEADER           : std_ulogic_vector(IN_DATA'range);
+    variable HEADER_ADDR_LOW  : std_ulogic_vector(IN_DATA'range);
+    variable HEADER_ADDR_HI   : std_ulogic_vector(IN_DATA'range);
+    variable BFM_BAR_HIT      : boolean_vector(1 downto 0);
+    variable RAM_ADDR         : std_ulogic_vector(N_RAM_MAX-1 downto 0);
+    variable RAM_BE           : std_ulogic_vector(3 downto 0);
+    variable RAM_DATA         : std_ulogic_vector(7 downto 0);
+    variable INBOUND_LABEL    : string(1 to 3);
+    variable FIRST_DW         : boolean;
+    variable COMPLETION_ID    : integer;  -- 
+    variable CID_COUNT        : integer;
+    variable I_HEADER_LENGTH  : integer;
+    variable I_CID            : integer;
+    variable CMP_DATA         : std_ulogic_vector(IN_DATA'range);
+    variable RD_DATA, RD_MASK : std_ulogic_vector(IN_DATA'range);
+
+    variable RD_BUFFER_PTR   : INT_VECTOR(N_COMPLETION_ID-1 downto 0);
+    variable RD_CPL_RAM_ADDR : DATA32(N_COMPLETION_ID-1 downto 0);
+    variable vERR            : integer;
+    variable TMP_I           : integer;
+    variable TMP32           : std_ulogic_vector(31 downto 0);
+
+  begin
+
+    if(MODE_PRIMARY) then
+      INBOUND_LABEL := "L2P";
+    else
+      INBOUND_LABEL := "P2L";
+    end if;
+
+    COMPLETION_ID := 0;
+    for I in 0 to N_COMPLETION_ID-1 loop
+      INBOUND_READ_REQUEST_ARRAY(I).STATE <= false;
+      RD_CPL_RAM_ADDR(I)                  := (others => '0');
+    end loop;
+
+    RD_BUFFER_PTR := (others => 0);
+
+    loop
+      CMD_RD_DATA_VALID <= '0';
+
+      wait until(ICLK'event and (ICLK = '1'));
+
+      if((IN_DFRAME and IN_VALID) = '1') then  -- Start of a packet
+        --*-------------------------------------------------------------
+        --* Header Phase
+        --*-------------------------------------------------------------
+        HEADER        := IN_DATA;
+        HEADER_TC     := IN_DATA(31 downto 29);
+        HEADER_S      := IN_DATA(28);
+        HEADER_TYPE   := IN_DATA(27 downto 24);
+        HEADER_LBE    := IN_DATA(23 downto 20);
+        HEADER_FBE    := IN_DATA(19 downto 16);
+        HEADER_STAT   := IN_DATA(17 downto 16);
+        HEADER_L      := IN_DATA(15);
+        HEADER_V      := IN_DATA(12);
+        HEADER_CID    := IN_DATA(11 downto 10);
+        HEADER_LENGTH := IN_DATA(9 downto 0);
+        if(Q_IN_DFRAME = '1') then
+          write(OUTPUT_LINE, "-- ERROR: " & INSTANCE_LABEL);
+          write(OUTPUT_LINE, NOW);
+          write(OUTPUT_LINE, ": " & INBOUND_LABEL &" Packet is Improperly Framed (Doesn't start with a L-to-H transition on DFRAME)");
+          writeline(OUT_FILE, OUTPUT_LINE);
+        end if;
+
+        COMPLETION_ID := to_int(HEADER_CID);
+
+        --*-------------------------------------------------------------
+        --* Address Phase
+        --*-------------------------------------------------------------
+        if((HEADER_TYPE = "0000") or (HEADER_TYPE = "0010")) then  -- 32 bit address
+          wait until(ICLK'event and (ICLK = '1') and (IN_VALID = '1'));
+          HEADER_ADDR_LOW := IN_DATA;
+          HEADER_ADDR_HI  := (others => '0');
+
+        elsif((HEADER_TYPE = "0001") or (HEADER_TYPE = "0011")) then  -- 64 bit address
+          wait until(ICLK'event and (ICLK = '1') and (IN_VALID = '1'));
+          HEADER_ADDR_HI  := IN_DATA;
+          wait until(ICLK'event and (ICLK = '1') and (IN_VALID = '1'));
+          HEADER_ADDR_LOW := IN_DATA;
+
+        elsif(HEADER_TYPE = "0100") then  -- Completion: No Data
+--                                      wait until(ICLK'event and (ICLK = '1') and (IN_VALID = '1'));
+
+        elsif(HEADER_TYPE = "0101") then  -- Completion: With Data
+
+        else
+          write(OUTPUT_LINE, "-- ERROR: " & INSTANCE_LABEL);
+          write(OUTPUT_LINE, NOW);
+          write(OUTPUT_LINE, string'(" Unrecognized TYPE=0x"));
+          write_hex_vector(OUTPUT_LINE, HEADER);
+          write(OUTPUT_LINE, string'(" --> Header Word=0x"));
+          write_hex_vector(OUTPUT_LINE, HEADER);
+          writeline(OUT_FILE, OUTPUT_LINE);
+        end if;
+
+        if(PRIMARY) then
+          BFM_BAR_HIT(0) := (((HEADER_ADDR_LOW and BFM_BAR_ATTRIBUTE_ARRAY(0).MASK) = BFM_BAR_ATTRIBUTE_ARRAY(0).BASE(31 downto 0))
+                             and (HEADER_ADDR_HI = BFM_BAR_ATTRIBUTE_ARRAY(0).BASE(63 downto 32)));
+          BFM_BAR_HIT(1) := (((HEADER_ADDR_LOW and BFM_BAR_ATTRIBUTE_ARRAY(1).MASK) = BFM_BAR_ATTRIBUTE_ARRAY(1).BASE(31 downto 0))
+                             and (HEADER_ADDR_HI = BFM_BAR_ATTRIBUTE_ARRAY(1).BASE(63 downto 32)));
+        else
+          if(HEADER_ADDR_LOW(1 downto 0) = "00") then
+            BFM_BAR_HIT(0) := true;
+            BFM_BAR_HIT(1) := false;
+          elsif(HEADER_ADDR_LOW(1 downto 0) = "01") then
+            BFM_BAR_HIT(0) := false;
+            BFM_BAR_HIT(1) := true;
+          else
+            BFM_BAR_HIT(0) := false;
+            BFM_BAR_HIT(1) := false;
+          end if;
+        end if;
+
+        RAM_ADDR := to_mvl(BFM_BAR_HIT(1)) & HEADER_ADDR_LOW(RAM_ADDR'high-1 downto 2) & "00";
+        RAM_BE   := HEADER_FBE;
+        FIRST_DW := true;
+
+        --*-------------------------------------------------------------
+        --* Data Phase - Write
+        --*-------------------------------------------------------------
+        if((HEADER_TYPE = "0010") or (HEADER_TYPE = "0011")) then  -- Write Packet
+          if(BFM_BAR_HIT(0) or BFM_BAR_HIT(1)) then
+            L1 : loop
+              wait until(ICLK'event and (ICLK = '1') and (IN_VALID = '1'));
+              if(FIRST_DW) then
+                RAM_BE := HEADER_FBE;
+              elsif(IN_DFRAME /= '1') then
+                RAM_BE := HEADER_LBE;
+              else
+                RAM_BE := (others => '1');
+              end if;
+
+              for i in 0 to RAM_BE'high loop
+                if (RAM_BE(i) = '1') then
+                  RAM_DATA := IN_DATA(8*i+7 downto 8*i);
+
+
+--write(OUTPUT_LINE, string'("-- " & INSTANCE_LABEL & INBOUND_LABEL &" Mem_Write("));
+--write_hex_vector(OUTPUT_LINE, RAM_ADDR);
+--write(OUTPUT_LINE, string'(", "));
+--write_hex_vector(OUTPUT_LINE, RAM_DATA);
+--write(OUTPUT_LINE, string'(")"));
+--writeline(OUT_FILE, OUTPUT_LINE);
+
+                  RAM_WR1      <= true;
+                  RAM_ADDR1    <= RAM_ADDR;
+                  RAM_WR_DATA1 <= RAM_DATA;
+                  RAM_REQ1     <= not RAM_REQ1;
+                  wait on RAM_ACK1;
+
+                end if;
+                RAM_ADDR := RAM_ADDR + '1';
+              end loop;
+
+              FIRST_DW := false;
+
+              if(IN_DFRAME /= '1') then
+                exit L1;
+              end if;
+            end loop;
+          else
+            write(OUTPUT_LINE, "-- ERROR: " & INSTANCE_LABEL);
+            write(OUTPUT_LINE, NOW);
+            write(OUTPUT_LINE, (" " & INBOUND_LABEL & " Address didn't match any local BAR: HEADER=0x"));
+            write_hex_vector(OUTPUT_LINE, HEADER);
+            write(OUTPUT_LINE, string'(", ADDRESS=0x"));
+            write_hex_vector(OUTPUT_LINE, HEADER_ADDR_HI);
+            write_hex_vector(OUTPUT_LINE, HEADER_ADDR_LOW);
+            writeline(OUT_FILE, OUTPUT_LINE);
+            wait until(ICLK'event and (ICLK = '1') and (IN_VALID = '1') and (IN_DFRAME = '0'));
+          end if;
+
+        elsif((HEADER_TYPE = "0000") or (HEADER_TYPE = "0001")) then  -- Read Request
+          --*-------------------------------------------------------------
+          --* Process the Read Request
+          --*-------------------------------------------------------------
+          -- INBOUND_READ_REQUEST_ARRAY(i).ADDRESS
+          -- INBOUND_READ_REQUEST_ARRAY(i).LENGTH
+          -- INBOUND_READ_REQUEST_ARRAY(i).STATE
+          -- constant N_INBOUND_RD_OUTSTANDING : integer := 3;  -- Maximim number of outstanding reads
+          -- constant N_COMPLETION_ID  : integer := 4;  -- Maximim number of completion IDs
+
+          if(INBOUND_READ_REQUEST_CPL_STATE(COMPLETION_ID) /= INBOUND_READ_REQUEST_ARRAY(COMPLETION_ID).STATE) then  -- ERROR: CID is in use
+            write(OUTPUT_LINE, "-- ERROR: " & INSTANCE_LABEL);
+            write(OUTPUT_LINE, NOW);
+            write(OUTPUT_LINE, (" " & INBOUND_LABEL & " Completion ID (CID=" & to_str(COMPLETION_ID) & ") is already in use: HEADER=0x"));
+            write_hex_vector(OUTPUT_LINE, HEADER);
+            write(OUTPUT_LINE, string'(", ADDRESS=0x"));
+            write_hex_vector(OUTPUT_LINE, HEADER_ADDR_HI);
+            write_hex_vector(OUTPUT_LINE, HEADER_ADDR_LOW);
+            writeline(OUT_FILE, OUTPUT_LINE);
+          else
+
+                                        -- Make sure we have a budget for completion IDs
+            CID_COUNT := 0;
+            for I in 0 to (N_COMPLETION_ID-1) loop
+              if(INBOUND_READ_REQUEST_CPL_STATE(I) /= INBOUND_READ_REQUEST_ARRAY(I).STATE) then
+                CID_COUNT := CID_COUNT + 1;
+              end if;
+            end loop;
+
+            if(CID_COUNT < N_INBOUND_RD_OUTSTANDING) then  -- OK to accept the request
+              INBOUND_READ_REQUEST_ARRAY(COMPLETION_ID).ADDRESS <= HEADER_ADDR_HI & HEADER_ADDR_LOW;
+              INBOUND_READ_REQUEST_ARRAY(COMPLETION_ID).BAR_HIT <= to_mvl(BFM_BAR_HIT(1)) & to_mvl(BFM_BAR_HIT(0));
+              INBOUND_READ_REQUEST_ARRAY(COMPLETION_ID).LENGTH  <= to_int(HEADER_LENGTH);
+              INBOUND_READ_REQUEST_ARRAY(COMPLETION_ID).STATE   <= not INBOUND_READ_REQUEST_ARRAY(COMPLETION_ID).STATE;
+              INBOUND_READ_REQUEST_ARRAY(COMPLETION_ID).TC      <= HEADER_TC;
+              INBOUND_READ_REQUEST_ARRAY(COMPLETION_ID).V       <= HEADER_V;
+              wait on INBOUND_READ_REQUEST_ARRAY;
+            else
+              write(OUTPUT_LINE, "-- ERROR: " & INSTANCE_LABEL);
+              write(OUTPUT_LINE, NOW);
+              write(OUTPUT_LINE, (" " & INBOUND_LABEL & " Number of outstanding read requests exceeded: HEADER=0x"));
+              write_hex_vector(OUTPUT_LINE, HEADER);
+              write(OUTPUT_LINE, string'(", ADDRESS=0x"));
+              write_hex_vector(OUTPUT_LINE, HEADER_ADDR_HI);
+              write_hex_vector(OUTPUT_LINE, HEADER_ADDR_LOW);
+              writeline(OUT_FILE, OUTPUT_LINE);
+            end if;
+          end if;
+
+        elsif((HEADER_TYPE = "0100") or (HEADER_TYPE = "0101")) then  -- Read Request
+          --*-------------------------------------------------------------
+          --* Process the Read Completion
+          --*-------------------------------------------------------------
+
+          I_HEADER_LENGTH := to_int(HEADER_LENGTH);
+          I_CID           := to_int(HEADER_CID);
+
+          if(RD_BUFFER_PTR(I_CID) = 0) then  -- start of a completion sequence
+            RD_CPL_RAM_ADDR(I_CID)(RAM_ADDR'high)            := RD_BUFFER(I_CID).BAR_HIT(1);
+            RD_CPL_RAM_ADDR(I_CID)(RAM_ADDR'high-1 downto 0) := RD_BUFFER(I_CID).ADDRESS(RAM_ADDR'high-1 downto 2) & "00";
+          end if;
+
+--                                      RAM_ADDR := RD_CPL_RAM_ADDR(I_CID)(RAM_ADDR'range);
+-- CHANGE Can get rid of RD_CPL_RAM_ADDR
+
+          loop
+            wait until(ICLK'event and (ICLK = '1') and (IN_VALID = '1'));
+
+            I_HEADER_LENGTH := I_HEADER_LENGTH - 1;
+
+--                                              for J in 0 to 3 loop -- Do all 4 bytes of the read
+--
+--                                                      RAM_ADDR1 <= RAM_ADDR;
+--                                                      RAM_REQ1 <= not RAM_REQ1;
+--
+--                                                      wait on RAM_ACK1;
+--
+--                                                      CMP_DATA(J*8+7 downto J*8) := RAM_RD_DATA1;
+--                                                      RAM_ADDR := RAM_ADDR + '1';
+--
+--                                              end loop;
+
+
+            CMP_DATA := IN_DATA;
+            CMD_RD_DATA <= IN_DATA;
+            CMD_RD_DATA_VALID <= '1';
+            wait for 1ns;
+                       
+            CMD_RD_DATA_VALID <= '0';
+
+            
+            RD_DATA := RD_BUFFER(I_CID).DATA(RD_BUFFER_PTR(I_CID));
+            RD_MASK := RD_BUFFER(I_CID).MASK(RD_BUFFER_PTR(I_CID));
+
+            read_cmp(OUTPUT_LINE, (INSTANCE_LABEL & "BFM Master Mode Read "), CMP_DATA, RD_DATA, RD_MASK, vERR);
+            if(vERR /= 0) then
+              TMP_I := RD_BUFFER_PTR(I_CID)*4;
+              TMP_I := to_int(RD_BUFFER(I_CID).ADDRESS(31 downto 0)) + TMP_I;
+              TMP32 := to_vector(TMP_I, TMP32'length);
+              write(OUTPUT_LINE, LF);
+              write(OUTPUT_LINE, string'("Header=0x"));
+              write_hex_vector(OUTPUT_LINE, HEADER);
+              write(OUTPUT_LINE, string'(", CID=0x"));
+              write_hex_vector(OUTPUT_LINE, HEADER_CID);
+              write(OUTPUT_LINE, string'(", ADDRESS=0x"));
+              write_hex_vector(OUTPUT_LINE, RD_BUFFER(I_CID).ADDRESS(63 downto 32));
+              write_hex_vector(OUTPUT_LINE, RD_BUFFER(I_CID).DATA(0));
+
+              write_hex_vector(OUTPUT_LINE, TMP32);
+              writeline(OUT_FILE, OUTPUT_LINE);
+            end if;
+
+            RD_BUFFER_PTR(I_CID) := RD_BUFFER_PTR(I_CID) + 1;
+
+            if(IN_DFRAME = '0') or (I_HEADER_LENGTH = 0) then
+              exit;
+            end if;
+
+          end loop;
+
+          RD_CPL_RAM_ADDR(I_CID)(RAM_ADDR'range) := RAM_ADDR;
+
+
+          if not((IN_DFRAME = '0') and (I_HEADER_LENGTH = 0)) then
+            write(OUTPUT_LINE, "-- ERROR: " & INSTANCE_LABEL);
+            write(OUTPUT_LINE, NOW);
+            write(OUTPUT_LINE, (" " & INBOUND_LABEL & "_DFRAME is not in agreement with the header length value: HEADER=0x"));
+            write_hex_vector(OUTPUT_LINE, HEADER);
+            write(OUTPUT_LINE, string'(", ADDRESS=0x"));
+            write_hex_vector(OUTPUT_LINE, HEADER_ADDR_HI);
+            write_hex_vector(OUTPUT_LINE, HEADER_ADDR_LOW);
+            writeline(OUT_FILE, OUTPUT_LINE);
+          end if;
+
+          if(HEADER_L = '1') then
+            OUTBOUND_READ_REQUEST_CPL_STATE(I_CID) <= RD_BUFFER(I_CID).STATE;
+            RD_BUFFER_PTR(I_CID)                   := 0;
+          end if;
+
+          
+
+        end if;
+      end if;
+
+
+    end loop;
+  end process;
+
+
+--#########################################################################--
+--
+-- BFM Internal RAM Handler 
+--
+-- Handles SRAM access from multiple processes
+--
+--#########################################################################--
+
+  process
+    --file      OUT_FILE : text is out "STD_OUTPUT";
+--              file      OUT_FILE : text open write_mode is "STD_OUTPUT";
+--              variable  OUTPUT_LINE     : line;
+    variable SRAM_BFM  : MEM_ID_TYPE;
+    variable vRAM_DATA : std_ulogic_vector(7 downto 0);
+    
+  begin
+
+    -- Initialize RAM model
+    SRAM_BFM := SRAM_INITIALIZE
+                (name          => "BFM RAM",
+                  length       => (2**N_RAM_MAX),
+                  width        => 8,
+                  default_word => std_ulogic_vector'("XXXXXXXX")
+                  );
+
+    RAM_ACK0 <= false;
+    RAM_ACK1 <= false;
+
+    loop
+
+      wait on RAM_REQ0, RAM_REQ1;
+
+      if(RAM_REQ0'event) then
+        if(RAM_WR0) then
+          Mem_Write
+            (mem_id   => SRAM_BFM,
+              address => RAM_ADDR0,
+              data    => RAM_WR_DATA0
+              );
+          wait for 1ns;
+        else
+          Mem_Read
+            (mem_id   => SRAM_BFM,
+              address => RAM_ADDR0,
+              data    => vRAM_DATA
+              );
+          RAM_RD_DATA0 <= vRAM_DATA;
+          wait for 1ns;
+        end if;
+
+        RAM_ACK0 <= not RAM_ACK0;
+--                              wait until(RAM_REQ0'event);
+      end if;
+
+      if(RAM_REQ1'event) then
+        if(RAM_WR1) then
+          Mem_Write
+            (mem_id   => SRAM_BFM,
+              address => RAM_ADDR1,
+              data    => RAM_WR_DATA1
+              );
+        else
+          Mem_Read
+            (mem_id   => SRAM_BFM,
+              address => RAM_ADDR1,
+              data    => vRAM_DATA
+              );
+          RAM_RD_DATA1 <= vRAM_DATA;
+        end if;
+        RAM_ACK1 <= not RAM_ACK1;
+--                              wait until(RAM_REQ1'event);
+      end if;
+
+    end loop;
+  end process;
+
+--#########################################################################--
+--
+-- Random # generator
+--
+-- Uses PRBS-23
+--
+--#########################################################################--
+
+  process(CLK0o)
+    variable RNDOUT : integer;
+    variable SEED   : integer;
+  begin
+    if(RSTOUTo = '1') then
+      SEED := 5000;
+    elsif(CLK0o'event and CLK0o = '1') then
+      get_random(SEED, 0, 100, RNDOUT);
+      RANDOM_NUMBER <= RNDOUT;
+    end if;
+  end process;
+
+--#########################################################################--
+--
+-- L2P Bus Sniffer
+--
+--#########################################################################--
+  process
+  begin
+    wait until(L2P_CLKpi'event);
+    L2P_CLKi_90 <= transport L2P_CLKpi after (T_LCLKi/4);
+  end process;
+
+  process
+    file OUT_FILE        : text open write_mode is "NullFile";
+    variable OUTPUT_LINE : line;
+    variable vHEADER     : std_ulogic_vector(31 downto 0);
+    variable vADDRESS    : std_ulogic_vector(63 downto 0);
+    variable vTYPE       : std_ulogic_vector(3 downto 0);
+    variable START       : time;
+  begin
+    wait until(L2P_CLKi_90'event and (L2P_CLKi_90 = '1') and (L2P_VALIDi = '1') and MODE_PRIMARY);
+    START := NOW;
+    if(L2P_DFRAMEi = '1') then
+      vHEADER(15 downto 0)  := L2P_DATAi;
+      wait until(L2P_CLKi_90'event and (L2P_CLKi_90 = '0'));
+      vHEADER(31 downto 16) := L2P_DATAi;
+      vTYPE                 := vHEADER(27 downto 24);
+      vADDRESS              := (others => '0');
+
+      -- Upper Address
+      if((vTYPE = "0001") or (vTYPE = "0011")) then  -- address is 64 bits
+        wait until(L2P_CLKi_90'event and (L2P_CLKi_90 = '1') and (L2P_VALIDi = '1'));
+        vADDRESS(47 downto 32) := L2P_DATAi;
+        wait until(L2P_CLKi_90'event and (L2P_CLKi_90 = '0'));
+        vADDRESS(63 downto 48) := L2P_DATAi;
+      end if;
+      -- Lower Address
+      if((vTYPE = "0000") or (vTYPE = "0001") or (vTYPE = "0010") or (vTYPE = "0011") or (vTYPE = "0100")) then  -- address is required
+        wait until(L2P_CLKi_90'event and (L2P_CLKi_90 = '1') and (L2P_VALIDi = '1'));
+        vADDRESS(15 downto 0)  := L2P_DATAi;
+        wait until(L2P_CLKi_90'event and (L2P_CLKi_90 = '0'));
+        vADDRESS(31 downto 16) := L2P_DATAi;
+      end if;
+
+--                      write(OUTPUT_LINE, ("-->> L2P Packet: " & to_string(START)));
+--                      writeline(OUT_FILE, OUTPUT_LINE);
+      write(OUTPUT_LINE, string'("-->>>> L2P Header: "));
+
+      case vTYPE is
+        when "0000" | "0001" =>
+          write(OUTPUT_LINE, string'("(L2P Master Read Request)"));
+          write(OUTPUT_LINE, string'(", FBE=0x"));
+          write_hex_vector(OUTPUT_LINE, vHEADER(23 downto 20));
+          write(OUTPUT_LINE, string'(", LBE=0x"));
+          write_hex_vector(OUTPUT_LINE, vHEADER(19 downto 16));
+          write(OUTPUT_LINE, string'(", V=" & to_str(vHEADER(12))));
+          write(OUTPUT_LINE, string'(", CID="));
+          write_hex_vector(OUTPUT_LINE, vHEADER(11 downto 10));
+          write(OUTPUT_LINE, string'(", LENGTH=0x"));
+          write_hex_vector(OUTPUT_LINE, vHEADER(9 downto 0));
+--                                      writeline(OUT_FILE, OUTPUT_LINE);
+          write(OUTPUT_LINE, string'("-->>>> Address: 0x"));
+          write_hex_vector(OUTPUT_LINE, vADDRESS);
+          write(OUTPUT_LINE, string'(" @ "));
+          write(OUTPUT_LINE, START);
+          writeline(OUT_FILE, OUTPUT_LINE);
+
+        when "0010" | "0011" =>
+          write(OUTPUT_LINE, string'("(L2P Master Write)"));
+          write(OUTPUT_LINE, string'(", FBE=0x"));
+          write_hex_vector(OUTPUT_LINE, vHEADER(23 downto 20));
+          write(OUTPUT_LINE, string'(", LBE=0x"));
+          write_hex_vector(OUTPUT_LINE, vHEADER(19 downto 16));
+          write(OUTPUT_LINE, string'(", V=" & to_str(vHEADER(12))));
+          write(OUTPUT_LINE, string'(", LENGTH=0x"));
+          write_hex_vector(OUTPUT_LINE, vHEADER(9 downto 0));
+--                                      writeline(OUT_FILE, OUTPUT_LINE);
+          write(OUTPUT_LINE, string'("-->>>> Address: 0x"));
+          write_hex_vector(OUTPUT_LINE, vADDRESS);
+          write(OUTPUT_LINE, string'(" @ "));
+          write(OUTPUT_LINE, START);
+          writeline(OUT_FILE, OUTPUT_LINE);
+
+        when "0100" =>
+          write(OUTPUT_LINE, string'("(L2P Target Read Completion Without Data)"));
+          write(OUTPUT_LINE, string'(", STAT="));
+          write_hex_vector(OUTPUT_LINE, vHEADER(17 downto 16));
+          write(OUTPUT_LINE, string'(", L=" & to_str(vHEADER(15))));
+          write(OUTPUT_LINE, string'(", V=" & to_str(vHEADER(12))));
+          write(OUTPUT_LINE, string'(", CID="));
+          write_hex_vector(OUTPUT_LINE, vHEADER(11 downto 10));
+          write(OUTPUT_LINE, string'(", LENGTH=0x"));
+          write_hex_vector(OUTPUT_LINE, vHEADER(9 downto 0));
+          write(OUTPUT_LINE, string'(" @ "));
+          write(OUTPUT_LINE, START);
+          writeline(OUT_FILE, OUTPUT_LINE);
+
+        when "0101" =>
+          write(OUTPUT_LINE, string'("(L2P Target Read Completion With Data)"));
+          write(OUTPUT_LINE, string'(", STAT="));
+          write_hex_vector(OUTPUT_LINE, vHEADER(17 downto 16));
+          write(OUTPUT_LINE, string'(", L=" & to_str(vHEADER(15))));
+          write(OUTPUT_LINE, string'(", V=" & to_str(vHEADER(12))));
+          write(OUTPUT_LINE, string'(", CID="));
+          write_hex_vector(OUTPUT_LINE, vHEADER(11 downto 10));
+          write(OUTPUT_LINE, string'(", LENGTH=0x"));
+          write_hex_vector(OUTPUT_LINE, vHEADER(9 downto 0));
+          write(OUTPUT_LINE, string'(" @ "));
+          write(OUTPUT_LINE, START);
+          writeline(OUT_FILE, OUTPUT_LINE);
+
+        when others =>
+          write(OUTPUT_LINE, string'("(Undefined)"));
+          write(OUTPUT_LINE, string'(" @ "));
+          write(OUTPUT_LINE, START);
+          writeline(OUT_FILE, OUTPUT_LINE);
+          assert false report "---- ERROR: Unsupported TYPE in L2P Header"
+            severity error;
+      end case;
+
+
+
+      if(L2P_DFRAMEi = '1') then
+        wait until(L2P_CLKi_90'event and (L2P_CLKi_90 = '1') and (L2P_VALIDi = '1') and (L2P_DFRAMEi = '0'));
+      end if;
+
+    else
+      write(OUTPUT_LINE, string'("-- ERROR: L2P Bus: P2L_VALID asserted without P2L_DFRAME @"));
+      write(OUTPUT_LINE, START);
+      writeline(OUT_FILE, OUTPUT_LINE);
+    end if;
+  end process;
+
+--#########################################################################--
+--
+-- P2L Bus Sniffer
+--
+--#########################################################################--
+  process
+    file OUT_FILE        : text open write_mode is "NullFile";
+    variable OUTPUT_LINE : line;
+    variable vHEADER     : std_ulogic_vector(31 downto 0);
+    variable vADDRESS    : std_ulogic_vector(63 downto 0);
+    variable vTYPE       : std_ulogic_vector(3 downto 0);
+    variable START       : time;
+  begin
+    wait until(P2L_CLKpi'event and (P2L_CLKpi = '1') and (P2L_VALIDi = '1') and MODE_PRIMARY);
+    START := NOW;
+    if(P2L_DFRAMEi = '1') then
+      vHEADER(15 downto 0)  := P2L_DATAi;
+      wait until(P2L_CLKpi'event and (P2L_CLKpi = '0'));
+      vHEADER(31 downto 16) := P2L_DATAi;
+      vTYPE                 := vHEADER(27 downto 24);
+      vADDRESS              := (others => '0');
+
+      -- Upper Address
+      if((vTYPE = "0001") or (vTYPE = "0011")) then  -- address is 64 bits
+        wait until(P2L_CLKpi'event and (P2L_CLKpi = '1') and (P2L_VALIDi = '1'));
+        vADDRESS(47 downto 32) := P2L_DATAi;
+        wait until(P2L_CLKpi'event and (P2L_CLKpi = '0'));
+        vADDRESS(63 downto 48) := P2L_DATAi;
+      end if;
+      -- Lower Address
+      if((vTYPE = "0000") or (vTYPE = "0001") or (vTYPE = "0010") or (vTYPE = "0011") or (vTYPE = "0100")) then  -- address is required
+        wait until(P2L_CLKpi'event and (P2L_CLKpi = '1') and (P2L_VALIDi = '1'));
+        vADDRESS(15 downto 0)  := P2L_DATAi;
+        wait until(P2L_CLKpi'event and (P2L_CLKpi = '0'));
+        vADDRESS(31 downto 16) := P2L_DATAi;
+      end if;
+
+      write(OUTPUT_LINE, string'("--<<<< P2L Header: "));
+
+      case vTYPE is
+        when "0000" | "0001" =>
+          write(OUTPUT_LINE, string'("(P2L Target Read Request)"));
+          write(OUTPUT_LINE, string'(", FBE=0x"));
+          write_hex_vector(OUTPUT_LINE, vHEADER(23 downto 20));
+          write(OUTPUT_LINE, string'(", LBE=0x"));
+          write_hex_vector(OUTPUT_LINE, vHEADER(19 downto 16));
+          write(OUTPUT_LINE, string'(", V=" & to_str(vHEADER(12))));
+          write(OUTPUT_LINE, string'(", CID="));
+          write_hex_vector(OUTPUT_LINE, vHEADER(11 downto 10));
+          write(OUTPUT_LINE, string'(", LENGTH=0x"));
+          write_hex_vector(OUTPUT_LINE, vHEADER(9 downto 0));
+          write(OUTPUT_LINE, string'("--<<<< Address: 0x"));
+          write_hex_vector(OUTPUT_LINE, vADDRESS);
+          write(OUTPUT_LINE, string'(" @ "));
+          write(OUTPUT_LINE, START);
+          writeline(OUT_FILE, OUTPUT_LINE);
+
+        when "0010" | "0011" =>
+          write(OUTPUT_LINE, string'("(P2L Target Write)"));
+          write(OUTPUT_LINE, string'(", FBE=0x"));
+          write_hex_vector(OUTPUT_LINE, vHEADER(23 downto 20));
+          write(OUTPUT_LINE, string'(", LBE=0x"));
+          write_hex_vector(OUTPUT_LINE, vHEADER(19 downto 16));
+          write(OUTPUT_LINE, string'(", V=" & to_str(vHEADER(12))));
+          write(OUTPUT_LINE, string'(", LENGTH=0x"));
+          write_hex_vector(OUTPUT_LINE, vHEADER(9 downto 0));
+          write(OUTPUT_LINE, string'("--<<<< Address: 0x"));
+          write_hex_vector(OUTPUT_LINE, vADDRESS);
+          write(OUTPUT_LINE, string'(" @ "));
+          write(OUTPUT_LINE, START);
+          writeline(OUT_FILE, OUTPUT_LINE);
+
+        when "0100" =>
+          write(OUTPUT_LINE, string'("(P2L Master Read Completion Without Data)"));
+          write(OUTPUT_LINE, string'(", STAT="));
+          write_hex_vector(OUTPUT_LINE, vHEADER(17 downto 16));
+          write(OUTPUT_LINE, string'(", L=" & to_str(vHEADER(15))));
+          write(OUTPUT_LINE, string'(", V=" & to_str(vHEADER(12))));
+          write(OUTPUT_LINE, string'(", CID="));
+          write_hex_vector(OUTPUT_LINE, vHEADER(11 downto 10));
+          write(OUTPUT_LINE, string'(", LENGTH=0x"));
+          write_hex_vector(OUTPUT_LINE, vHEADER(9 downto 0));
+          write(OUTPUT_LINE, string'(" @ "));
+          write(OUTPUT_LINE, START);
+          writeline(OUT_FILE, OUTPUT_LINE);
+
+        when "0101" =>
+          write(OUTPUT_LINE, string'("(P2L Master Read Completion With Data)"));
+          write(OUTPUT_LINE, string'(", STAT="));
+          write_hex_vector(OUTPUT_LINE, vHEADER(17 downto 16));
+          write(OUTPUT_LINE, string'(", L=" & to_str(vHEADER(15))));
+          write(OUTPUT_LINE, string'(", V=" & to_str(vHEADER(12))));
+          write(OUTPUT_LINE, string'(", CID="));
+          write_hex_vector(OUTPUT_LINE, vHEADER(11 downto 10));
+          write(OUTPUT_LINE, string'(", LENGTH=0x"));
+          write_hex_vector(OUTPUT_LINE, vHEADER(9 downto 0));
+          write(OUTPUT_LINE, string'(" @ "));
+          write(OUTPUT_LINE, START);
+          writeline(OUT_FILE, OUTPUT_LINE);
+
+        when others =>
+          write(OUTPUT_LINE, string'("(Undefined)"));
+          write(OUTPUT_LINE, string'(" @ "));
+          write(OUTPUT_LINE, START);
+          writeline(OUT_FILE, OUTPUT_LINE);
+          assert false report "---- ERROR: Unsupported TYPE in P2L Header"
+            severity error;
+      end case;
+
+
+      if(P2L_DFRAMEi = '1') then
+        wait until(P2L_CLKpi'event and (P2L_CLKpi = '1') and (P2L_VALIDi = '1') and (P2L_DFRAMEi = '0'));
+      end if;
+
+    else
+      write(OUTPUT_LINE, string'("-- ERROR: P2L Bus: P2L_VALID asserted without P2L_DFRAME @"));
+      write(OUTPUT_LINE, START);
+      writeline(OUT_FILE, OUTPUT_LINE);
+    end if;
+  end process;
+
+
+end MODEL;
+
+
diff --git a/hdl/gn4124core/sim/gn4124_bfm/mem_model.vhd b/hdl/gn4124core/sim/gn4124_bfm/mem_model.vhd
new file mode 100644
index 0000000000000000000000000000000000000000..f5d8f8a318efd4909ce49b73e48edbfd3bff3b25
--- /dev/null
+++ b/hdl/gn4124core/sim/gn4124_bfm/mem_model.vhd
@@ -0,0 +1,2324 @@
+library ieee;
+use ieee.std_logic_1164.all;
+library std;
+use std.textio.all;
+
+PACKAGE mem_model is
+
+    -- 2d array to store data for each row
+    type row_matrix  is array (NATURAL RANGE <>, NATURAL RANGE <>) of UX01;
+    type rowptr_type is access row_matrix;
+    -- record for for storing refresh and memory ptr for each row
+    type row_data_type is
+        record
+            last_refresh : time;                -- last time row was refreshed
+            rowptr       : rowptr_type;         -- ptr to 2d matrix with data
+            all_xs       : BOOLEAN;             -- true if row is filled with Xs
+        end record;
+    -- array of refresh times and memory ptrs for the rows
+    type row_data is array (NATURAL RANGE <>) of row_data_type;
+    type row_data_ptr_type is access row_data;
+    type strptr is access string;
+    type default_ptr_type is access std_logic_vector;
+    type mem_type is (DRAM, SRAM, ROM);   -- memory types
+    
+    -- record defining memory and holding general information
+
+    type mem_id_rtype is
+
+        record
+            memory_type    : mem_type;            -- memory type
+            refresh_period : time;                -- refresh period
+            last_init      : time;                -- last time a refresh was performed
+            counter        : NATURAL;             -- refresh counter
+            name           : strptr;              -- pointer to memory name
+            rows           : POSITIVE;            -- # of rows
+            columns        : POSITIVE;            -- # of columns
+            width          : POSITIVE;            -- # word length
+            length         : POSITIVE;            -- # of memory locations
+            row_data_ptr   : row_data_ptr_type;   -- ptr to memory ptrs.
+            default        : default_ptr_type;    -- ptr to default memory word value
+        end record;
+
+    type mem_id_type is access mem_id_rtype;
+
+    
+--********************************************************************************
+    --    Function Name   :  SRAM_Initialize
+    --
+    --    Purpose         :  To create the data structure used to store a
+    --                       static RAM and to initialize it
+    --
+    --    Parameters      :  name    - string used to represent the memory
+    --                       length  - the number of "words" in the memory
+    --                       width   - the length of a "word" of memory
+    --                       default_word   - value to which each word of memory
+    --                                        should be initialized
+    --
+    --    RETURNED VALUE  :  mem_id_type - ptr to memory record
+    --
+    --    NOTE            :  initially the data structure is empty with no
+    --                       space being allocated for the memory
+    --
+    --    Use             :  sram_l1 := SRAM_Initialize ("lsb_of_RAM",1048576,1,"0");
+--********************************************************************************
+        
+	impure    Function SRAM_Initialize ( Constant name            : IN string;
+                               Constant length          : IN POSITIVE;
+                               Constant width           : IN POSITIVE;
+                               Constant default_word    : IN std_ulogic_vector
+                             ) return mem_id_type;
+
+--********************************************************************************
+    --    Procedure Name  :  Mem_Read
+    --
+    --    Purpose         :  To read a "word" from memory
+    --
+    --    Parameters      :  mem_id    -  ptr to memory data structure
+    --                       address   -  address to read from
+    --                       data      -  contents of memory location
+    --                       
+    --
+    --    NOTE            :  a read refreshes row of a DRAM
+    --
+    --    Use             :  Mem_Read (ROM1, "100100111", data_bus);
+--********************************************************************************
+
+    Procedure Mem_Read (  Variable mem_id    : INOUT mem_id_type;
+                          Constant address   : IN std_ulogic_vector;
+                          Variable data      : OUT std_ulogic_vector
+                       );
+
+--********************************************************************************
+    --    Procedure Name  :  Mem_Write
+    --
+    --    Purpose         :  To write a "word" to memory
+    --
+    --    Parameters      :  mem_id    -  ptr to memory data structure
+    --                       address   -  address to read from
+    --                       data      -  "word" to be written to memory
+    --
+    --    NOTE            :  a write refreshes row of a DRAM
+    --
+    --    Use             :  Mem_Write (ROM1, "100100111", "10X1");
+--********************************************************************************
+        
+    Procedure Mem_Write (  Variable mem_id    : INOUT mem_id_type;
+                           Constant address   : IN std_ulogic_vector;
+                           Constant data      : IN std_ulogic_vector
+                        );
+
+END mem_model;
+
+
+
+
+
+
+PACKAGE BODY mem_model is
+
+    Type D1_b_ulogic_type is array(bit) of std_ulogic;
+    type hex_ray is array(1 to 16) of character;
+    type IDENTIFIER is (HEX_NUM1, COMMENT1, WIDTH1, DEFAULT1, COLON1, DOTDOT1, BLANK1, SYN_ERROR1);
+    type digit_to_hex_type is array(0 to 15) of character;
+
+    -- mentor doesn't like the subtype UX01  -  "resolved sybyte cannot be used as a discrete range"
+    type UX01_1DRAY is array(std_ulogic range 'U' to '1') of bit;
+
+    -------------------------------------------------------------------------------------------
+    --  THE FOLLOWING CONSTANTS MAY BE CHANGED BY THE USER TO CUSTOMIZE STD_MEMPAK
+    -------------------------------------------------------------------------------------------
+
+    -- defines the number of bits used to represent an integer on the machine used to run the vhdl simulator
+    CONSTANT IntegerBitLength : INTEGER := 32;
+
+    -- defines the maximum length of strings in this package
+
+    CONSTANT MAX_STR_LEN : NATURAL := 256;
+
+    -- constants used to map X's and U's  in an address to valid values
+    
+    CONSTANT ADDRESS_X_MAP : BIT := '1';
+    CONSTANT ADDRESS_U_MAP : BIT := '1';
+    CONSTANT ADDRESS_MAP : UX01_1DRAY :=
+                           (ADDRESS_U_MAP, ADDRESS_X_MAP, '0', '1');
+
+    -- constants used to map X's and U's in memory locations to a bit value 
+    -- when a bit or a bit_vector is returned by the memory read operation
+    
+    CONSTANT DATA_X_MAP : BIT := '1';
+    CONSTANT DATA_U_MAP : BIT := '1';
+    CONSTANT DATA_MAP : UX01_1DRAY :=
+                        (DATA_U_MAP, DATA_X_MAP, '0', '1');
+
+    -- constants setting collumn size of SRAM's and ROM's so that entire
+    -- memory does not have to be allocated if it is not used.
+    
+    CONSTANT SRAM_COL_SIZE : NATURAL := 1024;
+    CONSTANT ROM_COL_SIZE : NATURAL := 1024;
+
+    -- constant used to enable/disable certain warning assertions
+    CONSTANT MEM_WARNINGS_ON : BOOLEAN := TRUE;
+
+    -- constant used to determine how many words per line to output when doing a memory dump
+    CONSTANT WORDS_PER_LINE : POSITIVE := 16;
+
+
+    ----------------------------------------------------------------------------------------------------------
+    -- CONSTANTS THAT SHOULD NOT BE MODIFIED
+    -- These are used by the package to perform various conversions, comparisions, etc.
+    ----------------------------------------------------------------------------------------------------------
+
+    CONSTANT DIGIT_TO_HEX : digit_to_hex_type := ('0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F');
+    CONSTANT hex : hex_ray := ('0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F');
+    CONSTANT bit_to_std_ulogic : D1_b_ulogic_type := ('0', '1');
+    CONSTANT SPACESTR : STRING(1 to 20) := "                    ";
+    CONSTANT SPACE : CHARACTER :=  ' ';
+    CONSTANT TAB   : CHARACTER := HT;
+
+    
+
+
+function StrLen1 ( Constant l_str : IN string ) return NATURAL is
+
+Variable alias_l_str : string(1 to l_str'length) := l_str;
+Variable i : integer := 1;
+
+begin
+    while ( (i <= l_str'length) and (alias_l_str(i) /= NUL) ) loop
+        i := i + 1;
+    end loop;
+    i := i - 1;
+    return i;
+end;    
+
+function to_str (Constant dd : IN std_logic) return Character is
+
+begin
+    case dd is
+        when '1' => return '1';
+        when '0' => return '0';
+        when 'U' => return 'U';
+        when 'X' => return 'X';
+        when 'L' => return 'L';
+        when 'H' => return 'H';
+        when '-' => return '-';
+        when 'Z' => return 'Z';
+        when 'W' => return 'W';
+    end case;
+end;
+
+function to_str (Constant dd : IN bit) return character is
+
+begin
+  if dd = '0' then
+      return '0';
+  else
+      return '1';
+  end if;
+end;
+    
+function i_to_str (Constant int : IN integer) return string is
+
+Constant length : integer := 33;
+Variable i, len, pos : integer;
+Variable str : string (1 to length);
+Variable tint : integer := int;
+Variable temp : Character;
+Variable negative : BOOLEAN := FALSE;
+
+begin
+   for i in 1 to  length loop
+     str(i) := ' ';
+   end loop;
+   if (tint < 0 ) then
+      tint := -tint;
+      negative := TRUE;
+   end if;
+   i := length;
+   while (  (i >= 1 ) and (tint /= 0)) loop
+       str(i) := CHARACTER'Val(48 + (tint mod 10));
+       tint := tint/10;
+       i := i - 1;
+   end loop;
+   if (NEGATIVE) then
+      str(i) := '-';
+      i := i - 1;
+   end if;
+   len := length - i;
+   pos := i + 1;
+   for i in 1 to len loop
+     str(i) := str(pos);
+     pos := pos + 1;
+   end loop;
+   if (len = 0) then
+       len := 1;
+       str(1) := '0';
+   end if;
+   return  (str(1 to len));
+   
+end;
+
+function v_to_str  (Constant vect : IN bit_vector) return string is
+
+Variable str : string( 1 to vect'length);
+Variable alias_vect : bit_vector(1 to vect'length) := vect;
+Variable i : integer;
+
+begin
+    for i in 1 to vect'length loop
+        case alias_vect(i) is
+          when '1' => str(i) := '1';
+          when '0' => str(i) := '0';
+        end case;
+    end loop;
+    return(str);
+end;
+
+
+function v_to_str  (Constant vect : IN std_logic_vector) return string is
+
+Variable str : string( 1 to vect'length);
+Variable alias_vect : std_logic_vector(1 to vect'length) := vect;
+Variable i : integer;
+
+begin
+    for i in 1 to vect'length loop
+        case alias_vect(i) is
+          when '1' => str(i) := '1';
+          when '0' => str(i) := '0';
+          when 'U' => str(i) := 'U';
+          when 'X' => str(i) := 'X';
+          when 'L' => str(i) := 'L';
+          when 'H' => str(i) := 'H';
+          when '-' => str(i) := '-';
+          when 'Z' => str(i) := 'Z';
+          when 'W' => str(i) := 'W';
+        end case;
+    end loop;
+    return(str);
+end;
+
+-- used to return a printable string for the memory name
+function pstr ( Constant name : in string ) return string is
+
+   variable j : integer;
+
+begin
+   j := 1;
+   while ( (j < name'length) and (name(j) /= nul) ) loop
+      j := j + 1;
+   end loop;
+   if (name(j) = nul) then
+      j := j - 1;
+   end if;
+   return name(1 to j);
+end;
+
+
+
+    ---------------------------------------------------------------------------
+    --    Function Name   :  minimum
+    --
+    --    PURPOSE         :  to determine the smaller of two integers
+    --                       
+    --    Parameters      :  int1 - first integer
+    --                    :  int2 - second integer
+    --
+    --    Returned Value  :  integer - the smaller of int1 and int2
+    --
+    ---------------------------------------------------------------------------
+
+    Function minimum ( Constant int1 : IN integer;
+                       Constant int2 : IN integer
+                     ) return integer is
+
+    begin
+         if (int1 < int2) then
+            return int1;
+        else
+            return int2;
+        end if;
+    end;
+
+--+-----------------------------------------------------------------------------
+--|     Procedure Name : StrCpy1
+--| 1.2.3
+--|     Overloading    : None
+--|
+--|     Purpose        : Copy r_string to l_string.
+--|
+--|     Parameters     :
+--|                      l_str    - output,  STRING, target string
+--|                      r_str    - input, STRING, source string
+--|
+--|     Result         : 
+--|
+--|     NOTE           : If the length of target string is greater than
+--|                      the source string, then target string is padded
+--|                      with space characters on the right side and when
+--|                      the length of target string is shorter than the 
+--|                      length of source string only left most characters
+--|                      of the source string will be be copied to the target.
+--|                      
+--| 
+--|     USE            :
+--|                      Variable s1: string(1 TO 8);
+--|
+--|                       StrCpy1(s1, "123456789A");
+--|                       s1 will hold "12345678"      
+--|-----------------------------------------------------------------------------
+    PROCEDURE StrCpy1   ( VARIABLE l_str : OUT STRING;
+                          CONSTANT r_str : IN  STRING) IS
+       VARIABLE  l_len     : integer := l_str'LENGTH;
+       VARIABLE  r_len     : integer := r_str'LENGTH;
+       VARIABLE  r         : STRING ( 1 to r_len) := r_str;
+       VARIABLE result     : STRING (1 to l_len);
+       VARIABLE indx       : integer := 1;
+    BEGIN
+       assert (l_len > 0)
+          report "StrCpy:  target string is of zero length "
+          severity ERROR;
+              
+       while ( (indx <= r_len) and (indx <= l_len) and (r(indx) /= NUL) ) loop
+          result(indx) := r(indx);
+          indx := indx + 1;
+       end loop;
+       if (indx <= l_len) then
+          result(indx) := NUL;
+       end if;
+       l_str := result;
+       return;
+    END StrCpy1;
+
+    
+
+--+---------------------------------------------------------------------------
+--|     Procedure Name : fgetline1
+--| 
+--|     Overloading    : None
+--|
+--|     Purpose        : To read a line from the input TEXT file and 
+--|                      save into a string. 
+--|
+--|     Parameters     :
+--|                         l_str    -- output, STRING,
+--|                         stream   -- input, TEXT, input file 
+--|
+--|     result         : string.
+--|
+--|     Note:          : The TEXT is defined in the package TEXTIO to be 
+--|                      a  file of string.
+--|     USE:           :
+--|                      VARIABLE  line_buf : string(1 TO 256);
+--|                      FILE      in_file : TEXT IS IN "file_text_in.dat";
+--|                      
+--|                        fgetline1(line_buf, in_file);
+--|
+--|                       Will read a line  from the file
+--|                       file_text_in.dat  and place  into  line_buf.
+--|
+--|-----------------------------------------------------------------------------
+   PROCEDURE fgetline1  ( VARIABLE l_str    : OUT STRING;
+                         VARIABLE stream   : IN TEXT;
+                         VARIABLE line_ptr : INOUT LINE
+                       ) IS
+        VARIABLE str_copy   : STRING(1 TO MAX_STR_LEN + 1);
+        VARIABLE ch         : character;
+        VARIABLE indx       : NATURAL := 0;
+   BEGIN
+      If ( (line_ptr /= NULL) and (line_ptr'LENGTH > 0) ) then
+          NULL;
+      elsif ( not ENDFILE(stream) ) then
+         READLINE(stream, line_ptr);
+      else
+         assert NOT MEM_WARNINGS_ON
+            report " fgetline1 --- end of file text,  no text read "
+            severity WARNING;
+         l_str(l_str'left) := NUL;
+         return;
+      end if;
+      while ( (line_ptr /= NULL) and (line_ptr'length /= 0) ) loop
+         READ(line_ptr,ch);
+         indx := indx + 1;
+         str_copy(indx) := ch;
+      end loop;
+      str_copy(indx + 1) := NUL;
+      strcpy1(l_str, str_copy);
+      return;
+   END;
+
+   
+
+--+----------------------------------------------------------------------------- 
+--|     Function Name  : Is_White1
+--| hidden.
+--|     Overloading    : None 
+--|  
+--|     Purpose        : Test whether a character is a blank, a tab or 
+--|                      a newline character.
+--|
+--|     Parameters     : 
+--|                      c     - input   Character.
+--| 
+--|     Result         :Booelan -- True if the argument c is a blank or a tab(HT), 
+--|                     or a line feed (LF), or carriage return (CR). false otherwise.   
+--| 
+--| 
+--|     See Also       : Is_Space
+--|----------------------------------------------------------------------------- 
+     FUNCTION  Is_White1  ( CONSTANT c    : IN CHARACTER
+                         ) RETURN BOOLEAN IS
+         VARIABLE result : BOOLEAN;
+     BEGIN
+        IF ( (c = ' ') OR (c = HT)  OR (c = CR) OR (c=LF) ) THEN
+             result := TRUE;
+        ELSE
+             result := FALSE;
+        END IF;
+        RETURN result;
+
+     END; 
+    
+--+-----------------------------------------------------------------------------
+--|     Function Name  : Find_NonBlank1
+--| hidden 
+--|     Overloading    : None
+--|
+--|     Purpose        : Find first non_blank character in a string.
+--|
+--|     Parameters     :
+--|                      str_in    - input ,  
+--|
+--|     Result         : Natural, index of non_blank character. If string
+--|                      has all the white character then str_in'LENGTH is
+--|                      returned;
+--|
+--|     NOTE           :
+--|
+--|     Use            :
+--|                      VARIABLE s_flag : String(1 TO 10) := "      TRUE"; 
+--|                      VARIABLE idx: Natural 
+--|
+--|                       idx := Find_NonBlank1 (s_flag);
+--|
+--|-----------------------------------------------------------------------------
+   FUNCTION Find_NonBlank1  ( CONSTANT str_in   : IN STRING
+                           ) RETURN NATURAL IS
+      VARIABLE str_copy :  STRING (1 TO str_in'LENGTH) := str_in;
+      VARIABLE index    :  Natural := 1;
+      VARIABLE ch       :  character;
+       
+    BEGIN
+          loop
+            EXIT WHEN (index > str_in'LENGTH);
+            if Is_White1(str_copy(index)) then
+                index := index + 1;
+            else
+                EXIT;
+            end if;
+          end loop;
+          return index;
+--      
+-- old code
+-- 
+--        ch := str_copy(index);
+--        while ( ( index < str_in'LENGTH) AND (Is_White1(ch) ) ) LOOP
+--        	index := index + 1;
+--              ch := str_copy(index);
+--        end LOOP;
+--        return index;
+    END;
+
+--+----------------------------------------------------------------------------- 
+--|     Function Name  : To_Upper1
+--| 1.
+--|     Overloading    : None 
+--|  
+--|     Purpose        :Convert a string to upper case.
+--|
+--|     Parameters     : 
+--|                      val     - input, string to be converted   
+--| 
+--|     Result         :  string .
+--| 
+--| 
+--|     See Also       : To_Lower, Is_Upper, Is_Lower
+--|----------------------------------------------------------------------------- 
+    FUNCTION  To_Upper1  ( CONSTANT  val    : IN String
+                         ) RETURN STRING IS
+        VARIABLE result   : string (1 TO val'LENGTH) := val;
+        VARIABLE ch       : character;
+    BEGIN
+        FOR i IN 1 TO val'LENGTH LOOP
+            ch := result(i);
+            EXIT WHEN ((ch = NUL) OR (ch = nul));
+            IF ( ch >= 'a' and ch <= 'z') THEN
+    	          result(i) := CHARACTER'VAL( CHARACTER'POS(ch) 
+                                       - CHARACTER'POS('a')
+                                       + CHARACTER'POS('A') );
+            END IF;
+    	END LOOP;
+    	RETURN result;
+    END To_Upper1;
+    
+--+-----------------------------------------------------------------------------
+--|     Function Name  : From_HexString1
+--| 
+--|     Overloading    : None
+--|
+--|     Purpose        : Convert  from a Hex String to a bit_vector.
+--|
+--|     Parameters     :
+--|                      str     - input ,  Hex string to be converted,
+--|
+--|     Result         : bit_vector
+--|
+--|     NOTE           : 
+--|
+--|     Use            :
+--|                      VARIABLE b_vect : bit_vector( 15 DOWNTO 4) ; 
+--|
+--|                       b_vect := From_HexString1 ("   3DD   1010");
+--|                       This statement will set b_vect  equal to "001111011101".
+--|
+--|-----------------------------------------------------------------------------
+    FUNCTION From_HexString1   ( CONSTANT str   : IN STRING
+                               ) RETURN bit_vector IS
+
+      CONSTANT len         : Integer := 4 * str'LENGTH;
+      CONSTANT hex_dig_len : Integer := 4;
+      VARIABLE str_copy    : STRING (1 TO str'LENGTH) := To_Upper1(str);
+      VARIABLE index       : Natural;
+      VARIABLE ch          : character;
+      VARIABLE i, idx      : Integer;
+      VARIABLE invalid     : boolean := false;
+      VARIABLE r           : bit_vector(1 TO len) ;
+      VARIABLE result      : bit_vector(len - 1 DOWNTO 0) ;
+      CONSTANT BIT_ZERO    : bit_vector(1 to 4) := "0000";
+      CONSTANT BIT_ONE     : bit_vector(1 to 4) := "0001";
+      CONSTANT BIT_TWO     : bit_vector(1 to 4) := "0010";
+      CONSTANT BIT_THREE   : bit_vector(1 to 4) := "0011";
+      CONSTANT BIT_FOUR    : bit_vector(1 to 4) := "0100";
+      CONSTANT BIT_FIVE    : bit_vector(1 to 4) := "0101";
+      CONSTANT BIT_SIX     : bit_vector(1 to 4) := "0110";
+      CONSTANT BIT_SEVEN   : bit_vector(1 to 4) := "0111";
+      CONSTANT BIT_EIGHT   : bit_vector(1 to 4) := "1000";
+      CONSTANT BIT_NINE    : bit_vector(1 to 4) := "1001";
+      CONSTANT BIT_TEN     : bit_vector(1 to 4) := "1010";
+      CONSTANT BIT_ELEVEN  : bit_vector(1 to 4) := "1011";
+      CONSTANT BIT_TWELVE  : bit_vector(1 to 4) := "1100";
+      CONSTANT BIT_THIRTEEN: bit_vector(1 to 4) := "1101";
+      CONSTANT BIT_FOURTEEN: bit_vector(1 to 4) := "1110";
+      CONSTANT BIT_FIFTEEN : bit_vector(1 to 4) := "1111";
+       
+    BEGIN
+      -- Check for null input
+        IF (str'LENGTH = 0) THEN
+		assert false
+		report " From_HexString1  --- input string has zero length ";
+                RETURN "";
+
+        ELSIF  (str(str'LEFT) = NUL) THEN
+		assert false
+		report " From_HexString1  --- input string has nul character"
+                        & " at the LEFT position "
+                severity ERROR;
+                RETURN "";  -- null  bit_vector
+	END IF;
+        -- find the position of the first non_white character
+        index := Find_NonBlank1(str_copy);
+        IF (index > str'length) THEN
+		assert false
+		report " From_HexString1  --- input string is empty  ";
+                RETURN ""; 
+        ELSIF (str_copy(index)=NUL) THEN
+		assert false report " From_HexString1  -- first non_white character is a NUL ";
+                RETURN "";
+        END IF;
+
+        i := 0;
+        FOR idx IN index TO  str'length LOOP
+		ch := str_copy(idx);
+                EXIT WHEN ((Is_White1(ch)) OR (ch = NUL));                
+		CASE ch IS
+	          WHEN '0'        => r(i+1 TO i+ hex_dig_len) := BIT_ZERO;
+        	  WHEN '1'        => r(i+1 TO i+ hex_dig_len) := BIT_ONE;
+        	  WHEN '2'        => r(i+1 TO i+ hex_dig_len) := BIT_TWO;
+        	  WHEN '3'        => r(i+1 TO i+ hex_dig_len) := BIT_THREE;
+        	  WHEN '4'        => r(i+1 TO i+ hex_dig_len) := BIT_FOUR;
+        	  WHEN '5'        => r(i+1 TO i+ hex_dig_len) := BIT_FIVE;
+        	  WHEN '6'        => r(i+1 TO i+ hex_dig_len) := BIT_SIX;
+        	  WHEN '7'        => r(i+1 TO i+ hex_dig_len) := BIT_SEVEN;
+        	  WHEN '8'        => r(i+1 TO i+ hex_dig_len) := BIT_EIGHT;
+        	  WHEN '9'        => r(i+1 TO i+ hex_dig_len) := BIT_NINE;
+        	  WHEN 'A' | 'a'  => r(i+1 TO i+ hex_dig_len) := BIT_TEN;
+        	  WHEN 'B' | 'b'  => r(i+1 TO i+ hex_dig_len) := BIT_ELEVEN;
+        	  WHEN 'C' | 'c'  => r(i+1 TO i+ hex_dig_len) := BIT_TWELVE;
+        	  WHEN 'D' | 'd'  => r(i+1 TO i+ hex_dig_len) := BIT_THIRTEEN;
+        	  WHEN 'E' | 'e'  => r(i+1 TO i+ hex_dig_len) := BIT_FOURTEEN;
+        	  WHEN 'F' | 'f'  => r(i+1 TO i+ hex_dig_len) := BIT_FIFTEEN;
+       	  	  WHEN NUL        => exit;
+        	  WHEN OTHERS  	  => -- a non  binary value was passed
+       	  	                     invalid := TRUE;
+         	       		     ASSERT FALSE
+                                     REPORT "From_HexString1(str(" & i_to_str(idx) & ") => " 
+                                     & ch & ") is an invalid character"
+	                	     SEVERITY ERROR;
+       	       	END CASE;
+                i := i + hex_dig_len;
+	END LOOP;
+     -- check for invalid character in the string
+        if ( invalid ) THEN
+           r(1 TO i) := (OTHERS => '0');
+        end if;
+        result(i - 1 DOWNTO 0) := r(1 TO i);
+        return result(i - 1 DOWNTO 0);     -- return slice of result
+
+    END;
+
+    -------------------------------------------------------------------------------
+    --     Function Name  : RegFill1
+    -- 1.7.4
+    --     Overloading    : None
+    --
+    --     Purpose        : Fill an std_logic_vector with a given value
+    --
+    --     Parameters     :
+    --                      SrcReg     - input  std_logic_vector, the  logic vector to be read.
+    --                      DstLength  - input  NATURAL, length of the return logic vector.
+    --                      FillVal    - input  std_ulogic, default is '0'
+    --
+    --     Result         : std_logic_vector of length DstLength
+    --
+    --     NOTE           : The length of the return logic vector  is specified by the
+    --                      parameter 'DstLength'. The input logic vector will
+    --                      be  filled with the FillVal
+    --
+    --     Use            :
+    --                      VARIABLE vect : std_logic_vector ( 15 DOWNTO 0 );
+    --                      vect := RegFill1 ( "00000101", 16, 'U');
+    --
+    --     See Also       : SignExtend
+   -------------------------------------------------------------------------------
+    FUNCTION RegFill1   ( CONSTANT SrcReg      : IN std_logic_vector;
+                         CONSTANT DstLength   : IN NATURAL;
+                         CONSTANT FillVal     : IN std_ulogic   := '0'
+                       ) RETURN std_logic_vector IS
+      CONSTANT reslen : INTEGER := DstLength;
+      VARIABLE result : std_logic_vector (reslen - 1 DOWNTO 0) := (OTHERS => '0');
+      VARIABLE reg    : std_logic_vector (SrcReg'LENGTH - 1 DOWNTO 0) := SrcReg;
+    BEGIN
+     --  null range check
+      IF (SrcReg'LENGTH = 0) THEN
+         IF (DstLength = 0) THEN
+            ASSERT FALSE
+            REPORT " RegFill1 --- input  has null range and" &
+                " Destination also has null range. "
+            SEVERITY ERROR;
+            RETURN result ; 
+         ELSE
+            ASSERT FALSE
+            REPORT " RegFill1 --- input  has null range"
+            SEVERITY ERROR;
+            result := (OTHERS => FillVal);
+            RETURN result ; 
+         END IF;
+ 
+      ELSIF (DstLength = 0) THEN
+          ASSERT false
+          REPORT "RegFill1 --- Destination has null range "
+          SEVERITY ERROR;
+          RETURN result;   
+ 
+      ELSIF (DstLength <= SrcReg'LENGTH) THEN
+                        -- no need to sign extend
+         ASSERT (DstLength = SrcReg'LENGTH)
+         REPORT " RegFill1 ---  Destination length is less than source"
+         SEVERITY ERROR;
+         RETURN reg;        -- return the input data without any change
+ 
+      ELSE
+           result(SrcReg'LENGTH - 1 DOWNTO 0) := reg;
+        -- Fill the MSB's of result with the given fill value.
+          For i IN reslen - 1 DOWNTO SrcReg'LENGTH  Loop
+             result(i) := FillVal;
+          END LOOP;
+      END IF;
+    
+      -- convert to X01
+         result := To_X01(result);
+    -- That's all
+       RETURN result;
+    END;
+
+    
+    
+  --+-----------------------------------------------------------------------------
+  --|     Function Name  : bv_To_StdLogicVector
+  --|
+  --|     Overloading    : 
+  --|
+  --|     Purpose        : Translate a BIt_VECTOR into an std_logic_vector.
+  --|
+  --|     Parameters     : SrcVect - input  bit_vector , the value to be 
+  --|                                       translated.
+  --|                      width   - input  NATURAL, length of the return vector.
+  --|                                Default is IntegerBitLength (Machine integer length).
+  --|
+  --|     Result        : Std_logic_vector.
+  --|
+  --|     NOTE          : ****** this function not visible to the user **********
+  --|-----------------------------------------------------------------------------
+
+  -- ****  function modified so as not to produce an assertion for a zero length vector
+  
+  FUNCTION bv_To_StdLogicVector ( CONSTANT SrcVect  : IN Bit_Vector;
+                                  CONSTANT width    : IN Natural := 0
+                                ) RETURN Std_Logic_Vector IS
+                                
+   VARIABLE len        : INTEGER := SrcVect'LENGTH;
+   VARIABLE result     : Std_Logic_Vector(width - 1 DOWNTO 0) := (OTHERS=>'0');
+   VARIABLE loc_res    : Std_Logic_Vector(len  - 1 DOWNTO 0) := (OTHERS =>'0');
+   VARIABLE vect_copy : Bit_Vector(len - 1 DOWNTO 0) := SrcVect;
+     
+   BEGIN
+
+       IF (SrcVect'LENGTH = 0) THEN
+
+         return loc_res;
+
+       ELSE
+           FOR i IN 0 TO len - 1  LOOP
+              CASE vect_copy(i) IS 
+                 WHEN '0'   =>
+                                loc_res(i) := '0';
+                 WHEN '1'   =>
+                                loc_res(i) := '1';
+              END CASE;
+           END LOOP;  
+           
+           IF (width = 0)  THEN
+               return loc_res;
+           ELSIF (width <= SrcVect'LENGTH) THEN
+               result := loc_res(width - 1 DOWNTO 0);
+           ELSIF (width > SrcVect'LENGTH) THEN
+               result := RegFill1(loc_res, width, '0');
+           END IF;
+           RETURN result;
+        
+       END IF;
+
+    END;
+    
+
+    FUNCTION bv_to_hexstr ( CONSTANT val      : IN BIT_VECTOR
+                          )  RETURN STRING IS
+                          
+      CONSTANT hex_len : integer := (val'LENGTH + 3) / 4;
+      VARIABLE bin_str : STRING(1 to val'LENGTH);
+      VARIABLE hex_str : STRING(1 to hex_len);
+      VARIABLE hex_char : STRING(1 to 4);
+      VARIABLE bit_index : integer;
+      VARIABLE extended_bin_str : STRING(1 to hex_len * 4) := (others => '0');
+
+            
+    BEGIN
+      bin_str := v_to_str (val);
+      if ( (val'LENGTH mod 4) /= 0 ) then
+         extended_bin_str ( 5 - (val'LENGTH mod 4) to hex_len * 4 ) := bin_str;
+      else
+         extended_bin_str := bin_str;
+      end if;
+      FOR i IN 1 TO hex_len LOOP
+        bit_index := ((i - 1) * 4) + 1;
+        hex_char := extended_bin_str(bit_index To bit_index + 3);
+        CASE hex_char IS
+          WHEN "0000" => hex_str(i) := '0'; 
+	  WHEN "0001" => hex_str(i) := '1'; 
+	  WHEN "0010" => hex_str(i) := '2'; 
+	  WHEN "0011" => hex_str(i) := '3'; 
+	  WHEN "0100" => hex_str(i) := '4'; 
+	  WHEN "0101" => hex_str(i) := '5'; 
+	  WHEN "0110" => hex_str(i) := '6'; 
+	  WHEN "0111" => hex_str(i) := '7'; 
+          WHEN "1000" => hex_str(i) := '8'; 
+          WHEN "1001" => hex_str(i) := '9'; 
+	  WHEN "1010" => hex_str(i) := 'A'; 
+	  WHEN "1011" => hex_str(i) := 'B'; 
+	  WHEN "1100" => hex_str(i) := 'C'; 
+	  WHEN "1101" => hex_str(i) := 'D'; 
+          WHEN "1110" => hex_str(i) := 'E'; 
+	  WHEN "1111" => hex_str(i) := 'F'; 
+          WHEN OTHERS => null; 
+        END CASE;
+      END LOOP;
+      return (hex_str);
+ 
+    END;
+    
+    ---------------------------------------------------------------------------
+    --    Function Name   :  vector_size
+    --
+    --    PURPOSE         :  to determine the maximum number of bits needed to
+    --                       represent an integer
+    --
+    --    Parameters      :  int - integer whose bit width is determined
+    --
+    --    Returned Value  :  NATURAL - # of bits needed
+    --
+    ---------------------------------------------------------------------------    
+
+    function vector_size ( Constant int : IN integer ) return natural is
+
+    variable i : integer := int;
+    variable size : integer := 0;
+
+    begin
+        while i > 0 loop
+            i := i / 2;
+            size := size + 1;
+        end loop;
+        return size;
+    end;
+    
+    ---------------------------------------------------------------------------
+    --    Function Name   :  address_trans
+    --
+    --    Purpose         :  to translate an address in vector form to a
+    --                       NATURAL
+    --
+    --    Parameters      :  addr    - address to be translated
+    --
+    --    Returned Value  :  NATURAL - address as a natural number
+    --
+    --    NOTE            :  *****  this procedure is NOT user visible *******
+    --
+    --    Use             :  address_trans(addr)
+    ---------------------------------------------------------------------------
+
+    Function address_trans ( Constant mem_length : IN POSITIVE;
+                             Constant addr       : IN std_logic_vector
+                           ) return NATURAL is
+
+    Variable nad, power : NATURAL;
+    Variable uonce : BOOLEAN := TRUE;
+    Variable xonce : BOOLEAN := TRUE;
+    Variable vect_size : integer := vector_size(mem_length - 1);
+    Variable talias_addr : std_logic_vector(addr'length - 1 downto 0) := To_UX01(addr);
+    Variable alias_addr : std_logic_vector(vect_size - 1 downto 0) := (others => To_StdULogic(ADDRESS_X_MAP));
+    Variable temp_vect : bit_vector(vect_size - 1 downto 0);    
+                                                      
+    begin
+        nad := 0;
+        power := 1;
+        alias_addr( minimum(vect_size, addr'length)  - 1 downto 0) :=
+                                                 talias_addr( minimum(vect_size,addr'length) - 1 downto 0 );
+        assert ( (vect_size >= addr'length) or NOT MEM_WARNINGS_ON )
+            report "Bit width of address vector greater than that needed to access the entire memory."
+                   & LF & SPACESTR & "passed address bit width:  " & i_to_str(addr'length)
+                   & LF & SPACESTR & "required address bit width:  " & i_to_str(vect_size)
+            severity WARNING;
+        assert ( (vect_size <= addr'length) or NOT MEM_WARNINGS_ON )
+            report "Bit width of address vector less than that needed to access the entire memory."
+                   & LF & SPACESTR & "Resulting X's being mapped to:  " & to_str(ADDRESS_X_MAP)
+                   & LF & SPACESTR & "passed address bit width:  " & i_to_str(addr'length)
+                   & LF & SPACESTR & "required address bit width:  " & i_to_str(vect_size)
+            severity WARNING;
+            
+        for i IN 0 to vect_size - 1 loop
+            if ((alias_addr(i) = 'U') and MEM_WARNINGS_ON and uonce) then
+                uonce := FALSE;
+                assert FALSE
+                   report "Address contains a U - it is being mapped to:  " & to_str(ADDRESS_U_MAP)
+                          severity WARNING;
+            end if;
+            if ((alias_addr(i) = 'X') and MEM_WARNINGS_ON and xonce) then
+                xonce := FALSE;
+                assert false
+                   report "Address contains an X - it is being mapped to:  " & to_str(ADDRESS_X_MAP)
+                          severity WARNING;
+            end if;
+            temp_vect(i) := ADDRESS_MAP(alias_addr(i));
+            nad := nad + (power * bit'pos(temp_vect(i)));
+            power := power * 2;
+        end loop;
+        return nad;
+    end;
+           
+    Function address_trans ( Constant mem_length :  IN POSITIVE;
+                             Constant addr       :  IN std_ulogic_vector
+                           ) return NATURAL is
+
+    Variable nad, power : NATURAL;
+    Variable uonce : BOOLEAN := TRUE;
+    Variable xonce : BOOLEAN := TRUE;
+    Variable talias_addr : std_ulogic_vector(addr'length - 1 downto 0) := To_UX01(addr);    
+    Variable vect_size : integer := vector_size(mem_length - 1);
+    Variable alias_addr : std_ulogic_vector(vect_size - 1 downto 0) := (others => To_StdULogic(ADDRESS_X_MAP));
+    Variable temp_vect : bit_vector(vect_size - 1 downto 0);
+                                                      
+    begin
+        nad := 0;
+        power := 1;
+        alias_addr( minimum(vect_size, addr'length)  - 1 downto 0) :=
+                                                       talias_addr( minimum(vect_size,addr'length) - 1 downto 0);
+        assert ( (vect_size >= addr'length) or NOT MEM_WARNINGS_ON )
+            report "Bit width of address vector greater than that needed to access the entire memory."
+                   & LF & SPACESTR & "passed address bit width:  " & i_to_str(addr'length)
+                   & LF & SPACESTR & "required address bit width:  " & i_to_str(vect_size)
+            severity WARNING;
+        assert ( (vect_size <= addr'length) or NOT MEM_WARNINGS_ON )
+            report "Bit width of address vector less than that needed to access the entire memory."
+                   & LF & SPACESTR & "Resulting X's being mapped to:  " & to_str(ADDRESS_X_MAP)            
+                   & LF & SPACESTR & "passed address bit width:  " & i_to_str(addr'length)
+                   & LF & SPACESTR & "required address bit width:  " & i_to_str(vect_size)
+            severity WARNING;
+            
+        for i IN 0 to vect_size - 1 loop
+            if ((alias_addr(i) = 'U') and MEM_WARNINGS_ON and uonce) then
+                uonce := FALSE;
+                assert false
+                   report "Address contains a U - it is being mapped to:  " & to_str(ADDRESS_U_MAP)
+                          severity WARNING;
+            end if;
+            if ((alias_addr(i) = 'X') and MEM_WARNINGS_ON and xonce) then
+                xonce := FALSE;
+                assert false
+                   report "Address contains an X - it is being mapped to:  " & to_str(ADDRESS_X_MAP)
+                          severity WARNING;
+            end if;
+            temp_vect(i) := ADDRESS_MAP(alias_addr(i));
+            nad := nad + (power * bit'pos(temp_vect(i)));
+            power := power * 2;
+        end loop;
+        return nad;
+    end;                           
+
+    Function address_trans ( Constant mem_length :  IN POSITIVE;
+                             Constant addr       :  IN bit_vector
+                           ) return NATURAL is
+
+    Variable nad, power : NATURAL;
+    Variable vect_size : integer := vector_size(mem_length - 1);
+    Variable talias_addr : bit_vector(addr'length - 1 downto 0) := addr;        
+    Variable alias_addr : bit_vector(vect_size - 1 downto 0) := (others => ADDRESS_X_MAP);
+                                                      
+    begin
+        nad := 0;
+        power := 1;
+        alias_addr( minimum(vect_size, addr'length)  - 1 downto 0) :=
+                                                   talias_addr( minimum(vect_size,addr'length) - 1 downto 0);
+        if ( MEM_WARNINGS_ON and (vect_size > addr'length) ) then
+            assert false
+                report "Bit width of address vector smaller than that needed to access the entire memory."
+                       & LF & SPACESTR & "Resulting X's being mapped to:  " & to_str(ADDRESS_X_MAP)
+                       & LF & SPACESTR & "passed address bit width:  " & i_to_str(addr'length)
+                       & LF & SPACESTR & "required address bit width:  " & i_to_str(vect_size)
+                severity WARNING;
+        elsif ( MEM_WARNINGS_ON and (vect_size < addr'length) ) then
+            assert false
+                report "Bit width of address vector larger than that needed to access the entire memory."
+                       & LF & SPACESTR & "passed address bit width:  " & i_to_str(addr'length)
+                       & LF & SPACESTR & "required address bit width:  " & i_to_str(vect_size)
+                severity WARNING;
+        end if;
+        for i in 0 to vect_size - 1 loop
+            nad := nad + (power * bit'pos(alias_addr(i)));
+            power := power * 2;
+        end loop;
+        return nad;
+    end;
+    
+
+    ---------------------------------------------------------------------------
+    --    Procedure Name  :  allocate_row
+    --
+    --    Purpose         :  to allocate a row of memory and initialize it
+    --                       to the default value
+    --
+    --    Parameters      :  mem_id  -  ptr to memory data structure
+    --                       row     -  row to be allocated
+    --
+    --    NOTE            :  allocate data space for 1 row of memory
+    --                       ******  this procedure is NOT user visible *******
+    --
+    --    Use             :  allocate_row (ram1, 5);
+    ---------------------------------------------------------------------------
+
+    procedure allocate_row (  Variable mem_id  :  INOUT mem_id_type;
+                              Constant row     :  IN NATURAL
+                           ) is
+
+        subtype constrained_matrix is
+                     row_matrix (0 to mem_id.columns-1, 0 to mem_id.width-1);
+        variable ptr : rowptr_type;
+        variable i, j : integer;
+        
+    begin
+        
+        if mem_id.row_data_ptr(row).all_xs then    -- if row should be filled with X's then do so
+
+            mem_id.row_data_ptr(row).rowptr := new constrained_matrix'( others => (others => 'X'));
+
+        else                                       -- otherwise, row should be filled with the default
+
+            mem_id.row_data_ptr(row).rowptr := new constrained_matrix;
+
+            ptr := mem_id.row_data_ptr(row).rowptr;
+            for i in 0 to mem_id.columns - 1 loop
+                for j in 0 to mem_id.width - 1 loop
+                    ptr(i,j) := To_UX01(mem_id.default(j));
+                end loop;
+            end loop;
+        end if;
+        -- no longer necessary to indicate that its filled with X's
+        mem_id.row_data_ptr(row).all_xs := FALSE;   
+    end;                           
+
+    ---------------------------------------------------------------------------
+    --    Procedure Name  :  validate_row
+    --
+    --    Purpose         :  if memory is a DRAM then check if refresh period
+    --                       has expired.  If so, and space allocated, then
+    --                       reset all locations to X's.  This is done by setting
+    --                       the filed all_xs to TRUE
+    --
+    --    Parameters      :  mem_id   -  pointer to memory data structure
+    --                       row      -  row to be validated
+    --
+    --    NOTE            :  ******  this procedure is NOT user visible *******
+    --
+    --    Use             :  validate_row (dram1, 5);
+    ---------------------------------------------------------------------------
+
+    Procedure validate_row (  Variable mem_id : INOUT mem_id_type;
+                              Constant row    : IN NATURAL
+                           ) IS 
+
+    Variable rowdat : row_data_ptr_type := mem_id.row_data_ptr;
+    Variable i, j : INTEGER;
+                           
+    begin
+        -- check that it is a dram and that refresh period has expired
+        if ( (mem_id.memory_type = DRAM) and (NOW > (rowdat(row).last_refresh + mem_id.refresh_period)) ) then
+            if rowdat(row).all_xs then
+                -- if all_xs is true already then only an assertion is necessray
+                assert NOT MEM_WARNINGS_ON
+                    report "Refresh time has expired on row " & i_to_str(row) & " of memory:  "
+                           & pstr(mem_id.name(1 to mem_id.name'length)) & LF & SPACESTR & "however, row was not filled with valid data."
+                    severity WARNING;
+            elsif rowdat(row).rowptr = NULL then
+                -- if all_xs is false and no space has been allocated for this row then it must be at default
+                -- set all_xs to true and make an assertion
+                rowdat(row).all_xs := TRUE;
+                assert NOT MEM_WARNINGS_ON
+                    report "Refresh time has expired on row " & i_to_str(row) & " of memory:  "
+                           & pstr(mem_id.name(1 to mem_id.name'length)) & LF & SPACESTR & "Row was filled with default value."
+                    severity WARNING;
+            else
+                -- row has valid, non-default data in it
+                -- set all_xs to true and deallocate space for row
+                rowdat(row).all_xs := TRUE;
+                deallocate(mem_id.row_data_ptr(row).rowptr);
+                mem_id.row_data_ptr(row).rowptr := NULL;
+                assert NOT MEM_WARNINGS_ON
+                    report "Refresh time has expired on row " & i_to_str(row) & " of memory:  "
+                           & pstr(mem_id.name(1 to mem_id.name'length)) & LF & SPACESTR & "Data is lost."
+                    severity WARNING;
+            end if;
+        end if;
+    end;
+
+    ---------------------------------------------------------------------------
+    --    Procedure Name  :  refresh_row
+    --
+    --    Purpose         :  if memory is a DRAM then update the last_refresh
+    --                       time along with last time used (last_init)
+    --
+    --    Parameters      :  mem_id   -  pointer to memory data structure
+    --                       row      -  row to be refreshed
+    --
+    --    NOTE            :  ******  this procedure is NOT user visible *******
+    --
+    --    Use             :  refresh_row (dram1, 5);
+    ---------------------------------------------------------------------------
+
+    Procedure refresh_row ( VARIABLE mem_id  :  INOUT mem_id_type;
+                            Constant row     :  IN NATURAL
+                           ) is
+
+    begin
+        if ( (mem_id.memory_type = DRAM) and (mem_id.last_init + mem_id.refresh_period >= NOW)) then
+            mem_id.row_data_ptr(row).last_refresh := NOW;
+            mem_id.last_init := NOW;
+        end if;
+    end;
+
+    ---------------------------------------------------------------------------
+    --    Function Name   :  SRAM_Initialize
+    --
+    --    Purpose         :  To create the data structure used to store a
+    --                       static RAM and to initialize it
+    --
+    --    Parameters      :  name    - string used to represent the memory
+    --                       length  - the number of "words" in the memory
+    --                       width   - the length of a "word" of memory
+    --                       default_word   - value to which each word of
+    --                                        memory should be initialized
+    --
+    --    RETURNED VALUE  :  mem_id_type - pointer to memory record
+    --
+    --    NOTE            :  initially the data structure is empty with no
+    --                       space being allocated for the memory
+    --
+    --    Use             :  SRAM_Initialize (sram_l1,"lsb_of_RAM",1048576,1);
+    ---------------------------------------------------------------------------
+        
+    impure Function SRAM_Initialize ( Constant name            : IN string;
+                               Constant length          : IN POSITIVE;
+                               Constant width           : IN POSITIVE;
+                               Constant default_word    : IN std_logic_vector
+                             ) return mem_id_type IS
+
+    Variable i, name_len : INTEGER;
+    Variable mem_id : mem_id_type;
+    Variable alias_name : string (1 to name'length) := name;
+                              
+    begin
+        -- create and initialize data structure
+
+        mem_id := new mem_id_rtype '( memory_type => SRAM,
+                                      refresh_period => 0.0 ns,
+                                      last_init => 0.0 ns,
+                                      counter => 0,
+                                      name => NULL,
+                                      rows => 1,
+                                      columns => SRAM_COL_SIZE,
+                                      width => width,
+                                      length => length,
+                                      row_data_ptr => NULL,
+                                      default => NULL
+                                     );
+
+        if ( (length mod SRAM_COL_SIZE) /= 0) then
+           mem_id.rows := (length/SRAM_COL_SIZE) + 1;
+        else
+           mem_id.rows := length/SRAM_COL_SIZE;
+        end if;
+        -- store name of memory
+        name_len := 1;
+        while ( (name_len <= alias_name'length) and (alias_name(name_len) /= nul)) loop
+           name_len := name_len + 1;
+        end loop;
+        name_len := name_len - 1;
+
+        mem_id.name := new string(1 to name_len);
+
+        for i in 1 to name_len loop
+           mem_id.name(i) := alias_name(i);
+        end loop;
+        -- create and initialize data structure for rows
+
+        mem_id.row_data_ptr := new row_data(0 to mem_id.rows-1);
+
+        for i in 0 to mem_id.rows - 1 loop
+            mem_id.row_data_ptr(i) := (last_refresh => NOW,
+                                       rowptr => NULL,
+                                       all_xs => FALSE
+                                      );
+        end loop;
+        -- set default word
+
+        mem_id.default := new std_logic_vector(mem_id.width - 1 downto 0);
+
+        if (default_word'length /= mem_id.width) then
+            assert (default_word'length  = 0)
+                report "SRAM_INITIALIZE:  Default word width does not match word width of memory:  "
+                       & pstr(mem_id.name(1 to mem_id.name'length)) & LF & SPACESTR
+                       & "default will be set to a word filled with 'U'"
+                severity ERROR;
+            for i in 0 to mem_id.width - 1 loop
+                mem_id.default(i) := 'U';
+            end loop;
+        else
+
+            mem_id.default.all := To_X01(default_word);
+
+        end if;
+        return mem_id;
+    end;
+
+
+    impure Function SRAM_Initialize (  Constant name            : IN string;
+                                Constant length          : IN POSITIVE;
+                                Constant width           : IN POSITIVE;
+                                Constant default_word    : IN std_ulogic_vector
+                              ) return mem_id_type IS
+
+    Variable mem_id : mem_id_type;
+
+    begin
+        mem_id := SRAM_Initialize ( name,
+                                    length,
+                                    width,
+                                    std_logic_vector (default_word)
+                                  );
+        return mem_id;
+    end;
+
+                                      
+    ---------------------------------------------------------------------------
+    --    Procedure Name  :  Mem_Wake_Up
+    --
+    --    Purpose         :  to initialize a DRAM for use
+    --                       
+    --    Parameters      :  mem_id    - ptr to memory data structure
+    --
+    --    NOTE            :  a DRAM must be woken up before it can be used or if
+    --                       the refresh period passes without any operations
+    --
+    --    Use             :  Mem_Wake_Up (ROM_chip_1);
+    ---------------------------------------------------------------------------
+
+    Procedure Mem_Wake_Up (Variable mem_id     : INOUT mem_id_type) IS
+
+    begin
+        if (mem_id.memory_type = DRAM) then
+            mem_id.last_init := NOW;
+        else
+            assert false
+                report "Mem_Wake_Up:  Memory:  " & pstr(mem_id.name(1 to mem_id.name'length)) & " is a ROM or an SRAM."
+                        & LF & SPACESTR &
+                       "This operation only valid for DRAM's,operation ignored."
+                severity ERROR;
+        end if;
+    end;
+
+
+    
+    ---------------------------------------------------------------------------
+    --    Procedure Name  :  Mem_Basic_Write
+    --
+    --    Purpose         :  To write a "word" to memory
+    --                       this procedure will write to a ROM
+    --
+    --    Parameters      :  mem_id     - ptr to memory data structure
+    --                       address    - address to read from
+    --                       data       - "word" to be written to memory
+    --                                    must first be converted to X01
+    --                       ingore_rom - if true then write even if ROM
+    --
+    --    NOTE            :  a write refreshes row of a DRAM
+    --                       ***** this procedure not user visible *******
+    --
+    ---------------------------------------------------------------------------
+
+    
+    Procedure Mem_Basic_Write (  Variable mem_id      : INOUT mem_id_type;
+                                 Constant address     : IN NATURAL;
+                                 Constant data        : IN std_logic_vector;
+                                 Constant ignore_rom  : IN Boolean := FALSE
+                              ) IS
+
+    Constant alias_data : std_logic_vector (data'length - 1 downto 0) := data;
+    variable row, column, i : integer;
+    variable short_ptr : rowptr_type;
+    variable mem_word : std_logic_vector (mem_id.width - 1 downto 0)
+                                                          := (others => 'X');   
+                        
+    begin
+        if ( (mem_id.memory_type /= ROM) or (ignore_rom) ) then   -- make sure its not a rom
+            if address < mem_id.length then                       -- check that its a valid address
+                -- if memory is a dram make sure that it has been woken up
+                if ( (mem_id.memory_type /= DRAM) or (mem_id.last_init + mem_id.refresh_period >= NOW)) then
+                    -- calculate row and column
+                    row := address/mem_id.columns;
+                    column := address mod mem_id.columns;
+                    -- validate address and report if refresh time exceeded
+                    validate_row (mem_id, row);
+                    -- refresh the row 
+                    refresh_row (mem_id, row);
+                    -- if row never allocated then allocate it
+                    if (mem_id.row_data_ptr(row).rowptr = NULL) then
+                        allocate_row(mem_id, row);
+                    end if;
+                    -- handle data of different width than memory
+                    -- if data has less bits than memory than MSBs become Xs
+                    assert ( (data'length = mem_id.width) OR NOT MEM_WARNINGS_ON)
+                        report "Mem_Write:  passed data size does not match word size"
+                               & " of mem:  " & pstr(mem_id.name(1 to mem_id.name'length))  & LF & SPACESTR &
+                               "passed data size:  " & i_to_str(data'length) & " bits"
+                                & LF & SPACESTR & "memory word size:  " &
+                               i_to_str(mem_id.width) & " bits"
+                        severity WARNING;
+                    if (mem_id.width >= data'length) then
+                        mem_word (data'length - 1 downto 0) := alias_data;
+                    else
+                        mem_word := To_X01(alias_data(mem_id.width-1 downto 0));
+                    end if;
+                    -- write data to memory
+                    short_ptr := mem_id.row_data_ptr(row).rowptr;
+                    for i IN 0 to mem_id.width - 1 loop
+                        -- mem_id.row_data_ptr(row).rowptr(column,i) := mem_word(i);
+                        -- *************************************
+                        -- this is a bug work around for synopsys
+                        -- replaces line commented out above
+                        short_ptr(column,i) := mem_word(i);
+                        -- end bug fix
+                        -- *************************************
+                    end loop;
+                else
+                    assert false
+                        report "Mem_Write:  Device wake-up time limit exceeded for memory:  "
+                               & pstr(mem_id.name(1 to mem_id.name'length))  & LF & SPACESTR &
+                               "Operation ignored, device must be woken up."
+                        severity WARNING;
+                end if;
+            else
+                assert false
+                     report "Mem_Write:  Passed address exceeds address " 
+                            & "range of mem:  " & pstr(mem_id.name(1 to mem_id.name'length))  & LF & SPACESTR
+                            & "specified address:  " & i_to_str(address)  & LF &
+                            SPACESTR & "address range:  0 to " & i_to_str(mem_id.length - 1)
+                    severity ERROR;
+          end if;
+        else
+            assert false
+                report "Mem_Write:  Attempt to write to memory:  " & pstr(mem_id.name(1 to mem_id.name'length))
+                       & LF & SPACESTR & "Writes to ROMs are not allowed.  Operation ignored."
+                severity ERROR;
+        end if;
+    end;
+
+
+    ---------------------------------------------------------------------------
+    --    Procedure Name  :  Mem_All_Reset
+    --
+    --    Purpose         :  To set the contents of a memory to some predetermined
+    --                       value.  The locations to reset are specified by a
+    --                       range.
+    --
+    --    Parameters      :  mem_id       -  ptr to memory data structure
+    --                       reset_value  -  value to reset memory to
+    --                       start_addr   -  starting address within memory
+    --                       end_addr     -  ending address withim memory
+    --                       ROM_too      -  allows roms to be reset as well if true
+    --
+    --    NOTE            :  works for all mem types.  call by Mem_Reset
+    --                       ****    NOT USER VISIBLE   *****
+    --
+    --    Use             :  Mem_ALL_Reset (RAM1, "1010", 2048, 4096, FALSE);
+    ---------------------------------------------------------------------------
+        
+    procedure  Mem_ALL_Reset ( Variable mem_id          : INOUT mem_id_type;
+                               Constant reset_value     : IN std_logic_vector;
+                               Constant start_addr      : IN NATURAL := 0;
+                               Constant end_addr        : IN NATURAL := integer'high;
+                               Constant ROM_too         : IN BOOLEAN := FALSE
+                             ) IS
+
+    Variable real_end : NATURAL := end_addr;
+    Variable start_row, start_col, end_row, end_col : NATURAL;
+    Variable row, col, rstart_col, rend_col, bit_pos : NATURAL;
+    Variable row_ptr : rowptr_type;
+    Variable alias_reset : std_logic_vector (mem_id.width - 1 downto 0) := (others => 'U');
+    Variable xvector : std_logic_vector (mem_id.width - 1 downto 0) := (others => 'X');
+    Variable i : integer;
+                        
+    begin
+        if (reset_value'length /= mem_id.width) then
+           assert (reset_value'length <= 0)
+                report "Mem_Reset:  reset value of memory does not match memory width " &
+                       pstr(mem_id.name(1 to mem_id.name'length)) & LF & SPACESTR
+                       & "Resetting memory all all 'U's."
+                severity ERROR;
+           alias_reset := (others => 'U');
+        else
+           alias_reset := To_X01(reset_value);
+        end if;
+        if ( (mem_id.memory_type /= ROM) or ROM_too) then          -- make sure its not a rom
+            if (end_addr < start_addr) then   -- check address ranges
+                assert false
+                       report "Mem_Reset:  ending address is less than starting address."
+                              & LF & SPACESTR & "No operation performed."
+                       severity ERROR;
+            elsif (start_addr >= mem_id.length) then
+                assert false
+                       report "Mem_Reset:  starting address outside of address "
+                              & "range of memory:  " & pstr(mem_id.name(1 to mem_id.name'length))
+                              & LF & SPACESTR & "No operation performed."
+                       severity ERROR;
+            else
+                If (end_addr >= mem_id.length) then
+                    assert (end_addr = integer'high)
+                           report "Mem_Reset:  ending address outside address "
+                                  & "range of memory:  " & pstr(mem_id.name(1 to mem_id.name'length)) & LF & SPACESTR
+                                  & "Memory will be refreshed until end is reached."
+                           severity WARNING;
+                    real_end := mem_id.length - 1;
+                end if;
+                -- if memory is a dram, then wake it up
+                if mem_id.memory_type = DRAM then
+                    Mem_Wake_Up (mem_id);
+                end if;
+                -- calculate row and column of starting address
+                start_row := start_addr/mem_id.columns;
+                start_col := start_addr mod mem_id.columns;
+                -- calculate row and column of ending address
+                end_row := real_end/mem_id.columns;
+                end_col := real_end mod mem_id.columns;
+                -- starting column of row presently being written to
+                rstart_col := start_col;
+                for row in start_row to end_row loop 
+                    if row = end_row then       -- set ending collumn of row presently being written to
+                        rend_col := end_col;
+                    else
+                        rend_col := mem_id.columns - 1;
+                    end if;
+                    --  check for expired time period on row 
+                    if ( (rstart_col > 0) or (rend_col < mem_id.columns - 1) ) then
+                        -- it is only necessary to validate row if only part of the row is being reset
+                        validate_row (mem_id, row);
+                    else
+                       -- entire row being reset, check for expired refresh period
+                       assert ( (mem_id.memory_type /= DRAM) or
+                                ((mem_id.row_data_ptr(row).last_refresh + mem_id.refresh_period) >= NOW)
+                                or NOT MEM_WARNINGS_ON )
+                           report "Mem_Reset:  refresh period on row " &
+                                  i_to_str(row) & " has expired but"
+                                   & LF & SPACESTR
+                                  & "no data lost since entire row is being reset"
+                           severity WARNING;
+                    end if;
+                    -- if collumn not allocated & fill value is not the default or all x's then allocate
+
+                    if ( (mem_id.row_data_ptr(row).rowptr = NULL) and (alias_reset /= mem_id.default.all)
+                         and (alias_reset /= xvector) ) then
+
+                        allocate_row (mem_id, row);
+                    -- if filling partial row with default and currently is Xs then allocate
+
+                    elsif ( (mem_id.row_data_ptr(row).rowptr = NULL) and (alias_reset = mem_id.default.all)
+                            and mem_id.row_data_ptr(row).all_xs and
+                            ( (rstart_col /= 0) or (rend_col /= mem_id.columns - 1)) ) then
+
+                        allocate_row (mem_id, row);
+                    -- if filling partial row with Xs and currently is default then allocate
+                    elsif ( (mem_id.row_data_ptr(row).rowptr = NULL) and (alias_reset = xvector)
+                            and (NOT mem_id.row_data_ptr(row).all_xs) and
+                            ( (rstart_col /= 0) or (rend_col /= mem_id.columns - 1)) ) then
+                        allocate_row (mem_id, row);
+                    end if;                    
+                    -- if filling entire collumn with default then deallocate it
+
+                    If ( (alias_reset = mem_id.default.all) and (rstart_col = 0) and
+
+                         (rend_col = mem_id.columns - 1) ) then
+                        if (mem_id.row_data_ptr(row).rowptr /= NULL) then
+                            Deallocate (mem_id.row_data_ptr(row).rowptr);
+                            mem_id.row_data_ptr(row).rowptr := NULL;
+                        end if;
+                        mem_id.row_data_ptr(row).all_xs := FALSE;
+                    -- if filling entire collumn with X's then deallocate it
+                    elsif ( (alias_reset = xvector) and (rstart_col = 0) and (rend_col = mem_id.columns - 1) ) then
+                          if (mem_id.row_data_ptr(row).rowptr /= NULL)  then
+                             Deallocate (mem_id.row_data_ptr(row).rowptr);
+                             mem_id.row_data_ptr(row).rowptr := NULL;
+                         end if;
+                         mem_id.row_data_ptr(row).all_xs := TRUE;
+                    end if;
+                    -- fill up the row if the entire row isn't being filled with Xs or default
+                    row_ptr := mem_id.row_data_ptr(row).rowptr;
+                    if (row_ptr /= NULL)  then
+                        for col in rstart_col to rend_col loop
+                            for bit_pos in 0 to mem_id.width - 1 loop
+                                row_ptr(col,bit_pos) := alias_reset(bit_pos);
+                            end loop;
+                        end loop;
+                    end if;
+                    rstart_col := 0;              -- start at beginning of next collumn
+                    refresh_row (mem_id, row);    -- refresh the current row
+                end loop;
+            end if;
+        else
+            assert false
+                 report "Mem_Reset:   Reset of ROM not allowed.  Operation ignored"
+                 severity ERROR;
+        end if;
+    end;
+
+
+    --------------------------------------------------------------------------------------------------
+    --  The following functions and procedures are used in the recursive descent parser that is used
+    --  to parse the memory files.
+    --  *****************************   THESE ROUTINES ARE NOTE USER VISIBLE   ***********************
+    --------------------------------------------------------------------------------------------------    
+
+
+    -- return true if character is upper case character
+    function is_upper_case ( Constant ch : IN CHARACTER ) return BOOLEAN is
+
+    begin
+        return ( (ch >= 'A') and (ch <= 'Z') );
+    end;
+
+    -- return true if character is lower case character
+    function is_lower_case ( Constant ch : IN CHARACTER ) return BOOLEAN is
+
+    begin
+        return ( (ch >= 'a') and (ch <= 'z') );
+    end;
+
+    -- return true if character is a decimal digit
+    function is_dec_digit ( Constant ch : IN CHARACTER ) return BOOLEAN is
+
+    begin
+        return ( (ch >= '0') and (ch <= '9'));
+    end;
+
+    
+    -- skip over blanks and tabs
+    -- read characters and numbers until space, tab, or symbol is encountered
+    -- get special identifiers such as :, --, or ..
+    -- convert lower case to upper case
+    -- update buffer index to point to first character after identifier that was read
+    
+    procedure read_word ( Variable out_str  : OUT STRING;
+                          Constant in_str   : IN STRING;
+                          Variable b_ind    : INOUT INTEGER
+                        ) is
+
+    Variable out_ind : integer := 1;
+
+    begin
+      -- skip over spaces and tabs
+      while ( ( (in_str(b_ind) = ' ') or (in_str(b_ind) = HT) ) and (b_ind <= StrLen1(in_str)))  loop
+          b_ind := b_ind + 1;
+      end loop;
+      if ( b_ind > StrLen1(in_str) ) then  -- return if blank line
+          out_str(out_ind) := NUL;
+          return;
+      end if;
+      -- check for --
+      if ( (StrLen1(in_str) >= b_ind+1) and  (in_str(b_ind) = '-') and (in_str(b_ind + 1) = '-') ) then
+          out_str(1) := '-';
+          out_str(2) := '-';
+          out_str(3) := NUL;
+          b_ind := b_ind + 2;
+          return;
+      end if;
+      -- check for ..
+      if ( (StrLen1(in_str) >= b_ind+1) and (in_str(b_ind) = '.') and (in_str(b_ind + 1) = '.') ) then
+          out_str(1) := '.';
+          out_str(2) := '.';
+          out_str(3) := NUL;
+          b_ind := b_ind + 2;
+          return;
+      end if;
+      -- check for :
+      if ( (StrLen1(in_str) >= b_ind) and (in_str(b_ind) = ':') ) then
+          out_str(1) := ':';
+          out_str(2) := NUL;
+          b_ind := b_ind  + 1;
+          return;
+      end if;
+      -- get an identifier
+      loop  -- accept at least one character no matter what is is
+          if ( (in_str(b_ind) >= 'a') and (in_str(b_ind) <= 'z') ) then
+              out_str(out_ind) := Character'Val( (Character'POS(in_str(b_ind))) - 32 );  -- convert to upper case
+          else
+              out_str(out_ind) := in_str(b_ind);
+          end if;
+          out_ind := out_ind + 1;  
+          b_ind := b_ind + 1;
+          exit when ( (b_ind > StrLen1(in_str)) or
+                      not (is_upper_case (in_str(b_ind))  or is_lower_case(in_str(b_ind)) or
+                            is_dec_digit(in_str(b_ind))
+                          )
+                    );
+      end loop;
+      out_str(out_ind) := NUL;
+    end;
+
+    -- make sure string is a valid hexadecimal number
+    
+    Function   valid_hex ( Constant str : IN STRING ) return BOOLEAN is
+
+    variable i : integer;
+    variable valid : BOOLEAN := TRUE;
+
+    begin
+        i := 1;
+        while ( (i <= StrLen1(str)) and valid ) loop
+            valid := ( (str(i) >= '0') and (str(i) <= '9') )  or
+                     ( (str(i) >= 'a') and (str(i) <= 'z') ) or
+                     ( (str(i) >= 'A') and (str(i) <= 'Z') );
+            i := i + 1;
+        end loop;
+        return valid;
+    end;
+
+    -- determine what kind of identifier the sring is
+    Function word_id (Constant str : IN STRING) return IDENTIFIER is
+
+    begin
+       if StrLen1(str) = 0 then
+           -- assert false report "BLANK1" severity NOTE;
+           return BLANK1;
+       elsif ( (StrLen1(str) = 1) and (str(1) = ':') ) then
+           -- assert false report "COLON1" severity NOTE;
+           return COLON1;
+       elsif ( (StrLen1(str) = 2) and (str(1) = '-') and (str(2) = '-') ) then
+           -- assert false report "COMMENT1" severity NOTE;
+           return COMMENT1;
+       elsif ( (StrLen1(str) = 2) and (str(1) = '.') and (str(2) = '.') ) then
+           -- assert false report "DOTDOT1" severity NOTE;
+           return DOTDOT1;
+       elsif ( (StrLen1(str) = 5) and (str(1 to 5) = "WIDTH") ) then
+           -- assert false report "WIDTH1" severity NOTE;
+           return WIDTH1;           
+       elsif ( (StrLen1(str) = 7) and (str(1 to 7) = "DEFAULT") ) then
+           -- assert false report "DEFAULT1" severity NOTE;
+           return DEFAULT1;           
+       elsif (valid_hex(str)) then
+           -- assert false report "HEX_NUM1" severity NOTE;
+           return HEX_NUM1;           
+       else
+           -- assert false report "SYN_ERROR1" severity NOTE;
+           return SYN_ERROR1;           
+       end if;
+    end;
+
+    -- force the parser to start parsing from the next line.
+    -- Reset the string buffer index to the first element
+    procedure new_line ( Variable str_buff   : INOUT string;
+                         Variable b_index    : INOUT integer;
+                         Variable file_error : INOUT integer;
+                         Variable in_file    : IN TEXT;
+                         Variable line_num   : INOUT integer
+                       ) is
+
+    Variable line_ptr : LINE;                       
+
+    begin
+        b_index := 1;
+        if not endfile(in_file) then
+            fgetline1(str_buff, in_file, line_ptr);
+            line_num := line_num + 1;
+        else
+            file_error := 1;
+        end if;
+        DEALLOCATE(line_ptr);
+    end;
+
+    -- get the next symbol in the file going to the next line if necessary
+
+    procedure get_sym ( Variable word       : INOUT string;
+                        Variable str_buff   : INOUT string;
+                        Variable b_index    : INOUT integer;
+                        Variable file_error : INOUT integer;
+                        Variable in_file    : IN TEXT;
+                        Variable line_num   : INOUT integer
+                     ) is
+
+    Variable line_ptr : LINE;                         
+
+    begin
+        if ( b_index > StrLen1(str_buff) ) then  -- if end of line reached then get another line
+           b_index := 1;
+           if not endfile(in_file) then
+               fgetline1(str_buff, in_file, line_ptr);
+               line_num := line_num + 1;
+           else
+               file_error := 1;
+           end if;
+           word(1) := NUL;
+        else
+          read_word(word, str_buff, b_index);
+        end if;
+        DEALLOCATE(line_ptr);
+    end;
+
+    -- convert a hexadecimal string to an integer
+    function from_hex_to_int (word : IN string) return integer is
+
+    variable alias_word : string(1 to word'length) := word;
+    variable digit, start, leng : integer;
+    variable power : integer := 1;
+    variable result : integer := 0;
+    variable max_bit_num : integer := 0;  -- max number of bits needed to represent hex number
+    
+    begin
+        leng := StrLen1(alias_word);
+        start := 1;
+        -- eliminate preceeding 0's
+        while ( (alias_word(start) = '0') and (start < leng) ) loop  -- less than leng handles the 0 case
+            start := start + 1;
+        end loop;
+        for i in leng downto start loop
+            max_bit_num := max_bit_num + 4;
+            if ( (alias_word(i) >= '0') and (alias_word(i) <= '9')) then
+                digit := Character'Pos(alias_word(i)) - 48;
+            else
+                digit := Character'Pos(alias_word(i)) - 55;
+            end if;
+            if ( (max_bit_num >= IntegerBitLength) and (digit > 7) ) then
+               assert FALSE
+                  report "MemRead: hex value:  " & word & " is too large to represent as an integer on this machine"
+                  severity ERROR;
+               exit;  -- exit the loop
+            end if;
+            result := result + digit * power;
+            if (i /= start) then           -- power will not be multiplied by 16 on the last iteration.
+               power := power * 16;        -- This will prevent an integer overflow when dealing with 
+            end if;                        -- the maximum number of hex digits the machine can represent.
+        end loop;
+        return result;
+    end;
+
+    -- parse a width statement
+    procedure pwidth ( Variable word       : INOUT string;
+                       Variable str_buff   : INOUT string;
+                       Variable b_index    : INOUT integer;
+                       Variable file_error : INOUT integer;
+                       Variable in_file    : IN TEXT;
+                       Variable file_width : INOUT integer;
+                       Variable line_num   : INOUT integer
+                     ) is
+
+    Variable w_id : IDENTIFIER;
+    variable error_line : integer;
+
+    begin
+        w_id := word_id(word);
+        if (w_id /= WIDTH1) then
+            file_error := 2;
+            assert false
+                 report "Mem_Load:  Width specification not first executable line in file.  File load aborted."
+                        & LF & SPACESTR & "Occurred on line number " & i_to_str(line_num) & " of the input file."
+                 severity ERROR;
+        else
+            get_sym (word, str_buff, b_index, file_error, in_file, line_num);
+            w_id := word_id(word);
+            if (w_id = COLON1) then
+               get_sym (word, str_buff, b_index, file_error, in_file, line_num);
+               w_id := word_id(word);
+               if w_id = HEX_NUM1 then
+                   file_width := from_hex_to_int(word);
+                   get_sym (word, str_buff, b_index, file_error, in_file, line_num);
+                   w_id := word_id(word);
+                   error_line := line_num;
+                   if w_id = COMMENT1 then
+                        new_line (str_buff, b_index, file_error, in_file, line_num);
+                   elsif w_id /= BLANK1 then
+                        assert false
+                            report "Mem_Load:  Additional information on width specification line ignored."
+                                   & LF & SPACESTR & "File processing continuing."
+                                   & LF & SPACESTR & "Occurred on line number " & i_to_str(line_num)
+                                   & " of the input file."
+                            severity ERROR;
+                        new_line(str_buff, b_index, file_error, in_file, line_num);
+                   end if;
+                   if file_width = 0 then
+                       file_error := 10;
+                       assert false
+                           report "Mem_load:  Width must be greater than 0.  File load aborted."
+                                  & LF & SPACESTR & "Occurred on line number " & i_to_str(error_line)
+                                  & " of the input file."
+                           severity ERROR;
+                   end if;
+               else
+                   file_error := 3;
+               end if;
+            else
+                file_error := 3;
+            end if;
+        end if;
+        if file_error = 3 then
+            assert false
+                report "Mem_load:  Syntax error in width specification.  File load aborted."
+                       & LF & SPACESTR & "Occurred on line number " & i_to_str(line_num) & " of the input file."
+                severity ERROR;
+        end if;
+    end;
+
+    -- parse a default statement
+    procedure pdefault ( Variable word       : INOUT string;          -- present word
+                         Variable str_buff   : INOUT string;          -- string buffer
+                         Variable b_index    : INOUT integer;         -- string buffer index
+                         Variable file_error : INOUT integer;         -- error?
+                         Variable in_file    : IN TEXT;               -- file
+                         Variable file_width : IN integer;            -- width specified by file
+                         Variable mem_id     : INOUT mem_id_type;     -- memory 
+                         Constant hex_size   : IN integer;            -- number of hex digits expected
+                         Constant rwidth     : IN integer;            -- # of bits to be written to memory
+                         Variable line_num   : INOUT integer             -- line # of file 
+                       ) is
+
+    Variable w_id : IDENTIFIER;
+    Variable tdata : bit_vector (file_width - 1 downto 0);
+    Variable data : std_logic_vector (mem_id.width - 1 downto 0);
+    
+
+    begin
+        get_sym (word, str_buff, b_index, file_error, in_file, line_num);
+        w_id := word_id(word);
+        if w_id = COLON1 then
+            get_sym (word, str_buff, b_index, file_error, in_file, line_num);
+            w_id := word_id(word);
+            if w_id = HEX_NUM1 then
+                if StrLen1(word) = hex_size then
+                    data := (others => 'X');
+                    tdata := From_HexString1(word)(file_width - 1 downto 0);
+                    data(rwidth - 1 downto 0) := bv_To_StdLogicvector(tdata(rwidth - 1 downto 0));
+                    if mem_id.default = NULL then
+
+                        mem_id.default := new std_logic_vector(mem_id.width - 1 downto 0);
+
+                    else
+                        deallocate (mem_id.default);
+
+                        mem_id.default := new std_logic_vector(mem_id.width - 1 downto 0);
+
+                    end if;
+
+                    mem_id.default.all := data;
+
+                    get_sym (word, str_buff, b_Index, file_error, in_file, line_num);
+                    w_id := word_id(word);
+                    if w_id = COMMENT1 then
+                        new_line (str_buff, b_index, file_error, in_file, line_num);
+                    elsif w_id /= BLANK1 then
+                         assert false
+                             report "Mem_Load:  Additional information on default specification line ignored."
+                                    & LF & SPACESTR & "File processing continuing."
+                                    & LF & SPACESTR & "Occurred on line number " & i_to_str(line_num)
+                                    & " of the input file."
+                             severity ERROR;
+                         new_line(str_buff, b_index, file_error, in_file, line_num);
+                    end if;
+                else
+                    assert false
+                        report "Mem_Load:  Default word length does not match file specification for width of memory. "
+                               & " Default ignored."
+                               & LF & SPACESTR & "Occurred on line number " & i_to_str(line_num)
+                               & " of the input file."
+                        severity ERROR;
+                    new_line (str_buff, b_index, file_error, in_file, line_num);
+                end if;
+            
+            else
+                file_error := 4;
+            end if;
+        else
+            file_error := 4;
+        end if;
+        if file_error = 4 then  
+            assert false
+                report "Mem_Load:  Syntax error in default word specification.  Line ignored."
+                       & LF & SPACESTR & "Occurred on line number " & i_to_str(line_num) & " of the input file."
+                severity ERROR;
+           new_line (str_buff, b_index, file_error, in_file, line_num);
+           file_error := 0;
+        end if;
+    end;
+
+    -- parse an assignment statement
+    procedure passign ( Variable addr1      : IN integer;               -- starting address of assignment
+                        Variable word       : INOUT string;             -- current word
+                        Variable str_buff   : INOUT string;             -- string buffer
+                        Variable b_index    : INOUT integer;            -- string buffer index
+                        Variable file_error : INOUT integer;            -- error?
+                        Variable in_file    : IN TEXT;                  -- file
+                        Variable file_width : IN integer;               -- width specified by file
+                        Variable mem_id     : INOUT mem_id_type;        -- memory
+                        Constant hex_size   : IN integer;               -- number of hex digits expected
+                        Constant rwidth     : IN integer;               -- # of bits to be written to memory
+                        Variable line_num   : INOUT integer             -- line # of file 
+                       ) is
+
+    Variable addr : integer := addr1;
+    Variable w_id : IDENTIFIER := COLON1;
+    Variable tdata : bit_vector (file_width - 1 downto 0);
+    Variable data : std_logic_vector (mem_id.width - 1 downto 0);
+                       
+    begin
+        while ( (file_error = 0) and (w_id /= BLANK1) and (w_id /= COMMENT1) ) loop
+            get_sym (word, str_buff, b_Index, file_error, in_file, line_num);
+            w_id := word_id(word);
+            if w_id = HEX_NUM1 then
+                if StrLen1(word) = hex_size then
+                    data := (others => 'X');
+                    tdata := From_HexString1(word)(file_width - 1 downto 0);
+                    data(rwidth - 1 downto 0) := bv_To_StdLogicVector(tdata(rwidth - 1 downto 0));
+                    Mem_Basic_Write (mem_id, addr, data, TRUE);
+                else
+                    assert false
+                        report "Mem_Load:  Data word length does not match width specification on line:  "
+                               & i_to_str(line_num) & "." & LF & SPACESTR
+                               & "Data byte skipped."
+                        severity ERROR;
+                end if;
+                addr := addr + 1;
+            elsif ( (w_id /= BLANK1) and (w_id /= COMMENT1) ) then
+                file_error := 5;            
+                assert false
+                    report "Mem_Load:  Syntax error on assignment line. Line processed up to error." &
+                           LF & SPACESTR & "Occurred on line number " & i_to_str(line_num) & " of the input file."
+                    severity ERROR;
+                new_line (str_buff, b_index, file_error, in_file, line_num);
+            end if;
+        end loop;
+        if w_id = COMMENT1 then
+            new_line (str_buff, b_index, file_error, in_file, line_num);
+        end if;
+        if file_error = 5 then
+             file_error := 0;
+        end if;
+    end;
+
+    -- parse a range assignment
+    procedure prange ( Variable addr1      : IN integer;                -- strarting address
+                       Variable word       : INOUT string;              -- current word
+                       Variable str_buff   : INOUT string;              -- string buffer
+                       Variable b_index    : INOUT integer;             -- string buffer index
+                       Variable file_error : INOUT integer;             -- error?
+                       Variable in_file    : IN TEXT;                   -- file
+                       Variable file_width : IN integer;                -- width specified in file
+                       Variable mem_id     : INOUT mem_id_type;         -- memory
+                       Constant hex_size   : IN integer;                -- number of hex digits expected
+                       Constant rwidth     : IN integer;                -- # of bits to be written to memory
+                       Variable line_num   : INOUT integer              -- line # of file
+                      ) is
+
+    Variable addr2, addr : integer;
+    Variable w_id : IDENTIFIER;
+    Variable tdata : bit_vector (file_width - 1 downto 0);
+    Variable data : std_logic_vector (mem_id.width - 1 downto 0);
+    Variable error_line : integer;
+    
+    begin
+        get_sym (word, str_buff, b_index, file_error, in_file, line_num);
+        w_id := word_id(word);
+        if w_id = HEX_NUM1 then
+            addr2 := from_hex_to_int(word);
+            if addr2 < addr1 then
+                file_error := 7;
+                error_line := line_num;
+                new_line (str_buff, b_index, file_error, in_file, line_num);
+            else
+                get_sym (word, str_buff, b_index, file_error, in_file, line_num);
+                w_id := word_id(word);
+                if w_id = COLON1 then
+                    get_sym (word, str_buff, b_index, file_error, in_file, line_num);
+                    w_id := word_id(word);
+                    if w_id = HEX_NUM1 then
+                        if StrLen1(word) = hex_size then
+                            data := (others => 'X');
+                            tdata := From_HexString1(word)(file_width - 1 downto 0);
+                            data(rwidth - 1 downto 0) := bv_To_StdLogicVector(tdata(rwidth - 1 downto 0));
+                            Mem_ALL_Reset (mem_id, data, addr1, addr2, TRUE);
+                            get_sym (word, str_buff, b_Index, file_error, in_file, line_num);
+                            w_id := word_id(word);
+                            if w_id = COMMENT1 then
+                                new_line (str_buff, b_index, file_error, in_file, line_num);
+                            elsif w_id /= BLANK1 then
+                                 assert false
+                                     report "Mem_Load:  Additional information on range assignment line ignored."
+                                            & LF & SPACESTR & "File processing continuing."
+                                            & LF & SPACESTR & "Occurred on line number " & i_to_str(line_num)
+                                            & " of the input file."
+                                     severity ERROR;
+                                 new_line(str_buff, b_index, file_error, in_file, line_num);
+                            end if;
+                        else
+                            assert false
+                                report "Mem_Load:  Data word length does not match width specification."
+                                       & LF & SPACESTR & "Line skipped"
+                                       & LF & SPACESTR & "Occurred on line number " & i_to_str(line_num)
+                                       & " of the input file."
+                                severity ERROR;
+                            new_line(str_buff, b_index, file_error, in_file, line_num);    
+                        end if;
+                        addr := addr + 1;
+                    else 
+                        assert false
+                            report "Mem_Load:  Syntax Error on range assignment line.  Line skipped."
+                                   & LF & SPACESTR & "Occurred on line number " & i_to_str(line_num)
+                                   & " of the input file."
+                            severity ERROR;
+                        new_line(str_buff, b_index, file_error, in_file, line_num);    
+                    end if;
+                      -- get_sym (word, str_buff, b_index, file_error, in_file, line_num);
+                      -- w_id := word_id(word);
+                else
+                     file_error := 6;
+                     error_line := line_num;
+                     new_line (str_buff, b_index, file_error, in_file, line_num);
+                end if;
+            end if;
+        else
+            file_error := 6;
+            new_line (str_buff, b_index, file_error, in_file, line_num);
+        end if;
+        if file_error = 7 then
+            assert false
+                report "Mem_load:  Addr2 < Addr1 in range specification.  Line skipped."
+                       & LF & SPACESTR & "Occurred on line number " & i_to_str(error_line) & " of the input file."
+                severity ERROR;
+            file_error := 0;
+        end if;
+        if file_error = 6 then
+            assert false
+               report "Mem_load:  Syntax error in range specification.  Line skipped."
+                      & LF & SPACESTR & "Occurred on line number " & i_to_str(error_line) & " of the input file."
+                severity ERROR;
+           file_error := 0;
+        end if;
+    end;
+
+    -- decide if current statement is a simple assignment of a range assignment
+    -- then parse that statment
+    procedure p_op_statement ( Variable word       : INOUT string;
+                               Variable str_buff   : INOUT string;
+                               Variable b_index    : INOUT integer;
+                               Variable file_error : INOUT integer;
+                               Variable in_file    : IN TEXT;
+                               Variable file_width : IN integer;
+                               Variable mem_id     : INOUT mem_id_type;
+                               Constant hex_size   : IN integer;
+                               Constant rwidth     : IN integer;
+                               Variable line_num   : INOUT integer
+                             ) is
+
+    Variable addr1 : integer;
+    Variable w_id : IDENTIFIER;               
+    
+    begin
+        addr1 := from_hex_to_int(word);
+        get_sym (word, str_buff, b_index, file_error, in_file, line_num);
+        w_id := word_id(word);
+        if w_id = COLON1 then
+            passign ( addr1, word, str_buff, b_index, file_error, in_file,
+                      file_width, mem_id, hex_size, rwidth, line_num);
+        elsif w_id = DOTDOT1 then
+            prange ( addr1, word, str_buff, b_index, file_error, in_file,
+                     file_width, mem_id, hex_size, rwidth, line_num);
+        else
+            assert false
+                report "Mem_Load:   Syntax error.  Line skipped."
+                       & LF & SPACESTR & "Occurred on line number " & i_to_str(line_num) & " of the input file."
+                severity ERROR;
+            new_line (str_buff, b_index, file_error, in_file, line_num);
+        end if;
+    end;
+
+   ---------------------------------------------------------------------------
+    --    Procedure Name  :  Mem_Basic_Read
+    --
+    --    Purpose         :  To read a "word" from memory
+    --
+    --    Parameters      :  data           -  contents of memory location
+    --                       mem_id         -  ptr to memory data structure
+    --                       address        -  address to read from
+    --                       refresh_enable -  if true a refresh is performed
+    --                                         for DRAMs
+    --
+    --    NOTE            :  a read refreshes the corresponding row of a DRAM
+    --                       ***** this procedure is not exteranlly visible ****
+    --
+    ---------------------------------------------------------------------------
+    
+
+    Procedure Mem_Basic_Read (  Variable data           : OUT std_logic_vector;
+                                Variable mem_id         : INOUT mem_id_type;
+                                Constant address        : IN NATURAL;
+                                Constant refresh_enable : IN BOOLEAN := TRUE
+                             ) IS
+
+    Variable alias_data : std_logic_vector(data'length - 1 downto 0) := (others=>'X');
+    Variable mem_word   : std_logic_vector(mem_id.width - 1 downto 0);
+    Variable row : NATURAL := address/mem_id.columns;
+    Variable column : NATURAL := address mod mem_id.columns;
+    Variable limit : integer;
+    Variable i : NATURAL;
+    variable short_ptr : rowptr_type;
+                       
+    begin
+        if address < mem_id.length then        -- check for valid address range
+            -- if dram check if woken up and make assertion
+            -- nothing else has to be done since data will be invalidated due to refresh period violation
+            assert ( (mem_id.memory_type /= DRAM) or (NOT MEM_WARNINGS_ON) or 
+                       ( (mem_id.last_init + mem_id.refresh_period) >= NOW) )   
+                report "Mem_Read:  Device wake-up time limit exceeded for memory:  "
+                           & pstr(mem_id.name(1 to mem_id.name'length))  & LF & SPACESTR &
+                           "device must be woken up.  All reads will return X's or default word"
+                severity WARNING;
+            -- invalidate data if refresh period has expired
+            validate_row (mem_id, row);
+            -- now refresh row
+            if refresh_enable then
+                refresh_row (mem_id, row);
+            end if;
+            --  handle data of different width than memory
+            assert ( (data'length = mem_id.width) OR NOT MEM_WARNINGS_ON)
+                report "Mem_Read:  return data size does not match word size"
+                       & " of mem:  " & pstr(mem_id.name(1 to mem_id.name'length))  & LF & SPACESTR &
+                       "return data size:  " & i_to_str(data'length) & " bits"
+                        & LF & SPACESTR & "memory word size:  " &
+                       i_to_str(mem_id.width) & " bits"
+                severity WARNING;
+            if (mem_id.row_data_ptr(row).all_xs) then   -- if all xs then return x's
+                mem_word := (others => 'X');
+            elsif  (mem_id.row_data_ptr(row).rowptr = NULL) then   -- if not allocated return default
+
+                mem_word := mem_id.default.all;
+
+            else
+                short_ptr := mem_id.row_data_ptr(row).rowptr;
+                for i in 0 to mem_id.width - 1 loop         -- else return word at that location
+                    -- mem_word(i) := mem_id.row_data_ptr(row).rowptr(column,i);
+                    -- *************************************
+                    -- this is a bug work around for synopsys
+                    -- replaces line commented out above
+                    mem_word(i) := short_ptr(column,i);
+                    -- end bug fix
+                    -- *************************************
+                end loop;
+            end if;
+            if mem_id.width >= data'length then
+                limit := data'length;
+            else
+                limit := mem_id.width;
+            end if;
+            for i in 0 to limit - 1 loop
+                alias_data(i) := mem_word(i);
+            end loop;
+        else
+            assert false
+                 report "Mem_Read:  Passed address exceeds address " & 
+                    "range of mem:  " & pstr(mem_id.name(1 to mem_id.name'length))  & LF & SPACESTR 
+                    & "specified address:  " & i_to_str(address)  & LF &
+                    SPACESTR & "address range:  0 to " & i_to_str(mem_id.length - 1)
+                 severity ERROR;
+                 alias_data := (others => 'X');
+        end if;
+        data := alias_data;
+    end;
+
+    Procedure Mem_Basic_Read (  Variable data           : OUT std_ulogic;
+                                Variable mem_id         : INOUT mem_id_type;
+                                Constant address        : IN NATURAL;
+                                Constant refresh_enable : IN BOOLEAN := TRUE
+                             ) IS
+
+    Variable row : NATURAL := address/mem_id.columns;
+    Variable column : NATURAL := address mod mem_id.columns;
+    variable short_ptr : rowptr_type;
+                       
+    begin
+        if address < mem_id.length then
+            assert ( (mem_id.memory_type /= DRAM) or (NOT MEM_WARNINGS_ON) or
+                       ( (mem_id.last_init + mem_id.refresh_period) >= NOW) )
+                report "Mem_Read:  Device wake-up time limit exceeded for memory:  "
+                           & pstr(mem_id.name(1 to mem_id.name'length))  & LF & SPACESTR &
+                           "device must be woken up.  All reads will return X's or default word."
+                severity WARNING;
+            validate_row (mem_id, row);
+            if refresh_enable then
+                refresh_row (mem_id, row);
+            end if;
+            --  handle data of different width than memory
+            assert ( (mem_id.width = 1) OR NOT MEM_WARNINGS_ON)
+                report "Mem_Read:  return data size does not match word size" 
+                       & " of mem:  " & pstr(mem_id.name(1 to mem_id.name'length))  & LF &
+                       SPACESTR & "return data size: 1" & LF & SPACESTR & "memory word size:  " &
+                       i_to_str(mem_id.width) & " bits"
+                severity WARNING;
+            if (mem_id.row_data_ptr(row).all_xs) then
+                data := 'X';
+            elsif (mem_id.row_data_ptr(row).rowptr = NULL) then
+                data := mem_id.default(0);
+            else
+                --data := mem_id.row_data_ptr(row).rowptr(column,0);
+                -- *************************************
+                -- this is a bug work around for synopsys
+                -- replaces line commented out above
+                short_ptr := mem_id.row_data_ptr(row).rowptr;
+                data := short_ptr(column,0);
+                -- end bug fix
+                -- *************************************
+            end if;
+        else
+            assert false
+                 report "Mem_Read:  Passed address exceeds address " & 
+                    "range of mem:  " & pstr(mem_id.name(1 to mem_id.name'length))  & LF & SPACESTR
+                    & "specified address:  " & i_to_str(address)  & LF &
+                    SPACESTR & "address range:  0 to "
+                    & i_to_str(mem_id.length - 1)
+                 severity ERROR;
+                 data := 'X';
+        end if;
+    end;                                                  
+
+                                                          
+    ---------------------------------------------------------------------------
+    --    Procedure Name  :  Mem_Read
+    --
+    --    Purpose         :  To read a "word" from memory
+    --
+    --    Parameters      :  mem_id    -  ptr to memory data structure
+    --                       address   -  address to read from
+    --                       data      -  contents of memory location
+    --                       
+    --
+    --    NOTE            :  a read refreshes row of a DRAM
+    --
+    --    Use             :  Mem_Read (ROM1, "100100111", data);
+    ---------------------------------------------------------------------------
+
+    Procedure Mem_Read (  Variable mem_id    : INOUT mem_id_type;
+                          Constant address   : IN std_ulogic_vector;
+                          Variable data      : OUT std_ulogic_vector
+                       ) IS
+                       
+    Variable temp : std_logic_vector(data'length - 1 downto 0);
+
+    begin
+        Mem_Basic_Read (temp, mem_id, address_trans(mem_id.length, address));
+        data := std_ulogic_vector (temp);
+    end;                                                  
+
+    
+    ---------------------------------------------------------------------------
+    --    Procedure Name  :  Mem_Write
+    --
+    --    Purpose         :  To write a "word" to memory
+    --
+    --    Parameters      :  mem_id    -  ptr to memory data structure
+    --                       address   -  address to read from
+    --                       data      -  "word" to be written to memory
+    --
+    --    NOTE            :  a write refreshes row of a DRAM
+    --
+    --    Use             :  Mem_Write (ROM1, "100100111", "10X1");
+    ---------------------------------------------------------------------------
+        
+    Procedure Mem_Write (  Variable mem_id    : INOUT mem_id_type;
+                           Constant address   : IN std_ulogic_vector;
+                           Constant data      : IN std_ulogic_vector
+                        ) IS
+                        
+    begin
+        Mem_Basic_Write (  mem_id,
+                           Address_trans(mem_id.length, address),
+                           To_X01(std_logic_vector(data))
+                        );
+    end;                                                  
+
+                      
+                    
+
+                              
+END mem_model;
diff --git a/hdl/gn4124core/sim/gn4124_bfm/textutil.vhd b/hdl/gn4124core/sim/gn4124_bfm/textutil.vhd
new file mode 100644
index 0000000000000000000000000000000000000000..1cc3de15f66d170c1333190caae51998a3e40b47
--- /dev/null
+++ b/hdl/gn4124core/sim/gn4124_bfm/textutil.vhd
@@ -0,0 +1,744 @@
+library IEEE;
+use IEEE.std_logic_1164.all;
+use std.textio.all;
+use work.util.all;
+
+-----------------------------------------------------------------------------
+-- *Module      : textutil
+--
+-- *Description : Improved Free-format string and line manipulation
+--
+-- *History: M. Alford (originaly created 1993 with subsequent updates)
+-----------------------------------------------------------------------------
+package textutil is
+	procedure read_token(L : inout line; X : out STRING);
+	procedure sget_token(S : in string; P : inout integer; X : out STRING);
+	procedure sget_vector(S : in string; P : inout integer; VEC : out STD_ULOGIC_VECTOR);
+	procedure sget_vector_64(S : in string; P : inout integer; VEC : out STD_ULOGIC_VECTOR);
+	procedure sget_int(S : in string; P : inout integer; X : out integer);
+	function  hex_char_to_vector(C : in character) return STD_ULOGIC_VECTOR;
+	function  vector_to_hex_char(V : in STD_ULOGIC_VECTOR) return character;
+	function  is_hex(C : in character) return BOOLEAN;
+	function  hex_char_to_int(C : in character) return integer;
+	procedure read_vector(L : inout line; VEC : out STD_ULOGIC_VECTOR);
+	procedure read_int(L : inout line; I : out integer);
+	procedure write_hex_vector(L : inout line; V : in STD_ULOGIC_VECTOR);
+	function  to_str(constant V: in STD_ULOGIC_VECTOR) return STRING;
+	function  to_str(constant V: in STD_ULOGIC) return STRING;
+	function  to_str(constant val : in INTEGER) return STRING;
+	function  to_strn(constant val : in INTEGER; constant n : in INTEGER) return STRING;
+end textutil;
+
+package body textutil is
+
+-----------------------------------------------------------------------------
+-- *Module      : read_token
+--
+-- *Description : Skip over spaces then load a token string from a line
+--                until either the string is full or the token is finished
+--                (i.e. another space).  The output string is padded out
+--                with blanks at the end if the token length is less then
+--                the full string length.
+-----------------------------------------------------------------------------
+	procedure read_token(L : inout line; X : out STRING) is
+	variable char : character;
+	begin
+		if(L'length > 0) then
+			char := ' ';
+			while((char = ' ') and (L'length > 0)) loop	-- Skip spaces
+				read(L, char);
+			end loop;
+			for i in X'low to X'high loop
+				X(i) := char;
+				if(char /= ' ') then
+					if(L'length > 0) then
+						read(L, char);
+					else
+						char := ' ';
+					end if;
+				end if;
+			end loop;
+		else
+			assert false report "Couldn't read a token from file"
+			severity error;
+		end if;
+	end read_token;
+
+-----------------------------------------------------------------------------
+-- *Module      : sget_token
+--
+-- *Description : Same as read_token except for strings.
+-----------------------------------------------------------------------------
+	procedure sget_token(S : in string; P : inout integer; X : out STRING) is
+	variable char : character;
+	begin
+		if(S'length > P) then
+			char := ' ';
+			while((char = ' ') and (S'length >= P)) loop	-- Skip spaces
+				char := S(P);
+				P := P + 1;
+			end loop;
+			for i in X'low to X'high loop
+				X(i) := char;
+				if(char /= ' ') then
+					if(S'length > P) then
+						char := S(P);
+						P := P + 1;
+					else
+						char := ' ';
+					end if;
+				end if;
+			end loop;
+		else
+			assert false report "Couldn't read a token from a string"
+			severity error;
+		end if;
+	end sget_token;
+			
+-----------------------------------------------------------------------------
+-- *Module      : hex_char_to_vector
+--
+-- *Description : Convert a hex character to a vector
+-----------------------------------------------------------------------------
+	function hex_char_to_vector(C : in character) return STD_ULOGIC_VECTOR is
+	variable X : STD_ULOGIC_VECTOR( 3 downto 0);
+	begin
+		case C is
+			when '0' => X := "0000";
+			when '1' => X := "0001";
+			when '2' => X := "0010";
+			when '3' => X := "0011";
+			when '4' => X := "0100";
+			when '5' => X := "0101";
+			when '6' => X := "0110";
+			when '7' => X := "0111";
+			when '8' => X := "1000";
+			when '9' => X := "1001";
+			when 'A' => X := "1010";
+			when 'B' => X := "1011";
+			when 'C' => X := "1100";
+			when 'D' => X := "1101";
+			when 'E' => X := "1110";
+			when 'F' => X := "1111";
+			when 'a' => X := "1010";
+			when 'b' => X := "1011";
+			when 'c' => X := "1100";
+			when 'd' => X := "1101";
+			when 'e' => X := "1110";
+			when 'f' => X := "1111";
+			when others => 
+				X := "0000";
+				assert false report "Invalid Hex Character"
+				severity error;
+		end case;
+		return(X);
+	end hex_char_to_vector;
+
+-----------------------------------------------------------------------------
+-- *Module      : vector_to_hex_char
+--
+-- *Description : Convert a vector to a hex character. Only uses low 4 bits.
+-----------------------------------------------------------------------------
+	function vector_to_hex_char(V : in STD_ULOGIC_VECTOR) return character is
+	variable C  : character;
+	variable VV : STD_ULOGIC_VECTOR(3 downto 0);
+	begin
+		if(V'length < 4) then
+			VV := To_X01(V(V'low + 3 downto V'low));
+		else
+			VV := To_X01(V(V'low + V'length - 1 downto V'low));
+		end if;
+		case VV is
+			when "0000" => C := '0';
+			when "0001" => C := '1';
+			when "0010" => C := '2';
+			when "0011" => C := '3';
+			when "0100" => C := '4';
+			when "0101" => C := '5';
+			when "0110" => C := '6';
+			when "0111" => C := '7';
+			when "1000" => C := '8';
+			when "1001" => C := '9';
+			when "1010" => C := 'A';
+			when "1011" => C := 'B';
+			when "1100" => C := 'C';
+			when "1101" => C := 'D';
+			when "1110" => C := 'E';
+			when "1111" => C := 'F';
+			when others => C := 'X';
+		end case;
+		return(C);
+	end vector_to_hex_char;
+
+-----------------------------------------------------------------------------
+-- *Module      : is_hex
+--
+-- *Description : report if a char is ASCII hex
+-----------------------------------------------------------------------------
+	function is_hex(C : in character) return BOOLEAN is
+	variable X : boolean;
+	begin
+		case C is
+			when '0' => X := TRUE;
+			when '1' => X := TRUE;
+			when '2' => X := TRUE;
+			when '3' => X := TRUE;
+			when '4' => X := TRUE;
+			when '5' => X := TRUE;
+			when '6' => X := TRUE;
+			when '7' => X := TRUE;
+			when '8' => X := TRUE;
+			when '9' => X := TRUE;
+			when 'A' => X := TRUE;
+			when 'B' => X := TRUE;
+			when 'C' => X := TRUE;
+			when 'D' => X := TRUE;
+			when 'E' => X := TRUE;
+			when 'F' => X := TRUE;
+			when 'a' => X := TRUE;
+			when 'b' => X := TRUE;
+			when 'c' => X := TRUE;
+			when 'd' => X := TRUE;
+			when 'e' => X := TRUE;
+			when 'f' => X := TRUE;
+			when others => 
+				X := FALSE;
+		end case;
+		return(X);
+	end is_hex;
+
+-----------------------------------------------------------------------------
+-- *Module      : hex_char_to_int
+--
+-- *Description : Convert a hex character to an integer
+-----------------------------------------------------------------------------
+	function hex_char_to_int(C : in character) return integer is
+	variable X : integer;
+	begin
+		case C is
+			when '0' => X := 0;
+			when '1' => X := 1;
+			when '2' => X := 2;
+			when '3' => X := 3;
+			when '4' => X := 4;
+			when '5' => X := 5;
+			when '6' => X := 6;
+			when '7' => X := 7;
+			when '8' => X := 8;
+			when '9' => X := 9;
+			when 'A' => X := 10;
+			when 'B' => X := 11;
+			when 'C' => X := 12;
+			when 'D' => X := 13;
+			when 'E' => X := 14;
+			when 'F' => X := 15;
+			when 'a' => X := 10;
+			when 'b' => X := 11;
+			when 'c' => X := 12;
+			when 'd' => X := 13;
+			when 'e' => X := 14;
+			when 'f' => X := 15;
+			when others => 
+				X := 0;
+				assert false report "Invalid Hex Character"
+				severity error;
+		end case;
+		return(X);
+	end hex_char_to_int;
+			
+-----------------------------------------------------------------------------
+-- *Module      : read_vector
+--
+-- *Description : load a vector from the input line in a free floating format
+-----------------------------------------------------------------------------
+	procedure read_vector(L : inout line; VEC : out STD_ULOGIC_VECTOR) is
+	variable char : character;
+	variable base : integer;
+	variable q : integer;
+	variable v : STD_ULOGIC_VECTOR(31 downto 0);
+	begin
+		if(L'length > 0) then
+			char := ' ';
+			while(char = ' ') loop	-- Skip spaces
+				read(L, char);
+			end loop;
+			base := 16;			-- Hex is the default
+			if(char = '%') then -- determine base
+				read(L, char);
+				if(char = 'b' or char = 'B') then
+					base := 2;
+				elsif(char = 'x' or char = 'X') then
+					base := 16;
+				elsif(char = 'd' or char = 'D') then
+					base := 10;
+				else
+					assert false report "Unsupported Base detected when reading a Vector"
+					severity error;
+				end if;
+				read(L, char);
+			end if;
+			q := 0;
+			if(is_hex(char)) then
+				q := q * base + hex_char_to_int(char);
+				while(is_hex(char) and not (L'length = 0))  loop
+					read(L, char);
+					if(is_hex(char)) then
+						q := q * base + hex_char_to_int(char);
+					end if;
+				end loop;
+			end if;			
+			if(q < 0) then
+				q := q-2147483648;
+				V(30 downto 0) := to_vector(q, 31);
+				V(31) := '1';
+			else
+				V(30 downto 0) := to_vector(q, 31);
+				V(31) := '0';
+			end if;
+			VEC := V((VEC'high - VEC'low) downto 0);
+		else
+			assert false report "Couldn't read a vector"
+			severity error;
+		end if;
+	end read_vector;
+
+-----------------------------------------------------------------------------
+-- *Module      : sget_vector
+--
+-- *Description : Same as sget_vector except for strings
+-----------------------------------------------------------------------------
+	procedure sget_vector(S : in string; P : inout integer; VEC : out STD_ULOGIC_VECTOR) is
+	variable char : character;
+	variable base : integer;
+	variable q : integer;
+	variable v : STD_ULOGIC_VECTOR(31 downto 0);
+	begin
+		while(S(P) = ' ') loop	-- Skip spaces
+			if(P >= S'length) then
+				P := S'length;
+				exit;
+			end if;
+			P := P + 1;
+		end loop;
+		if(S'length > P) then
+			char := S(P);
+			if(char = '"') then -- read in as a literal
+				q := v'high;
+				v := (others => 'U');
+				VEC := v(VEC'range);
+				char := ' ';
+				P := P + 1;
+				while((char /= '"') and not (S'length = P))  loop
+					char := S(P);
+					P := P + 1;				
+					case char is
+						when '0' =>
+							v(q) := '0';
+						when '1' =>
+							v(q) := '1';
+						when 'L' | 'l' =>
+							v(q) := 'L';
+						when 'H' | 'h' =>
+							v(q) := 'H';
+						when 'Z' | 'z' =>
+							v(q) := 'Z';
+						when 'X' | 'x' =>
+							v(q) := 'X';
+						when 'U' | 'u' =>
+							v(q) := 'U';
+						when others =>
+--							char := '"';
+							exit;
+					end case;
+					q := q - 1;
+				end loop;
+				if(v'high-q < 2) then -- only a single bit was read
+					VEC(VEC'low) := v(v'high);
+				elsif((v'high - q) > VEC'length) then -- too many bits
+					VEC := v(q+VEC'length downto q+1);
+				else -- the number of bits read is same or less than required
+					VEC(v'high-q-1+VEC'low downto VEC'low) := v(v'high downto q+1);
+				end if;
+			else
+				base := 16;			-- Hex is the default
+				if(char = '%') then -- determine base
+					P := P + 1;			
+					char := S(P);
+					P := P + 1;			
+					if(char = 'b' or char = 'B') then
+						base := 2;
+					elsif(char = 'x' or char = 'X') then
+						base := 16;
+					elsif(char = 'd' or char = 'D') then
+						base := 10;
+					else
+						assert false report "Unsupported Base detected when reading a Vector"
+						severity error;
+					end if;
+				elsif((char = '0') and ((S(P+1) = 'x') or (S(P+1) = 'X'))) then
+					P := P + 2;
+				end if;
+				q := 0;
+				char := S(P);
+				if(is_hex(char)) then
+					while(is_hex(char) and not (S'length = P))  loop
+						if(is_hex(char)) then
+							q := q * base + hex_char_to_int(char);
+						end if;
+						P := P + 1;				
+						char := S(P);
+					end loop;
+				end if;			
+				if(q < 0) then
+					q := q-2147483648;
+					V(30 downto 0) := to_vector(q, 31);
+					V(31) := '1';
+				else
+					V(30 downto 0) := to_vector(q, 31);
+					V(31) := '0';
+				end if;
+				VEC := V((VEC'high - VEC'low) downto 0);
+			end if;
+		else
+			assert false report "Couldn't read a vector"
+			severity error;
+			V := (others => '0');
+			VEC := V((VEC'high - VEC'low) downto 0);
+		end if;
+	end sget_vector;
+	
+-----------------------------------------------------------------------------
+-- *Module      : sget_vector_64
+--
+-- *Description : Same as sget_vector except can handle 64 bit quantities and hex or binary base (no base 10)
+-----------------------------------------------------------------------------
+	procedure sget_vector_64(S : in string; P : inout integer; VEC : out STD_ULOGIC_VECTOR) is
+	variable char : character;
+	variable base : integer;
+	variable q : integer;
+	variable v : STD_ULOGIC_VECTOR(63 downto 0);
+	begin
+		while(S(P) = ' ') loop	-- Skip spaces
+			if(P >= S'length) then
+				P := S'length;
+				exit;
+			end if;
+			P := P + 1;
+		end loop;
+		if(S'length > P) then
+			char := S(P);
+			if(char = '"') then -- read in as a literal
+				q := v'high;
+				v := (others => 'U');
+				VEC := v(VEC'range);
+				char := ' ';
+				P := P + 1;
+				while((char /= '"') and not (S'length = P))  loop
+					char := S(P);
+					P := P + 1;				
+					case char is
+						when '0' =>
+							v(q) := '0';
+						when '1' =>
+							v(q) := '1';
+						when 'L' | 'l' =>
+							v(q) := 'L';
+						when 'H' | 'h' =>
+							v(q) := 'H';
+						when 'Z' | 'z' =>
+							v(q) := 'Z';
+						when 'X' | 'x' =>
+							v(q) := 'X';
+						when 'U' | 'u' =>
+							v(q) := 'U';
+						when others =>
+--							char := '"';
+							exit;
+					end case;
+					q := q - 1;
+				end loop;
+				if(v'high-q < 2) then -- only a single bit was read
+					VEC(VEC'low) := v(v'high);
+				elsif((v'high - q) > VEC'length) then -- too many bits
+					VEC := v(q+VEC'length downto q+1);
+				else -- the number of bits read is same or less than required
+					VEC(v'high-q-1+VEC'low downto VEC'low) := v(v'high downto q+1);
+				end if;
+			else
+				base := 16;			-- Hex is the default
+				if(char = '%') then -- determine base
+					P := P + 1;			
+					char := S(P);
+					P := P + 1;			
+					if(char = 'b' or char = 'B') then
+						base := 2;
+					elsif(char = 'x' or char = 'X') then
+						base := 16;
+					else
+						assert false report "Unsupported Base detected when reading a Vector"
+						severity error;
+					end if;
+					char := S(P);
+--					P := P + 1;	
+				elsif((char = '0') and ((S(P+1) = 'x') or (S(P+1) = 'X'))) then
+					P := P + 2;
+				end if;
+				v := (others => '0');
+				char := S(P);
+				if(base = 2) then
+					while(((char = '0') or (char = '1')) and not (P > S'length))  loop
+						if(char = '0') then
+							v := v(v'high-1 downto 0) & '0';
+						else
+							v := v(v'high-1 downto 0) & '1';
+						end if;
+						P := P + 1;				
+						char := S(P);
+					end loop;
+				else
+					while(is_hex(char) and not (P > S'length))  loop
+						if(is_hex(char)) then
+							v := v(v'high-4 downto 0) & hex_char_to_vector(char);
+						end if;
+						P := P + 1;				
+						char := S(P);
+					end loop;
+				end if;
+				VEC := V((VEC'high - VEC'low) downto 0);
+			end if;
+		else
+			assert false report "Couldn't read a vector"
+			severity error;
+			V := (others => '0');
+			VEC := V((VEC'high - VEC'low) downto 0);
+		end if;
+	end sget_vector_64;
+	
+-----------------------------------------------------------------------------
+-- *Module      : read_int
+--
+-- *Description : load an integer from the input line in a free floating format
+-----------------------------------------------------------------------------
+	procedure read_int(L : inout line; I : out integer) is
+	variable char : character;
+	variable base : integer;
+	variable q : integer;
+	begin
+		if(L'length > 0) then
+			char := ' ';
+			while(char = ' ') loop	-- Skip spaces
+				read(L, char);
+			end loop;
+			base := 16;			-- Hex is the default
+			if(char = '%') then -- determine base
+				read(L, char);
+				if(char = 'b' or char = 'B') then
+					base := 2;
+				elsif(char = 'x' or char = 'X') then
+					base := 16;
+				elsif(char = 'd' or char = 'D') then
+					base := 10;
+				else
+					assert false report "Unsupported Base detected when reading an integer"
+					severity error;
+				end if;
+				read(L, char);
+			end if;
+			q := 0;
+			if(is_hex(char)) then
+				q := q * base + hex_char_to_int(char);
+				while(is_hex(char) and not (L'length = 0))  loop
+					read(L, char);
+					if(is_hex(char)) then
+						q := q * base + hex_char_to_int(char);
+					end if;
+				end loop;
+			end if;
+			I := q;
+		else
+			assert false report "Couldn't read an integer"
+			severity error;
+		end if;
+	end read_int;
+
+-----------------------------------------------------------------------------
+-- *Module      : sget_int
+--
+-- *Description : Same as read_int except for strings
+-----------------------------------------------------------------------------
+	procedure sget_int(S : in string;  P : inout integer; X : out integer) is
+	variable char : character;
+	variable base : integer;
+	variable q : integer;
+	begin
+		if(S'length > P) then
+			char := ' ';
+			while(char = ' ') loop	-- Skip spaces
+				char := S(P);
+				P := P + 1;
+			end loop;
+			base := 16;			-- Hex is the default
+			if(char = '%') then -- determine base
+				char := S(P);
+				P := P + 1;			
+				if(char = 'b' or char = 'B') then
+					base := 2;
+				elsif(char = 'x' or char = 'X') then
+					base := 16;
+				elsif(char = 'd' or char = 'D') then
+					base := 10;
+				else
+					assert false report "Unsupported Base detected when reading an integer"
+					severity error;
+				end if;
+				char := S(P);
+				P := P + 1;
+			end if;
+			q := 0;
+			if(is_hex(char)) then
+				q := q * base + hex_char_to_int(char);
+				while(is_hex(char) and not (S'length = P))  loop
+					char := S(P);
+					P := P + 1;					
+					if(is_hex(char)) then
+						q := q * base + hex_char_to_int(char);
+					end if;
+				end loop;
+			end if;
+			X := q;
+		else
+			assert false report "Couldn't read an integer"
+			severity error;
+		end if;
+	end sget_int;
+
+-----------------------------------------------------------------------------
+-- *Module      : write_hex_vector
+--
+-- *Description : writes out a vector as hex
+-----------------------------------------------------------------------------
+	procedure write_hex_vector(L : inout line; V : in STD_ULOGIC_VECTOR) is
+	variable C : character;
+	variable VV : STD_ULOGIC_VECTOR(((V'length + 3)/4) * 4 - 1 downto 0);
+	begin
+
+		VV := (others => '0');
+		VV(V'length -1 downto 0) := V;
+
+		for i in VV'length/4 - 1 downto 0 loop
+			C := vector_to_hex_char(VV(i*4+3 downto i*4));
+			write(L, C);
+		end loop;
+
+	end write_hex_vector;
+
+-----------------------------------------------------------------------------
+-- *Module      : to_str
+--
+-- *Description : Converts a STD_ULOGIC_VECTOR to a string of the same length
+-----------------------------------------------------------------------------
+	function to_str(constant V: in STD_ULOGIC_VECTOR) return STRING is
+	variable S : STRING(1 to V'length);
+	variable sp : integer;
+	begin
+		sp := 1;
+		for i in V'range loop
+			case V(i) is
+				when '1' | 'H' =>
+					S(sp) := '1';
+				when '0' | 'L' =>
+					S(sp) := '0';
+				when others =>
+					S(sp) := 'X';
+			end case;
+			sp := sp + 1;
+		end loop;
+		return(S);
+	end to_str;
+
+-----------------------------------------------------------------------------
+-- *Module      : to_str
+--
+-- *Description : Converts a STD_ULOGIC to a string
+-----------------------------------------------------------------------------
+	function to_str(constant V: in STD_ULOGIC) return STRING is
+--	variable S : STRING(1);
+	begin
+			case V is
+				when '1' | 'H' =>
+					return("1");
+				when '0' | 'L' =>
+					return("0");
+				when others =>
+					return("X");
+			end case;
+		return("X");
+	end to_str;
+
+-----------------------------------------------------------------------------
+-- *Module      : to_str
+--
+-- *Description : Converts a integer to a string
+-----------------------------------------------------------------------------
+	function  to_str(constant val : in INTEGER) return STRING is
+	variable result : STRING(11 downto 1) := "-2147483648"; -- smallest integer and longest string
+	variable tmp    : INTEGER;
+	variable pos    : NATURAL := 1;
+	variable digit  : NATURAL;
+	begin
+		-- for the smallest integer MOD does not seem to work...
+		--if val = -2147483648 then	: compilation error with Xilinx tools...
+		if val < -2147483647 then
+			pos := 12;
+		else
+			tmp := abs(val);
+			loop
+		  		digit := abs(tmp MOD 10);
+			   		tmp := tmp / 10;
+		    		result(pos) := character'val(character'pos('0') + digit);
+		    		pos := pos + 1;
+		    		exit when tmp = 0;
+			end loop;
+			if val < 0 then
+		   		result(pos) := '-';
+		   		pos := pos + 1;
+			end if;
+		end if;
+		return result((pos-1) downto 1);
+	end to_str;
+
+-----------------------------------------------------------------------------
+-- *Module      : to_strn
+--
+-- *Description : Converts an integer to a string of length N
+-----------------------------------------------------------------------------
+	function  to_strn(constant val : in INTEGER; constant n : in INTEGER) return STRING is
+	variable result : STRING(11 downto 1) := "-2147483648"; -- smallest integer and longest string
+	variable tmp    : INTEGER;
+	variable pos    : NATURAL := 1;
+	variable digit  : NATURAL;
+	begin
+		-- for the smallest integer MOD does not seem to work...
+		--if val = -2147483648 then	: compilation error with Xilinx tools...
+		if val < -2147483647 then
+			pos := 12;
+		else
+			result := (others => ' ');
+			tmp := abs(val);
+			loop
+		  		digit := abs(tmp MOD 10);
+			   		tmp := tmp / 10;
+		    		result(pos) := character'val(character'pos('0') + digit);
+		    		pos := pos + 1;
+		    		exit when tmp = 0;
+			end loop;
+			if val < 0 then
+		   		result(pos) := '-';
+		   		pos := pos + 1;
+			end if;
+		end if;
+		return result(n downto 1);
+	end to_strn;
+
+
+end textutil;
diff --git a/hdl/gn4124core/sim/gn4124_bfm/util.vhd b/hdl/gn4124core/sim/gn4124_bfm/util.vhd
new file mode 100644
index 0000000000000000000000000000000000000000..c51777765340926dab50b075b00069b942f5e096
--- /dev/null
+++ b/hdl/gn4124core/sim/gn4124_bfm/util.vhd
@@ -0,0 +1,680 @@
+library ieee;
+use ieee.std_logic_1164.all;
+--library synopsys;
+--use synopsys.arithmetic.all;
+
+package UTIL is
+
+  type t_cmd_array is array (1 to 256) of integer;
+  
+  
+	function to_mvl ( b: in boolean ) return STD_ULOGIC;
+	function to_mvl ( i: in integer ) return STD_ULOGIC;
+	function to_vector(input,num_bits:integer) return STD_ULOGIC_VECTOR;
+--	function to_signed( b: in std_ulogic_vector ) return signed;
+--	function to_std_ulogic_vector( b: in signed ) return std_ulogic_vector;
+--	function std_logic_to_std_ulogic( b: in std_logic ) return std_ulogic;
+--	function std_ulogic_to_std_logic( b: in std_ulogic ) return std_logic;
+	function "and"(l: STD_ULOGIC_VECTOR; r: STD_ULOGIC) return STD_ULOGIC_VECTOR;
+	function "and"(l: STD_ULOGIC; r: STD_ULOGIC_VECTOR) return STD_ULOGIC_VECTOR;
+	function "and"(l: STD_ULOGIC_VECTOR; r: BOOLEAN) return STD_ULOGIC_VECTOR;
+	function "and"(l: BOOLEAN; r: STD_ULOGIC_VECTOR) return STD_ULOGIC_VECTOR;
+	function "and"(l: BOOLEAN; r: STD_ULOGIC) return STD_ULOGIC;
+	function "and"(l: STD_ULOGIC; r: BOOLEAN) return STD_ULOGIC;
+	function exp(input: STD_ULOGIC; num_bits: integer) return STD_ULOGIC_VECTOR;
+	function exp(input: STD_ULOGIC_VECTOR; num_bits: integer) return STD_ULOGIC_VECTOR;
+	function conv_integer ( ARG: in STD_ULOGIC_VECTOR ) return integer;
+	function "+"(l: STD_ULOGIC_VECTOR; r: STD_ULOGIC) return STD_ULOGIC_VECTOR;
+--	function "+"(l: STD_ULOGIC_VECTOR; r: STD_ULOGIC_VECTOR) return STD_ULOGIC_VECTOR;
+--	function "-"(l: STD_ULOGIC_VECTOR; r: STD_ULOGIC) return STD_ULOGIC_VECTOR;
+--	function "-"(l: STD_ULOGIC_VECTOR; r: STD_ULOGIC_VECTOR) return STD_ULOGIC_VECTOR;
+	function to_int(l: std_ulogic_vector) return natural;
+	function to_int(l: std_ulogic) return natural;
+	function and_reduce(ARG: STD_ULOGIC_VECTOR) return STD_ULOGIC;
+	function nand_reduce(ARG: STD_ULOGIC_VECTOR) return STD_ULOGIC;
+	function or_reduce(ARG: STD_ULOGIC_VECTOR) return STD_ULOGIC;
+	function nor_reduce(ARG: STD_ULOGIC_VECTOR) return STD_ULOGIC;
+	function xor_reduce(ARG: STD_ULOGIC_VECTOR) return STD_ULOGIC;
+	function xnor_reduce(ARG: STD_ULOGIC_VECTOR) return STD_ULOGIC;
+	function ge ( l, r : STD_ULOGIC_VECTOR ) return BOOLEAN;
+	function gt ( l, r : STD_ULOGIC_VECTOR ) return BOOLEAN;
+	function lt ( l, r : STD_ULOGIC_VECTOR ) return BOOLEAN;
+	function eq ( l, r : STD_ULOGIC_VECTOR ) return BOOLEAN;
+	function maximum ( arg1, arg2 : INTEGER) return INTEGER;
+	function minimum ( arg1, arg2 : INTEGER) return INTEGER;
+	procedure keep(signal X: inout STD_LOGIC);
+	function log2(A: in integer) return integer;
+    -------------------------------------------------------------------
+    -- Declaration of Synthesis directive attributes
+    -------------------------------------------------------------------
+    ATTRIBUTE synthesis_return : string ;
+
+end UTIL;
+
+package body UTIL is
+
+    --------------------------------------------------------------------
+--	function to_signed ( b: in std_ulogic_vector ) return signed is
+--		variable result	: signed(b'range);
+--	begin
+--		for i in b'range loop
+--			result(i) := b(i);
+--		end loop;
+--		return result;
+--	end to_signed;
+
+    --------------------------------------------------------------------
+--	function to_std_ulogic_vector ( b: in signed ) return std_ulogic_vector is
+--		variable result	: std_ulogic_vector(b'range);
+--	begin
+--		for i in b'range loop
+--			result(i) := b(i);
+--		end loop;
+--		return result;
+--	end to_std_ulogic_vector;
+
+    --------------------------------------------------------------------
+
+	function to_mvl ( b: in boolean ) return STD_ULOGIC is
+	begin
+		if ( b = TRUE ) then
+			return( '1' );
+		else
+			return( '0' );
+		end if;
+	end to_mvl;
+
+    --------------------------------------------------------------------
+
+	function to_mvl ( i: in integer ) return STD_ULOGIC is
+	begin
+		if ( i = 1 ) then
+			return( '1' );
+		else
+			return( '0' );
+		end if;
+	end to_mvl;
+
+    --------------------------------------------------------------------
+
+	function "and"(l: STD_ULOGIC; r: STD_ULOGIC_VECTOR) return STD_ULOGIC_VECTOR is
+	variable rr: STD_ULOGIC_vector(r'range);
+	begin
+		if (l = '1') then
+			rr := r;
+		else
+			rr := (others => '0');
+		end if;
+		return(rr);
+	end;
+
+    --------------------------------------------------------------------
+
+	function "and"(l: STD_ULOGIC_VECTOR; r: STD_ULOGIC) return STD_ULOGIC_VECTOR is
+	variable ll: STD_ULOGIC_vector(l'range);
+	begin
+		if (r = '1') then
+			ll := l;
+		else
+			ll := (others => '0');
+		end if;
+		return(ll);
+	end;
+
+    --------------------------------------------------------------------
+
+	function "and"(l: BOOLEAN; r: STD_ULOGIC_VECTOR) return STD_ULOGIC_VECTOR is
+	variable rr: STD_ULOGIC_vector(r'range);
+	begin
+		if (l) then
+			rr := r;
+		else
+			rr := (others => '0');
+		end if;
+		return(rr);
+	end;
+
+    --------------------------------------------------------------------
+
+	function "and"(l: STD_ULOGIC_VECTOR; r: BOOLEAN) return STD_ULOGIC_VECTOR is
+	variable ll: STD_ULOGIC_vector(l'range);
+	begin
+		if (r) then
+			ll := l;
+		else
+			ll := (others => '0');
+		end if;
+		return(ll);
+	end;
+
+    --------------------------------------------------------------------
+
+	function "and"(l: BOOLEAN; r: STD_ULOGIC) return STD_ULOGIC is
+	variable ll: STD_ULOGIC;
+	begin
+		if (l) then
+			ll := r;
+		else
+			ll := '0';
+		end if;
+		return(ll);
+	end;
+
+    --------------------------------------------------------------------
+
+	function "and"(l: STD_ULOGIC; r: BOOLEAN) return STD_ULOGIC is
+	variable ll: STD_ULOGIC;
+	begin
+		if (r) then
+			ll := l;
+		else
+			ll := '0';
+		end if;
+		return(ll);
+	end;
+
+    --------------------------------------------------------------------
+
+--	function std_ulogic_to_std_logic(b : std_ulogic) return std_logic is
+--	variable result: std_logic;
+--	begin
+--		result := b;
+--		return result;
+--	end;
+
+    --------------------------------------------------------------------
+ 
+--	function std_logic_to_std_ulogic(b : std_logic) return std_ulogic is
+--	variable result: std_ulogic;
+--	begin
+--		result := b;
+--		return result;
+--	end;
+ 
+	--------------------------------------------------------------------
+
+	function to_vector(input,num_bits: integer) return std_ulogic_vector is
+		variable vec: std_ulogic_vector(num_bits-1 downto 0);
+		variable a: integer;
+	begin
+		a := input;
+		for i in 0 to num_bits-1 loop
+			if ((a mod 2) = 1) then
+				vec(i) := '1';
+			else
+				vec(i) := '0';
+			end if;
+			a := a / 2;
+		end loop;
+		return vec;
+	end to_vector;
+
+
+
+--	FUNCTION  to_vector(input,num_bits:integer) RETURN STD_ULOGIC_VECTOR IS
+--	VARIABLE result:STD_ULOGIC_VECTOR(num_bits-1 DOWNTO 0);
+--	VARIABLE weight:integer;
+--	VARIABLE temp:integer;
+--	BEGIN
+--		weight := 2**(num_bits-1);
+--		temp := input;
+--		FOR i in result'HIGH DOWNTO result'LOW LOOP
+--			IF temp >= weight THEN
+--				result(i) := '1';
+--				temp := temp - weight;
+--			ELSE
+--				result(i) := '0';
+--			END IF;
+--			weight := weight/2;
+--		END LOOP;
+--		RETURN result;
+--	END to_vector;
+
+	--------------------------------------------------------------------
+	-- exp: Expand one bit into many
+	--------------------------------------------------------------------
+
+	FUNCTION  exp(input:STD_ULOGIC; num_bits:integer) RETURN STD_ULOGIC_VECTOR IS
+	VARIABLE result:STD_ULOGIC_VECTOR(num_bits-1 DOWNTO 0);
+	BEGIN
+		FOR i in result'HIGH DOWNTO result'LOW LOOP
+			result(i) := input;
+		END LOOP;
+		RETURN result;
+	END exp;
+
+	--------------------------------------------------------------------
+	-- exp: Expand n bits into m bits
+	--------------------------------------------------------------------
+
+	FUNCTION  exp(input:STD_ULOGIC_VECTOR; num_bits:integer) RETURN STD_ULOGIC_VECTOR IS
+	VARIABLE result:STD_ULOGIC_VECTOR(num_bits-1 DOWNTO 0);
+	BEGIN
+		result(input'high-input'low downto 0) := input;
+		result(num_bits-1 downto input'high-input'low+1) := (others => '0');
+		RETURN result;
+	END exp;
+
+	--------------------------------------------------------------------
+	-- conv_integer
+	--------------------------------------------------------------------
+
+	function conv_integer ( ARG: in STD_ULOGIC_VECTOR ) return integer is
+	variable result: INTEGER;
+	begin
+		assert ARG'length <= 31
+			report "ARG is too large in CONV_INTEGER"
+			severity FAILURE;
+		result := 0;
+		for i in ARG'range loop
+			result := result * 2;
+			if(ARG(i) = 'H' or ARG(i) = '1') then
+				result := result + 1;
+			end if;
+		end loop;
+		return result;
+	end;
+
+	--------------------------------------------------------------------
+	-- "+" Increment function
+	--------------------------------------------------------------------
+	function "+"(L: STD_ULOGIC_VECTOR; R: STD_ULOGIC) return STD_ULOGIC_VECTOR is
+	variable Q: STD_ULOGIC_VECTOR(L'range);
+	variable A: STD_ULOGIC;
+	begin
+		A := R;
+		for i in L'low to L'high loop
+			Q(i) := L(i) xor A;
+			A := A and L(i);
+		end loop;
+		return Q;
+	end;
+
+	--------------------------------------------------------------------
+	-- "+" adder function
+	--------------------------------------------------------------------
+--	function "+"(L: STD_ULOGIC_VECTOR; R: STD_ULOGIC_VECTOR) return STD_ULOGIC_VECTOR is
+--	variable Q	 : SIGNED(L'range);
+--	variable result: STD_ULOGIC_VECTOR(L'range);
+--	begin
+--		Q      := to_signed(L) + to_signed(R);
+--		result := to_std_ulogic_vector(Q);
+--		return result;
+--	end;
+
+	--------------------------------------------------------------------
+	-- "-" Decrement function
+	--------------------------------------------------------------------
+--	function "-"(L: STD_ULOGIC_VECTOR; R: STD_ULOGIC) return STD_ULOGIC_VECTOR is
+--	variable Q: STD_ULOGIC_VECTOR(L'range);
+--	variable A: STD_ULOGIC;
+--	begin
+--		A := R;
+--		for i in L'low to L'high loop
+--			Q(i) := L(i) xor A;
+--			A := A and not L(i);
+--		end loop;
+--		return Q;
+--	end;
+
+	--------------------------------------------------------------------
+	-- "-" subtractor function
+	--------------------------------------------------------------------
+--	function "-"(L: STD_ULOGIC_VECTOR; R: STD_ULOGIC_VECTOR) return STD_ULOGIC_VECTOR is
+--	variable Q     : SIGNED(L'range);
+--	variable result: STD_ULOGIC_VECTOR(L'range);
+--	begin
+--		Q      := to_signed(L) - to_signed(R);
+--		result := to_std_ulogic_vector(Q);
+--		return result;
+--	end;
+
+	--------------------------------------------------------------------
+	-- to_int : Convert std_ulogic_vector to an integer
+	--------------------------------------------------------------------
+	function to_int(l: std_ulogic_vector) return natural is
+	variable result: natural := 0;
+	begin
+		for t1 in l'range loop
+			result := result * 2;
+			if (l(t1) = '1') or (l(t1) = 'H') then
+				result := result + 1;
+			end if;
+		end loop;
+		return result;
+	end to_int;
+
+	--------------------------------------------------------------------
+	-- to_int : Convert std_ulogic_vector to an integer
+	--------------------------------------------------------------------
+	function to_int(l: std_ulogic) return natural is
+	variable result: natural := 0;
+	begin
+		if (l = '1') or (l = 'H') then
+			result := 1;
+		else
+			result := 0;
+		end if;
+		return result;
+	end to_int;
+
+	--------------------------------------------------------------------
+	-- Reduce Functions
+	--------------------------------------------------------------------
+	function and_reduce(ARG: STD_ULOGIC_VECTOR) return STD_ULOGIC is
+	variable result: STD_ULOGIC;
+	begin
+	result := '1';
+	for i in ARG'range loop
+		result := result and ARG(i);
+	end loop;
+	return result;
+	end;
+
+	function nand_reduce(ARG: STD_ULOGIC_VECTOR) return STD_ULOGIC is
+	begin
+	return not and_reduce(ARG);
+	end;
+
+	function or_reduce(ARG: STD_ULOGIC_VECTOR) return STD_ULOGIC is
+	variable result: STD_ULOGIC;
+	begin
+	result := '0';
+	for i in ARG'range loop
+		result := result or ARG(i);
+	end loop;
+	return result;
+	end;
+
+	function nor_reduce(ARG: STD_ULOGIC_VECTOR) return STD_ULOGIC is
+	begin
+	return not or_reduce(ARG);
+	end;
+
+	function xor_reduce(ARG: STD_ULOGIC_VECTOR) return STD_ULOGIC is
+	variable result: STD_ULOGIC;
+	begin
+	result := '0';
+	for i in ARG'range loop
+		result := result xor ARG(i);
+	end loop;
+	return result;
+	end;
+
+	function xnor_reduce(ARG: STD_ULOGIC_VECTOR) return STD_ULOGIC is
+	begin
+	return not xor_reduce(ARG);
+	end;
+
+	--------------------------------------------------------------------
+	-- Some useful generic functions
+	--------------------------------------------------------------------
+	--//// Zero Extend ////
+	--
+	-- Function zxt
+	--
+	FUNCTION zxt( q : STD_ULOGIC_VECTOR; i : INTEGER ) RETURN STD_ULOGIC_VECTOR IS
+		VARIABLE qs : STD_ULOGIC_VECTOR (1 TO i);
+		VARIABLE qt : STD_ULOGIC_VECTOR (1 TO q'length);
+		-- Hidden function. Synthesis directives are present in its callers
+	BEGIN
+		qt := q;
+		IF i < q'length THEN
+			qs := qt( (q'length-i+1) TO qt'right);
+		ELSIF i > q'length THEN
+			qs := (OTHERS=>'0');
+			qs := qs(1 TO (i-q'length)) & qt;
+		ELSE
+			qs := qt;
+		END IF;
+		RETURN qs;
+	END;
+
+	FUNCTION maximum (arg1,arg2:INTEGER) RETURN INTEGER IS
+	BEGIN
+		IF(arg1 > arg2) THEN
+			RETURN(arg1) ;
+		ELSE
+			RETURN(arg2) ;
+		END IF;
+	END ;
+
+	FUNCTION minimum (arg1,arg2:INTEGER) RETURN INTEGER IS
+	BEGIN
+		IF(arg1 < arg2) THEN
+			RETURN(arg1) ;
+		ELSE
+			RETURN(arg2) ;
+		END IF;
+	END ;
+
+	--------------------------------------------------------------------
+	-- Comparision functions
+	--------------------------------------------------------------------
+--
+-- Equal functions.
+--
+	TYPE stdlogic_boolean_table IS ARRAY(std_ulogic, std_ulogic) OF BOOLEAN;
+
+	CONSTANT eq_table : stdlogic_boolean_table := (
+	--
+	 ----------------------------------------------------------------------------
+	--      |  U       X      0     1      Z      W      L      H      D |   |
+    --
+     ----------------------------------------------------------------------------
+    ( FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE ),  -- | U |
+    ( FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE ),  -- | X |
+    ( FALSE, FALSE, TRUE, FALSE, FALSE, FALSE, TRUE, FALSE, FALSE ),    -- | 0 |
+    ( FALSE, FALSE, FALSE, TRUE, FALSE, FALSE, FALSE, TRUE, FALSE ),    -- | 1 |
+    ( FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE ),  -- | Z |
+    ( FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE ),  -- | W |
+    ( FALSE, FALSE, TRUE, FALSE, FALSE, FALSE, TRUE, FALSE, FALSE ),    -- | L |
+    ( FALSE, FALSE, FALSE, TRUE, FALSE, FALSE, FALSE, TRUE, FALSE ),    -- | H |
+    ( FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE )   -- | D |
+    );
+
+    FUNCTION eq  ( l, r : STD_LOGIC ) RETURN BOOLEAN IS
+        -- Equal for two logic types  
+        VARIABLE result : BOOLEAN ;
+        ATTRIBUTE synthesis_return OF result:VARIABLE IS "EQ" ; 
+    BEGIN
+        result := eq_table( l, r );
+        RETURN result ;
+    END;
+
+    FUNCTION eq  ( l,r : STD_ULOGIC_VECTOR ) RETURN BOOLEAN IS
+        CONSTANT ml  : INTEGER := maximum( l'length, r'length );
+        VARIABLE lt  : STD_ULOGIC_VECTOR ( 1 TO ml );
+        VARIABLE rt  : STD_ULOGIC_VECTOR ( 1 TO ml );
+        -- Arithmetic Equal for two Unsigned vectors  
+        VARIABLE result : BOOLEAN ;
+        ATTRIBUTE synthesis_return OF result:VARIABLE IS "EQ" ; 
+    BEGIN
+        lt := zxt( l, ml );
+        rt := zxt( r, ml );
+        FOR i IN lt'range LOOP
+            IF NOT eq( lt(i), rt(i) ) THEN
+               result := FALSE;
+               RETURN result ;
+            END IF;
+        END LOOP;
+        RETURN TRUE;
+    END;
+
+    TYPE std_ulogic_fuzzy_state IS ('U', 'X', 'T', 'F', 'N');
+    TYPE std_ulogic_fuzzy_state_table IS ARRAY ( std_ulogic, std_ulogic ) OF std_ulogic_fuzzy_state;
+
+    CONSTANT ge_fuzzy_table : std_ulogic_fuzzy_state_table := (
+	--      ----------------------------------------------------
+	--      |  U    X    0    1    Z    W    L    H    D         |   |
+	--      ----------------------------------------------------
+            ( 'U', 'U', 'N', 'U', 'U', 'U', 'N', 'U', 'U' ),  -- | U |
+	        ( 'U', 'X', 'N', 'X', 'X', 'X', 'N', 'X', 'X' ),  -- | X |
+	        ( 'U', 'X', 'N', 'F', 'X', 'X', 'N', 'F', 'X' ),  -- | 0 |
+	        ( 'N', 'N', 'T', 'N', 'N', 'N', 'T', 'N', 'N' ),  -- | 1 |
+	        ( 'U', 'X', 'N', 'X', 'X', 'X', 'N', 'X', 'X' ),  -- | Z |
+	        ( 'U', 'X', 'N', 'X', 'X', 'X', 'N', 'X', 'X' ),  -- | W |
+	        ( 'U', 'X', 'N', 'F', 'X', 'X', 'N', 'F', 'X' ),  -- | L |
+	        ( 'N', 'N', 'T', 'N', 'N', 'N', 'T', 'N', 'N' ),  -- | H |
+	        ( 'U', 'X', 'N', 'X', 'X', 'X', 'N', 'X', 'X' )   -- | D |
+    );
+
+    FUNCTION ge  ( L,R : std_ulogic_vector ) RETURN boolean IS
+        CONSTANT ml  : integer := maximum( L'LENGTH, R'LENGTH );
+        VARIABLE lt  : std_ulogic_vector ( 1 to ml );
+        VARIABLE rt  : std_ulogic_vector ( 1 to ml );
+        VARIABLE res : std_ulogic_fuzzy_state;
+        -- Greater-than-or-equal for two Unsigned vectors
+        VARIABLE result : BOOLEAN ;
+        ATTRIBUTE synthesis_return OF result:VARIABLE IS "GTE" ; 
+    begin
+        lt := zxt( l, ml );
+        rt := zxt( r, ml );
+        FOR i IN lt'RANGE LOOP
+            res := ge_fuzzy_table( lt(i), rt(i) );
+            CASE res IS
+              WHEN 'U'          => RETURN FALSE;
+              WHEN 'X'          => RETURN FALSE;
+              WHEN 'T'          => RETURN TRUE;
+              WHEN 'F'          => RETURN FALSE;
+              WHEN OTHERS       => null;
+            END CASE;
+        END LOOP;
+        result := TRUE ;
+        RETURN result;
+    end ;
+
+--
+-- Greater Than functions.
+--
+    CONSTANT gtb_table : stdlogic_boolean_table := (
+    --
+     ----------------------------------------------------------------------------
+    --      |  U       X      0     1      Z      W      L      H      D |   |
+    --
+     ----------------------------------------------------------------------------
+    ( FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE ),  -- | U |
+    ( FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE ),  -- | X |
+    ( FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE ),  -- | 0 |
+    ( FALSE, FALSE, TRUE, FALSE, FALSE, FALSE, TRUE, FALSE, FALSE ),    -- | 1 |
+    ( FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE ),  -- | Z |
+    ( FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE ),  -- | W |
+    ( FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE ),  -- | L |
+    ( FALSE, FALSE, TRUE, FALSE, FALSE, FALSE, TRUE, FALSE, FALSE ),    -- | H |
+    ( FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE )   -- | D |
+    );
+
+    FUNCTION gt  ( l, r : std_logic ) RETURN BOOLEAN IS
+        -- Greater-than for two logic types
+        VARIABLE result : BOOLEAN ;
+        ATTRIBUTE synthesis_return OF result:VARIABLE IS "GT" ; 
+    BEGIN
+        result := gtb_table( l, r );
+        RETURN result ;
+    END ;
+
+    FUNCTION gt  ( l,r : STD_ULOGIC_VECTOR ) RETURN BOOLEAN IS
+        CONSTANT ml  : INTEGER := maximum( l'length, r'length );
+        VARIABLE lt  : STD_ULOGIC_VECTOR ( 1 TO ml );
+        VARIABLE rt  : STD_ULOGIC_VECTOR ( 1 TO ml );
+        -- Greater-than for two logic unsigned vectors
+        VARIABLE result : BOOLEAN ;
+        ATTRIBUTE synthesis_return OF result:VARIABLE IS "GT" ; 
+    BEGIN
+        lt := zxt( l, ml );
+        rt := zxt( r, ml );
+        FOR i IN lt'range LOOP
+            IF NOT eq( lt(i), rt(i) ) THEN
+               result := gt( lt(i), rt(i) );
+               RETURN result ;
+            END IF;
+        END LOOP;
+        RETURN FALSE;
+    END;
+
+--
+-- Less Than functions.
+--
+    CONSTANT ltb_table : stdlogic_boolean_table := (
+    --
+     ----------------------------------------------------------------------------
+    --      |  U       X      0     1      Z      W      L      H      D |   |
+    --
+     ----------------------------------------------------------------------------
+    ( FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE ),  -- | U |
+    ( FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE ),  -- | X |
+    ( FALSE, FALSE, FALSE, TRUE, FALSE, FALSE, FALSE, TRUE, FALSE ),    -- | 0 |
+    ( FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE ),  -- | 1 |
+    ( FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE ),  -- | Z |
+    ( FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE ),  -- | W |
+    ( FALSE, FALSE, FALSE, TRUE, FALSE, FALSE, FALSE, TRUE, FALSE ),    -- | L |
+    ( FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE ),  -- | H |
+    ( FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE )   -- | D |
+    );
+
+    FUNCTION lt  ( l, r : STD_LOGIC ) RETURN BOOLEAN IS
+        -- Less-than for two logic types
+        VARIABLE result : BOOLEAN ;
+        ATTRIBUTE synthesis_return OF result:VARIABLE IS "LT" ; 
+    BEGIN
+        result := ltb_table( l, r );
+        RETURN result ;
+    END;
+
+    FUNCTION lt  ( l,r : STD_ULOGIC_VECTOR ) RETURN BOOLEAN IS
+        CONSTANT ml  : INTEGER := maximum( l'length, r'length );
+        VARIABLE ltt  : STD_ULOGIC_VECTOR ( 1 TO ml );
+        VARIABLE rtt  : STD_ULOGIC_VECTOR ( 1 TO ml );
+        -- Less-than for two Unsigned vectors
+        VARIABLE result : BOOLEAN ;
+        ATTRIBUTE synthesis_return OF result:VARIABLE IS "LT" ; 
+    BEGIN
+        ltt := zxt( l, ml );
+        rtt := zxt( r, ml );
+        FOR i IN ltt'range LOOP
+            IF NOT eq( ltt(i), rtt(i) ) THEN
+               result := lt( ltt(i), rtt(i) );
+               RETURN result ;
+            END IF;
+        END LOOP;
+        RETURN FALSE;
+    END;
+
+    --------------------------------------------------------------------
+    -- "keep" Retain Last value when floated
+    --------------------------------------------------------------------
+    procedure keep(signal X: inout STD_LOGIC) is
+    begin
+		if(X = 'Z') then
+			if(X'last_value = '0') then
+				X <= 'L';
+			elsif(X'last_value = '1') then
+				X <= 'H';
+			else
+				X <= 'Z';
+			end if;
+		else
+			X <= 'Z';
+		end if;
+    end keep;
+
+	---------------------------------------------------------------------
+	-- log base 2 function
+	---------------------------------------------------------------------
+	function log2 ( A: in integer ) return integer is
+		variable B  : integer;
+		begin
+			B := 1;
+			for i in 0 to 31 loop
+				if not ( A > B ) then
+					return ( i );
+					exit;
+				end if;
+				B := B * 2;
+			end loop;
+	end log2;
+
+
+
+end UTIL;
+