{-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE ScopedTypeVariables #-} -- | High-level translation of Copilot Core into Bluespec. module Copilot.Compile.Bluespec.CodeGen ( -- * Type declarations mkStructDecln -- * Ring buffers , mkBuffDecln , mkIndexDecln , mkAccessDecln -- * Stream generators , mkGenFun -- * Monitor processing , mkStepRule , mkTriggerRule -- * Module interface specifications , mkSpecIfcFields ) where -- External imports import Data.String (IsString (..)) import qualified Language.Bluespec.Classic.AST as BS import qualified Language.Bluespec.Classic.AST.Builtin.Ids as BS import qualified Language.Bluespec.Classic.AST.Builtin.Types as BS -- Internal imports: Copilot import Copilot.Core -- Internal imports import Copilot.Compile.Bluespec.Expr import Copilot.Compile.Bluespec.External import Copilot.Compile.Bluespec.Name import Copilot.Compile.Bluespec.Type -- | Write a generator function for a stream. mkGenFun :: String -> Expr a -> Type a -> BS.CDefl mkGenFun name expr ty = -- name :: ty -- name = expr BS.CLValueSign (BS.CDef nameId (BS.CQType [] (transType ty)) [def]) [] where nameId = BS.mkId BS.NoPos $ fromString $ lowercaseName name def = BS.CClause [] [] (transExpr expr) -- | Bind a buffer variable and initialise it with the stream buffer. mkBuffDecln :: forall a. Id -> Type a -> [a] -> [BS.CStmt] mkBuffDecln sId ty xs = initVals ++ [BS.CSletrec [initBufSig]] where -- sId_0 :: Reg <- mkReg xs_0 -- ... -- sId_(n-1) :: Reg <- mkReg xs_(n-1) initVals = zipWith mkInitVal xs [0..] -- sId :: Vector n (Reg ) -- sId = update (... (update newVector 0 sId_0) ...) (n-1) sId_(n-1) initBufSig = BS.CLValueSign (BS.CDef nameId (BS.CQType [] vecTy) [initBufDef]) [] initBufDef = BS.CClause [] [] (genVector (\idx _ -> BS.CVar $ BS.mkId BS.NoPos $ fromString $ streamElemName sId idx) xs) nameId = BS.mkId BS.NoPos $ fromString $ streamName sId bsTy = tReg `BS.TAp` transType ty vecTy = tVector `BS.TAp` BS.cTNum numElems BS.NoPos `BS.TAp` bsTy numElems = toInteger $ length xs mkInitVal :: a -> Int -> BS.CStmt mkInitVal x elemNum = BS.CSBindT (BS.CPVar elemId) Nothing [] (BS.CQType [] bsTy) (BS.CApply (BS.CVar (BS.mkId BS.NoPos "mkReg")) [constTy ty x]) where elemName = streamElemName sId elemNum elemId = BS.mkId BS.NoPos $ fromString elemName -- | Make an index variable and initialise it to 0. mkIndexDecln :: Id -> BS.CStmt mkIndexDecln sId = -- sId_idx :: Reg (Bit 64) <- mkReg 0 BS.CSBindT (BS.CPVar nameId) Nothing [] (BS.CQType [] bsTy) (BS.CApply (BS.CVar (BS.mkId BS.NoPos "mkReg")) [cLit $ BS.LInt $ BS.ilDec 0]) where nameId = BS.mkId BS.NoPos $ fromString $ indexName sId bsTy = tReg `BS.TAp` BS.tBitN 64 BS.NoPos -- | Define an accessor function for the ring buffer associated with a stream mkAccessDecln :: Id -> Type a -> [a] -> BS.CDefl mkAccessDecln sId ty xs = -- sId_get :: Bits 64 -> ty -- sId_get x = (select sId ((sId_idx + x) % buffLength))._read BS.CLValueSign (BS.CDef nameId (BS.CQType [] funTy) [def]) [] where def = BS.CClause [BS.CPVar argId] [] expr argTy = BS.tBit `BS.TAp` BS.cTNum 64 BS.NoPos retTy = transType ty funTy = BS.tArrow `BS.TAp` argTy `BS.TAp` retTy name = streamAccessorName sId nameId = BS.mkId BS.NoPos $ fromString name buffLength = cLit $ BS.LInt $ BS.ilDec $ toInteger $ length xs argId = BS.mkId BS.NoPos "x" index = BS.CApply (BS.CVar (BS.idPercentAt BS.NoPos)) [ BS.CApply (BS.CVar BS.idPlus) [ BS.CVar (BS.mkId BS.NoPos (fromString (indexName sId))) , BS.CVar argId ] , buffLength ] indexExpr = cIndexVector (BS.CVar (BS.mkId BS.NoPos (fromString (streamName sId)))) index expr = BS.CSelect indexExpr (BS.id_read BS.NoPos) -- | Define fields for a module interface containing a specification's trigger -- functions and external variables. mkSpecIfcFields :: [Trigger] -> [External] -> [BS.CField] mkSpecIfcFields triggers exts = map mkTriggerField triggers ++ map mkExtField exts where -- trigger :: args_1 -> ... -> args_n -> Action mkTriggerField :: Trigger -> BS.CField mkTriggerField (Trigger name _ args) = mkField name $ foldr (\(UExpr arg _) res -> BS.tArrow `BS.TAp` transType arg `BS.TAp` res) BS.tAction args -- ext :: Reg ty mkExtField :: External -> BS.CField mkExtField (External name ty) = mkField name $ tReg `BS.TAp` transType ty -- | Define a rule for a trigger function. mkTriggerRule :: Trigger -> BS.CRule mkTriggerRule (Trigger name _ args) = BS.CRule [] (Just $ cLit $ BS.LString name) [ BS.CQFilter $ BS.CVar $ BS.mkId BS.NoPos $ fromString $ guardName name ] (BS.CApply nameExpr args') where ifcArgId = BS.mkId BS.NoPos $ fromString ifcArgName nameId = BS.mkId BS.NoPos $ fromString $ lowercaseName name nameExpr = BS.CSelect (BS.CVar ifcArgId) nameId args' = take (length args) (map argCall (argNames name)) argCall = BS.CVar . BS.mkId BS.NoPos . fromString -- | Writes the @step@ rule that updates all streams. mkStepRule :: [Stream] -> Maybe BS.CRule mkStepRule streams | null allUpdates = -- If there is nothing to update, don't bother creating a step rule. -- Doing so wouldn't harm anything, but bsc will generate a warning -- when compiling such an empty rule. Nothing | otherwise = Just $ BS.CRule [] (Just $ cLit $ BS.LString "step") [BS.CQFilter $ BS.CCon BS.idTrue []] (BS.Caction BS.NoPos allUpdates) where allUpdates = bufferUpdates ++ indexUpdates (bufferUpdates, indexUpdates) = unzip $ map mkUpdateGlobals streams -- Write code to update global stream buffers and index. mkUpdateGlobals :: Stream -> (BS.CStmt, BS.CStmt) mkUpdateGlobals (Stream sId buff _ _) = (bufferUpdate, indexUpdate) where bufferUpdate = BS.CSExpr Nothing $ BS.Cwrite BS.NoPos (cIndexVector (BS.CVar buffId) (BS.CVar indexId)) (BS.CVar genId) indexUpdate = BS.CSExpr Nothing $ BS.Cwrite BS.NoPos (BS.CVar indexId) (BS.CApply (BS.CVar (BS.idPercentAt BS.NoPos)) [incIndex, buffLength]) where buffLength = cLit $ BS.LInt $ BS.ilDec $ toInteger $ length buff incIndex = BS.CApply (BS.CVar BS.idPlus) [ BS.CVar indexId , cLit $ BS.LInt $ BS.ilDec 1 ] buffId = BS.mkId BS.NoPos $ fromString $ streamName sId genId = BS.mkId BS.NoPos $ fromString $ generatorName sId indexId = BS.mkId BS.NoPos $ fromString $ indexName sId -- | Write a struct declaration based on its definition. mkStructDecln :: Struct a => a -> BS.CDefn mkStructDecln x = BS.Cstruct True BS.SStruct (BS.IdK structId) [] -- No type variables structFields -- Derive a Bits instance so that we can put this struct in a Reg [BS.CTypeclass BS.idBits] where structId = BS.mkId BS.NoPos $ fromString $ uppercaseName $ typeName x structFields = map mkStructField $ toValues x mkStructField :: Value a -> BS.CField mkStructField (Value ty field) = mkField (fieldName field) (transType ty) -- | Write a field of a struct or interface, along with its type. mkField :: String -> BS.CType -> BS.CField mkField name ty = BS.CField { BS.cf_name = BS.mkId BS.NoPos $ fromString $ lowercaseName name , BS.cf_pragmas = Nothing , BS.cf_type = BS.CQType [] ty , BS.cf_default = [] , BS.cf_orig_type = Nothing } -- | The @Reg@ Bluespec interface type. tReg :: BS.CType tReg = BS.TCon $ BS.TyCon { BS.tcon_name = BS.idReg , BS.tcon_kind = Just (BS.Kfun BS.KStar BS.KStar) , BS.tcon_sort = BS.TIstruct (BS.SInterface []) [BS.id_write BS.NoPos, BS.id_read BS.NoPos] }