module Params( -- a specific stage on ISyntax for handling params iParams, -- functions for checking if a param's expr is legal -- (is a constant expression, as defined by Verilog) isConstIExpr, isConstAExpr ) where import qualified Data.Map as M import Error(internalError, EMsg, ErrMsg(..), ErrorHandle, bsError) import PPrint import Id import Position import ISyntax import ASyntax import VModInfo import Prim -- ========== -- XXX This could become part of IExpand.handlePrim on ICVerilog, -- XXX instead of being a separate pass. iParams :: ErrorHandle -> IModule a -> IO (IModule a) iParams errh imod = let ds = imod_local_defs imod ss = imod_state_insts imod {- -- XXX not needed because we don't need to know the Ids, -- XXX because the references are marked as ICModParam size_ps = imod_type_args imod inputs = imod_wire_args imod wi = imod_external_wires imod varginfo = wArgs wi -- identify which inputs are parameters param_inputs = [ i | (i, Param {}) <- zip inputs varginfo ] param_ids = map fst size_ps ++ map fst param_inputs -} -- create a map of ids and the exprs they inline to -- (with each expr already itself inlined completely) -- (assuming that "ds" is tsorted) --dmap :: M.Map Id (IExpr a) dmap = M.fromList [ (i, iSubst dmap e) | IDef i _ e _ <- ds ] ss' = map (inlineParams dmap) ss imod' = imod { imod_state_insts = ss' } emsgs = concatMap checkParams ss' in if (null emsgs) then return imod' else bsError errh emsgs inlineParams :: M.Map Id (IExpr a) -> (Id, IStateVar a) -> (Id, IStateVar a) inlineParams dmap (inst, svar) = let -- get the relevant fields from the IStateVar varginfo = vArgs (isv_vmi svar) es = isv_iargs svar -- for each parameter instantiation argument, -- inline variables references in its instantiation expression inlineParam (Param {}, expr) = iSubst dmap expr inlineParam (_, expr) = expr -- create the new IStateVar to return es' = map inlineParam (zip varginfo es) svar' = svar { isv_iargs = es' } in (inst, svar') checkParams :: (Id, IStateVar a) -> [EMsg] checkParams (inst, svar) = let -- get the relevant fields from the IStateVar varginfo = vArgs (isv_vmi svar) arg_es = isv_iargs svar -- position for reporting errors pos = getPosition inst -- users only know the instantiation arguments by position, -- so zip with a number indicating the position triples = zip3 [1..] varginfo arg_es -- check a single param checkParam :: (Integer, VArgInfo, IExpr a) -> [EMsg] checkParam (n, varginfo@(Param {}), expr) = if (not (isConstIExpr expr)) then let port_name = getIdString (getVArgInfoName varginfo) inst_name = getIdString inst in [(pos, EModParameterDynamic inst_name port_name)] else [] checkParam _ = [] in concatMap checkParam triples -- XXX copied from IInline; consider putting it in one place? iSubst :: M.Map Id (IExpr a) -> IExpr a -> IExpr a iSubst m e = sub e where sub (IAps f ts es) = IAps (sub f) ts (map sub es) sub d@(ICon i _) = case M.lookup i m of Nothing -> d Just e -> e sub ee = internalError ("iSubst: " ++ ppReadable ee) -- ========== isConstIExpr :: IExpr a -> Bool isConstIExpr (ICon _ (ICInt {} )) = True -- constant number isConstIExpr (ICon _ (ICReal {} )) = True -- constant number isConstIExpr (ICon _ (ICString {} )) = True -- constant string isConstIExpr (ICon _ (ICModParam {} )) = True -- parameter reference isConstIExpr (ICon _ (ICUndet {} )) = -- Undetermined values will become constant values True isConstIExpr (ICon _ (ICDef {})) = -- Local def references should have been inlined away internalError "Params.isConstIExpr: inlining not complete" isConstIExpr (ICon _ (ICPrim {})) = -- primitive operators should be applied, -- there is no 0-arity primitive that we allow False isConstIExpr (ICon _ (ICCon {} )) = -- Constructors should be turned into bits by now -- XXX check this! False isConstIExpr (IAps f _ es) = -- application of acceptable operators on constant args is constant (isConstIExprFunc f && all isConstIExpr es) isConstIExpr _ = False isConstIExprFunc :: IExpr a -> Bool -- XXX this should check for only prims which can be turned into -- XXX acceptable Verilog primitives, and return False for others isConstIExprFunc (ICon _ (ICPrim {})) = True -- tuples, selector functions, constructor selectors etc, should all -- have been evaluated away, leaving only bits isConstIExprFunc _ = False -- ========== -- Check that expressions for submodule instantiation parameters are -- "constant expressions": literals, top-level parameters, or certain -- primitive operations on those. -- The first argument is a list of ASPort names which can be assumed -- to have constant values isConstAExpr :: [AId] -> AExpr -> Bool isConstAExpr _ (ASInt {}) = True -- constant number isConstAExpr _ (ASReal {}) = True -- constant real number isConstAExpr _ (ASStr {}) = True -- constant string isConstAExpr _ (ASParam {}) = True -- parameter reference isConstAExpr ps (ASPort _ port_id) = -- ports are dynamic unless we're told to assume them constant (port_id `elem` ps) isConstAExpr _ (ASAny {}) = -- Undetermined values will become constant values True isConstAExpr _ e@(ASDef {}) = -- Local def references should have been inlined away internalError ("Params.isConstAExpr: inlining not complete: " ++ ppReadable e) -- XXX could have said "False" here, so that it's available -- XXX for other uses? isConstAExpr ps (APrim { aprim_prim = op, ae_args = es }) = (isConstOp op && all (isConstAExpr ps) es) isConstAExpr _ _ = False -- ========== -- XXX Should this go into Prims.hs? -- Check whether a primitive is an acceptable operator for -- a constant ASyntax expression. -- (This check should not be specific to any backend.) isConstOp :: PrimOp -> Bool isConstOp PrimAdd = True -- VAdd isConstOp PrimSub = True -- VSub isConstOp PrimAnd = True -- VAnd isConstOp PrimOr = True -- VOr isConstOp PrimXor = True -- VXor isConstOp PrimMul = True -- VMul isConstOp PrimQuot = True -- VQuot isConstOp PrimRem = True -- VRem isConstOp PrimSL = True -- VShL isConstOp PrimSRL = True -- VShR isConstOp PrimSRA = True -- VeriQuirks removes it? isConstOp PrimInv = True -- VInv isConstOp PrimNeg = True -- VNeg isConstOp PrimEQ = True -- VEQ isConstOp PrimEQ3 = True -- VEQ3 isConstOp PrimULE = True -- VULE isConstOp PrimULT = True -- VULT isConstOp PrimSLE = True -- converted to VULT isConstOp PrimSLT = True -- converted to VULE -- These would be replaced with Concat or Extract? isConstOp PrimSignExt = True isConstOp PrimZeroExt = True isConstOp PrimTrunc = True isConstOp PrimExtract = True isConstOp PrimConcat = True -- This would also be replaced? isConstOp PrimSplit = True -- not handled in AVerilogUtil isConstOp PrimBNot = True -- VNot isConstOp PrimBAnd = True -- VLAnd isConstOp PrimBOr = True -- VLOr -- Case statements will need to be inlined as if-expressions in -- the Verilog backend, but we consider them constant expressions -- if the arguments are constant isConstOp PrimIf = True -- becomes VEIf ( b?x:y ) isConstOp PrimCase = True -- These appear as a pair and are equivalent to PrimCase/PrimIf isConstOp PrimArrayDynSelect = True isConstOp PrimBuildArray = True -- XXX No reason not to allow these, but they won't occur in parameter exprs isConstOp PrimMux = False isConstOp PrimPriMux = False -- This should be transformed away too, right? isConstOp PrimSelect = True -- not handled in AVerilog -- improved string parameter support isConstOp PrimStringConcat = True isConstOp _ = False -- ==========