copilot-bluespec-4.2: src/Copilot/Compile/Bluespec/CodeGen.hs
{-# 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.Representation
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 <ty> <- mkReg xs_0
-- ...
-- sId_(n-1) :: Reg <ty> <- mkReg xs_(n-1)
initVals = zipWith mkInitVal xs [0..]
-- sId :: Vector n (Reg <ty>)
-- 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 :: UniqueTrigger -> BS.CRule
mkTriggerRule (UniqueTrigger uniqueName (Trigger name _ args)) =
BS.CRule
[]
(Just $ cLit $ BS.LString uniqueName)
[ BS.CQFilter $
BS.CVar $ BS.mkId BS.NoPos $
fromString $ guardName uniqueName
]
(BS.CApply nameExpr args')
where
ifcArgId = BS.mkId BS.NoPos $ fromString ifcArgName
-- Note that we use 'name' here instead of 'uniqueName', as 'name' is the
-- name of the actual external function.
nameId = BS.mkId BS.NoPos $ fromString $ lowercaseName name
nameExpr = BS.CSelect (BS.CVar ifcArgId) nameId
args' = take (length args) (map argCall (argNames uniqueName))
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]
}