content: module InlineWires(aInlineWires) where
  
  import ASyntax
  import ASyntaxUtil
  import Data.List(partition)
  import PFPrint
  import IntLit(IntLit(..))
  import qualified Data.Map as M
  import qualified Data.Set as S
  import BackendNamingConventions
  import Error(internalError, EMsg, WMsg)
  import Flags(Flags, removeCross)
  
  -- import Debug.Trace
  
  --
  -- The following contents of the ASPackage are changed:
  --   vs    :: [AVInst]    = state elements (Verilog instances)
  --   svars :: [AStateOut] = state element outputs (wires coming out of state)
  --   defs  :: [ADef]      = local definitions
  --
  --
  -- To inline wires, we do the following:
  --
  --  (1) Separate out the RWire, RWire0 and BypassWire instances from "vs",
  --      leaving all other instances.
  --
  --  (2) For each wire, we used to create an ADef for the
  --      whas and wget ports (RWire0 has no wget port), and define them
  --      in terms of the wset and wset_1 ports (the enable and the input
  --      data).  (Except when the set is not called, and so we give whas
  --      a constant value of False and wget a value of dont-care.)
  --      This resulted in unnecessary indirection, and sometimes the rw$set_1
  --      signal would remain in the Verilog and not the rw$wget.  So now,
  --      instead of indirection, we rename rw$EN_set and rw$set_1 to be
  --      rw$whas and rw$wget.  If the set signals aren't defined, then we
  --      do have to declare a new def for the outputs, with constant values.
  --      Note that we do not define rw$whas for BypassWires (since it should be
  --      just a constant 1).
  --
  --  (3) Create a substitution from (ASPort rw$whas) to (ASDef rw$whas),
  --      and substitute over the ASPackage.  (Because the signals are no
  --      longer submodule ports, but internally defined signals.)
  --
  --  (4) The "svars" is a list of state element outputs, so remove all
  --      the wire outputs from this list (those are just the def ids).
  --
  --      But don't lose them; keep them in the "ws" field in the ASPackage.
  --      This is a list of ports to inlined modules which should not be
  --      inlined if not necessary (for instance, if it only has one use).
  --
  --  (5) Return the ASPackage with the new defs, with the filtered vs,
  --      with the filtered svars, and with the substitution applied to
  --      all expressions in the ASPackage (with aSubst).
  --
  
  aInlineWires :: Flags -> ASPackage -> (ASPackage, [WMsg], [EMsg])
  aInlineWires flags pkg@(ASPackage { aspkg_state_instances = vs,
                                aspkg_state_outputs = svars,
                                aspkg_values = defs,
                                aspkg_signal_info = si }) =
  --  trace (ppReadable rws ++ ppReadable ds) $
      (aSubst rmap (pkg { aspkg_state_instances = nonwire_vs,
                          aspkg_state_outputs = nonwire_svars,
                           aspkg_values = (defs' ++ newdefs),
                          aspkg_inlined_ports = ws',
                          aspkg_signal_info = si' }),
       warnings, errors)
    where -- definition map (id -> value)
          defmap = M.fromList [(i, e) | (ADef i _t e _) <- defs]
          defset = M.keysSet defmap
          -- find the RWires and RWire0s, and the remaining
          -- instances which we will leave in the package
          (rws, vs') = partition isRWire vs
          (rw0s, nonrwire_vs) = partition isRWire0 vs'
          (bw0s, nonrwire_vs') = partition isBypassWire0 nonrwire_vs
          should_inline_bw x = isBypassWire x &&
                            ((not (isClockCrossingBypassWire x)) || (removeCross flags))
          (bws, nonwire_vs) = partition should_inline_bw nonrwire_vs'
          -- for each RWire instance, make a tuple of:
          --  * the instance name
          --  * the instance outputs (whas and possibly wget)
          --  * any new defs (only if the value is constant)
          --  * any substitutions on the defs (e.g. replace wsetEn with whas)
          --  * any warnings to trigger
          --  * any errors to trigger
          rwire_tuples = map mkRW rws ++ map mkRW0 rw0s ++
                         map mkBW bws ++ map mkBW0 bw0s
  
          -- record the rwire output port names in ws
          ws' = concatMap (\(a,b,c,d,e,f) -> b) rwire_tuples
  
          -- record the signal info for RTL grouping
          rwire_signal_info = map (\(a,b,c,d,e,f) -> (a,"RWire",b)) rwire_tuples
          si_ips = aspsi_inlined_ports si
          si' = si { aspsi_inlined_ports = rwire_signal_info ++ si_ips }
  
          -- XXX this traverses the package once for every inlines wire
          -- for performance the sub should be aggregated into a map
          -- perform the substitutions
          -- to rename input port defs with the output names
          renameADef s d@(ADef { adef_objid = i }) =
              case (lookup i s) of
                  Nothing    -> d
                  Just new_i -> d { adef_objid = new_i }
          name_subst = concatMap (\(a,b,c,d,e,f) -> d) rwire_tuples
          defs' = map (renameADef name_subst) defs
  
          -- any new definitions
          -- (to constants, when the input port was dangling)
          newdefs = concatMap (\(a,b,c,d,e,f) -> c) rwire_tuples
  
          -- -----
          warnings = concatMap (\(a,b,c,d,e,f) -> e) rwire_tuples
          errors   = concatMap (\(a,b,c,d,e,f) -> f) rwire_tuples
  
          -- since the RWire output ports are now defined locally and are
          -- not outputs of modules anymore, remove them from the svars list
          (wire_svars, nonwire_svars) =
              partition (\(i,t) -> i `elem` ws') svars
  
          -- create a map of the rwire values methods to the signals which
          -- now carry their values (whether new def or subst of existing def)
          -- (this will be used to replace ASPort uses with ASDef uses)
          rmap = M.fromList [(i, ASDef t i) | (i, t) <- wire_svars]
  
          -- functions for making the defs for RWire and BypassWire
          mkRW0 :: AVInst -> (AId,[AId],[ADef],[(AId,AId)],[WMsg],[EMsg])
          mkRW0 (AVInst { avi_vname=i }) = rw_defs i 0 True False
  
          mkRW :: AVInst  -> (AId,[AId],[ADef],[(AId,AId)],[WMsg],[EMsg])
          mkRW (AVInst { avi_vname=i,
                         avi_iargs=(ASInt _ _ (IntLit _ _ n) : _) }) = rw_defs i n False False
          mkRW (x@(_)) = internalError ("aRWire.mkRW: " ++ ppReadable x)
  
          mkBW0 :: AVInst  -> (AId,[AId],[ADef],[(AId,AId)],[WMsg],[EMsg])
          mkBW0 (AVInst { avi_vname=i }) = rw_defs i 0 False True
  
          mkBW :: AVInst  -> (AId,[AId],[ADef],[(AId,AId)],[WMsg],[EMsg])
          mkBW (AVInst { avi_vname=i,
                         avi_iargs=(ASInt _ _ (IntLit _ _ n) : _) }) = rw_defs i n False True
          mkBW (x@(_)) = internalError ("aRWire.mkBW: " ++ ppReadable x)
  
          -- take in the base module identifier
          -- the number of bits in the data input/output
          -- whether the enable should be 1 (for BypassWire)
          -- return:
          --   instance identifier
          --   list of state element outputs
          --   new internal definitions (that support the inlining)
          --   a substitution (to replace ASVar by ASDef where necessary)
          --   any warnings encountered (i.e. enables not night)
          rw_defs :: AId -> ASize -> Bool -> Bool -> (AId, [AId], [ADef], [(AId, AId)], [WMsg], [EMsg])
          rw_defs i sz is_rwire0 always_en =
              let
                  -- the rwire inputs
                  rw_en_id   = rwireSetEnId i
                  rw_data_id = rwireSetArgId i
                  -- the rwire outputs
                  rw_has_id  = rwireHasResId i
                  rw_get_id  = rwireGetResId i
  
                  -- we no longer report EEnableNotHigh or EEnableAlwaysLow,
                  -- as always_enabled property is now checked in AAddScheduleDefs
                  (wmsg, emsg) = ([], [])
  
                  -- what signal names are defined locally
                  -- if set enable is not defined, then whas is constant False
                  (whas_def, whas_subst) =
                      if always_en then
                        -- no definition or substitution if always_enabled
                        ([], [])
                      else if (rw_en_id `S.member` defset) then
                        ([], [(rw_en_id, rw_has_id)])
                      else
                        ([ADef rw_has_id (ATBit 1) aFalse []], [])
                  -- even if the enable has been defined, the data might not be
                  -- there because the RWire is never set or because the data
                  -- size is 0 (though that is probably unnecessary paranoia)
                  (wget_def, wget_subst) =
                      if (is_rwire0) then
                          ([], [])
                      else if (rw_data_id `S.member` defset) then
                          ([], [(rw_data_id, rw_get_id)])
                      else
                          ([ADef rw_get_id (ATBit sz) (ASAny (ATBit sz) Nothing) []],
                           [])
                  -- the output names
                  outputs =
                      -- wget
                      (if is_rwire0 then [] else [rw_get_id]) ++
                      -- whas
                      (if always_en then [] else [rw_has_id])
              in
                  (i, outputs, whas_def ++ wget_def, whas_subst ++ wget_subst, wmsg, emsg)
  