----------------------------------------------------------------------------------
-- Company: 
-- Engineer: 
-- 
-- Create Date:    15:59:45 05/06/2013 
-- Design Name: 
-- Module Name:    abo_localMux - Behavioral 
-- Project Name: 
-- Target Devices: 
-- Tool versions: 
-- Description: Inner component of ABO-seq
--              This file contains the ABO_seq.c tick function.
--              It is nearly directly implemtented into a process.
--
-- Dependencies: 
--
-- Revision: 
-- Revision 0.01 - File Created
-- Additional Comments: 
--
----------------------------------------------------------------------------------
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;

-- Uncomment the following library declaration if using
-- arithmetic functions with Signed or Unsigned values
--use IEEE.NUMERIC_STD.ALL;

-- Uncomment the following library declaration if instantiating
-- any Xilinx primitives in this code.
--library UNISIM;
--use UNISIM.VComponents.all;

entity abo_seq is
    Port ( A_in : in  boolean;
           B_in : in  boolean;
           O1 : out  boolean;
           O2 : out  boolean;
           A_out : out  boolean;
           B_out : out  boolean;
           
           -- Control           
           tick : in std_logic);
--           entry : in boolean);
end abo_seq;

architecture Behavioral of abo_seq is
   begin
   
   abo: process (tick)
-- Variable declaration
   -- ****************************************************************************
 
--   signal O2_int : std_logic ;
--  // Guards for basic blocks
--  //
--  // Note that these might be computed even more efficiently using a
--  // Program Dependence Graph (PDG), which could expose further parallelism.
--  // However, we for now make do with a simpler, locally computed scheme.
   variable u_g0 : boolean := false;     --,  //_entry,
   variable u_g1 : boolean := false;     -- //_go0_HandleA,
   variable u_g2 : boolean := false;     -- //_surf_HandleA_WaitA,
   variable u_g4 : boolean := false;     -- //_exit_HandleA,
   variable u_g5 : boolean := false;     -- //_surf_HandleB_WaitB,
   variable u_g7 : boolean := false;     -- //_exit_HandleB,
   variable u_g8 : boolean := false;     -- //_exit;

--  // Registered guards, for basic blocks that end in pause statements
--  // "true" indicates an active pause statement; initially pause statements are inactive
   variable u_g3 : boolean := false;   -- //_depth_HandleA_WaitA,
   variable u_g6 : boolean := false;   -- //_depth_HandleB_WaitB;

--  // Flags indicating that a region is empty (inactive),
--  // ie, neither just started nor any active pause
   variable u_e0 : boolean := false;  --  //_empty_HandleA,
   variable u_e1 : boolean := false;  --  //_empty_HandleB;

--  // Values; multiple instances of O1, O2, and (input) B
--  // On Naming:
--  // Use suffix derived from guards of the enclosing basic block.
--  // If there are multiple assignments to the same variable within on basic block, 
--  // use increasing counter, starting at 0, for the assignments - except for the last
--  // assignment, as that is the one visible outside of the basic block.
--  // The assignments with a counter suffix are only visible within the basic block.
--  // Eg, assume we are in basic block "entry":
--  //   X = 0; Y = X; X = 1
--  // becomes
--  //   X_entry0 = 0; Y_entry = X_entry0; X_entry = 1
--  //bool O1_g0, O1_g4, O1_g7, O1_g8, O2_g0, O2_g8, B_g4;
--
--  // Registered values of non-input variables
--  // For SW, one would not need to make these explicit, one might instead just use O1 and O2
--  // Note: O1 and O2 are declared globally, to make them visible outside of tick function
--  //static bool _preO1, _preO2;

   variable O1_int : boolean := false;
   variable O2_int : boolean := false;
   variable B_int : boolean := false;
   
   variable u_notInitial : boolean := false;
   variable u_notInitialDetect : boolean:= true;
--   variable u_entry : boolean;
   
  -- ****************************************************************************

   begin
   
   if (rising_edge(tick)) then

   B_int := B_in;

--   _g0 = !_notInitial;   // _g0 indicates initial tick
   u_g0 := not u_notInitial;
--   u_g0 := entry;        --//for external initial tick generation

--  // "Pre-initialize" registers in initial tick
  if (u_g0 = true) then 
   u_g3 := false;
   u_g6 := false;
--    //_preO1 = UNDEFINED;
--    //_preO2 = UNDEFINED;
  end if;

--  _signals2vars    -- not needed here

  if ( not (u_notInitial = true)) then
    u_notInitial := u_notInitialDetect;
  end if;

--  //Replaced with code that avoids jump:
--  _notInitial |= _notInitialDetect;

  if (u_g0 = true) then
--    O1_int := false;
    O1_int := false;

--    // Observations:
--    // 1) The above assignment is only effective when _g0 holds
--    // 2) If the assignment is effective, it overrides _preO1
--    // Thus, we perform a "sequential merge" below.
--    // This is somewhat similar to a phi node, except that the merged assignments
--    // do not have to come from separate control paths.
--    //_mergeFalse(O1_g0, _g0, _preO1);

--    O2_int := false;
    O2_int := false;
--    //_mergeFalse(O2_g0, _g0, _preO2);
   end if;

--  //fork1(HandleB, 1) {
--  //HandleA:

--  // Merge paths before reaching conditional
  u_g1 := u_g0 or u_g3;

--  //  if (!A) {

--  //  pause;  // WaitA
--  // Replace this pause with a register "_g2" that indicates an active pause
  u_g2 := (u_g1 and (not A_in));  -- // _g3 is registered value

--  //  goto HandleA;
--  //}

  u_g4 := (u_g1 and A_in);

  if (u_g4 = true) then
--    // The input B constitutes a "0th" copy of B that is always present
--    // Therefore, must create copy for following write to B
--    B_out <= true;
    B_int := true;
--    // Possible optimization for SW: replace assignment to B_g4 by assignment
--    // to just B, as it is the final value of B
--    //_mergeTrue(B_g4, _g4, B);

--    O1_int := true;
    O1_int := true;
--    //_mergeTrue(O1_g4, _g4, O1_g0);
  end if;

--  // Compute "output pins" of thread HandleA
--  // This is analogous to the LEM/REM pins [1, Fig. 11.1]
--  // Note: as "starting guard" we here use _g1, which guards the first block of the thread,
--  // instead of _g0, which guards the fork node. This sometimes allows slight optimizations,
--  // as for _e1
  u_e0 := not(u_g1 or u_g2);  -- // Dead thread - neither just started nor any active register

--  //} par {
--  //  HandleB:
--  //  pause;

--  // Observation, regarding the read of B:
--  // Possible writes to B are the input (called just "B") and B_g4.
--  // As B_g4 is a "sequential successor" of B, the "merge" of these two is
--  // already computed in B_g4
  u_g5 := (u_g0 or (u_g6 and (not (B_int))));  --  // Set register _g5

--  //  if (!B) {
--  //    goto HandleB;
--  //  }
  u_g7 := (u_g6 and (B_int));

  if (u_g7 = true) then
--    O1_int := true;
    O1_int := true;

--    // Observation:
--    // The assignments to O1 that correspond to O1_g4 and O1_g7
--    // are concurrent, i.e., not sequential, but confluent.
--    // Thus we (arbitrarily) merge them by first computing O1_g4, and then
--    // possibly overwriting this with "true" in the assignment to
--    // O1_g7.
--    // We could also have reversed this order.
--    //_mergeTrue(O1_g7, _g7, O1_g4);
  end if;

--  // Compute "output pins" of thread HandleB
--  // Note: as "starting guard" we here use _g5, which guards the first block of the thread,
--  // instead of _g0, which guards the fork node. For _e1, this reduces the operation/gate count, 
--  // since _g5 also happens to guard the one surface node of the thread, thus the resulting
--  // guard is !(_g5 | _g5), which reduces to just !_g5
--  // Otherwise we would have to compute !(_g0 | _g5) instead of just !_g5
  u_e1 := not u_g5;

--  //} join1(2);
--  // This corresponds to output K0 of Sychronizer circuit [1, Fig. 11.10]
--  // If this were not the basic block corresponding to the top-level exit node,
--  // we would have called this "_join_WaitAB"
  u_g8 := (u_g4 or u_g7)  -- // At least one thread must have just terminated
    and (u_g4 or u_e0)   -- // HandleA has terminated or was already dead
    and (u_g7 or u_e1);  -- // HandleB has terminated or was already dead

  if (u_g8 = true) then
--    O1_int := false;
    O1_int := false;
--    // O1_g8 is sequentially after O1_g4 and O1_g7.
--    // As stated above, O1_g4 and O1_g7 are concurrent, but are merged in
--    // O1_g7. Thus we here merge with O1_g7.
--    //_mergeFalse(O1_g8, _g8, O1_g7);

--    O2_int := true;
    O2_int := true;
--    //_mergeTrue(O2_g8, _g8, O2_g0);
  end if;

--  // Compute output values.
--  // This is where the nature of sequentiality becomes apparent:
--  // we always use the "last" write to a variable.
--  // For HW synthesis, would have to replace these by A_out, B_out, O1_out, O2_out
--  //A = A;       // Input A is not overwritten
--  //B = B_g4;
--  //O1 = O1_g8;
--  //O2 = O2_g8;

--  _vars2signals;      -- not needed here

  A_out <= A_in;
  B_out <= B_int;
  O1 <= O1_int;
  O2 <= O2_int; 

  u_g3 := u_g2;      --PRE(_g2);
  u_g6 := u_g5;      --PRE(_g5);
   
  end if;
  
  end process;

end Behavioral;








