module InlineCReg(aInlineCReg) where import ASyntax import ASyntaxUtil import Prim import Data.List(partition) import PPrint import IntLit(IntLit(..)) import qualified Data.Map as M import qualified Data.Set as S import BackendNamingConventions import ErrorUtil(internalError) -- -- 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 -- -- See comments in the InlineWires package, on which this is based. -- However, unlike for RWire, we don't rename the input port def to be -- the output port def, because the CReg output is a mux not a wire. -- So, unlike for RWire, we need to record both the inputs and outputs -- as inlined ports (not just the outputs). -- -- The register part of the CReg module is retained as a RegN/RegUN/RegA -- instance. This is separately inlined in the Verilog generation, -- depending on the -inline-reg flag. -- aInlineCReg :: ASPackage -> ASPackage aInlineCReg pkg@(ASPackage { aspkg_state_instances = vs, aspkg_state_outputs = svars, aspkg_values = defs, aspkg_inlined_ports = ips, aspkg_signal_info = si }) = let -- definition map (id -> value) defmap = M.fromList [(i, e) | (ADef i _t e _) <- defs] defset = M.keysSet defmap -- find the CReg modules -- and the remaining instances which we will leave in the package (creg_vs, noncreg_vs) = partition isCRegInst vs -- for each CReg instance, make a tuple of: -- * the instance name -- * the instance inputs and outputs -- * new Reg instance -- * new Reg instance's output ports -- * new defs (for the CReg outputs and the Reg inputs) creg_tuples = map mkCR creg_vs -- record the CReg port names creg_ports = concatMap (\(a,b,c,d,e) -> b) creg_tuples -- record the signal info for RTL grouping creg_signal_info = map (\(a,b,c,d,e) -> (a,"CReg",b)) creg_tuples si_ips = aspsi_inlined_ports si si' = si { aspsi_inlined_ports = creg_signal_info ++ si_ips } -- any new definitions new_defs = concatMap (\(a,b,c,d,e) -> e) creg_tuples -- new Reg instances new_vs = map (\(a,b,c,d,e) -> c) creg_tuples -- since the CReg output ports are now defined locally and are -- not outputs of modules anymore, remove them from the svars list (creg_svars, noncreg_svars) = -- XXX slightly inefficient because creg_ports contains also inputs partition (\(i,t) -> i `elem` creg_ports) svars -- the output ports for the new Reg instances new_reg_svars = concatMap (\(a,b,c,d,e) -> d) creg_tuples -- create a map of the CReg output ports to the signals which -- now carry their values -- (this will be used to replace ASPort uses with ASDef uses) portmap = M.fromList [(i, ASDef t i) | (i, t) <- creg_svars] -- function for making the tuple for a CReg mkCR :: AVInst -> (AId, [AId], AVInst, [(AId, AType)], [ADef]) mkCR avi = let ports = 5 i = avi_vname avi width = case (avi_iargs avi) of (ASInt _ _ (IntLit _ _ n) : _) -> n as -> internalError ("aInlineCReg: " ++ ppReadable as) dataTy = ATBit width makePorts n = [cregReadResId i n, cregWriteEnId i n, cregWriteArgId i n] old_creg_ports = concatMap makePorts [0..(ports-1)] -- if a write method is unused, create a def for it makeUnusedMethodDefs n = let en_id = cregWriteEnId i n arg_id = cregWriteArgId i n en_def = if (en_id `S.member` defset) then [] else [ADef en_id (ATBit 1) aFalse []] -- XXX is it unnecessary paranoia to check the data separately? arg_def = if (arg_id `S.member` defset) then [] else [ADef arg_id dataTy (ASAny dataTy Nothing) []] in en_def ++ arg_def makeReadResExpr 0 = ASPort dataTy (regReadResId i) makeReadResExpr n = let prev_en_e = ASDef aTBool (cregWriteEnId i (n-1)) prev_arg_e = ASDef dataTy (cregWriteArgId i (n-1)) prev_res_e = ASDef dataTy (cregReadResId i (n-1)) in APrim defaultAId dataTy PrimIf [prev_en_e, prev_arg_e, prev_res_e] makeReadResDef n = let res_id = cregReadResId i n in ADef res_id dataTy (makeReadResExpr n) [] new_creg_defs = concatMap makeUnusedMethodDefs [0..(ports-1)] ++ map makeReadResDef [0..(ports-1)] new_reg_avi = cregToReg avi new_reg_ports = [(regReadResId i, dataTy)] new_reg_defs = [ADef (regWriteEnId i) (ATBit 1) aTrue [], ADef (regWriteArgId i) dataTy (makeReadResExpr ports) []] in (i, old_creg_ports, new_reg_avi, new_reg_ports, new_creg_defs ++ new_reg_defs) in aSubst portmap $ pkg { -- XXX OK that this changes the order? aspkg_state_instances = new_vs ++ noncreg_vs -- XXX OK that this changes the order? , aspkg_state_outputs = new_reg_svars ++ noncreg_svars , aspkg_values = defs ++ new_defs , aspkg_inlined_ports = ips ++ creg_ports , aspkg_signal_info = si' }