clashilator-0.1.2: src/Clash/Clashilator.hs
{-# LANGUAGE RecordWildCards, OverloadedStrings, ViewPatterns #-}
{-# LANGUAGE TemplateHaskell, CPP #-}
module Clash.Clashilator (generateFiles) where
import Clash.Driver.Manifest
import Control.Monad (forM_)
import Data.List (partition)
import System.FilePath
import Data.Text (Text)
import qualified Data.Text as T
import qualified Data.Text.Lazy as TL
import Development.Shake (writeFileChanged)
import Text.Mustache
import qualified Text.Mustache.Compile.TH as TH
import Data.Aeson hiding (Options)
#if MIN_VERSION_aeson(2, 0, 0)
import qualified Data.Aeson.KeyMap as Aeson
#else
import qualified Data.HashMap.Strict as Aeson
#endif
data Port = Port Text Int
deriving Show
data FFIType
= FFIBit
| FFIU8
| FFIU16
| FFIU32
| FFIU64
ffiType :: Int -> FFIType
ffiType 1 = FFIBit
ffiType n
| n <= 8 = FFIU8
| n <= 16 = FFIU16
| n <= 32 = FFIU32
| n <= 64 = FFIU64
| otherwise = error $ unwords ["ffiType:", show n]
cType :: FFIType -> Text
cType FFIBit = "bit"
cType FFIU8 = "uint8_t"
cType FFIU16 = "uint16_t"
cType FFIU32 = "uint32_t"
cType FFIU64 = "uint64_t"
hsType :: FFIType -> Text
hsType FFIBit = "Bit"
hsType FFIU8 = "Word8"
hsType FFIU16 = "Word16"
hsType FFIU32 = "Word32"
hsType FFIU64 = "Word64"
cName :: Text -> Text
cName = id
hsName :: Text -> Text -> Text
hsName tag s = tag <> s
getClockAndInPorts :: Maybe Text -> [ManifestPort] -> (Maybe Text, [Port])
getClockAndInPorts clkName inPorts = (clk, [ Port mpName mpWidth | ManifestPort{..} <- inPorts' ])
where
(map mpName -> clks, inPorts') = partition mpIsClock inPorts
clk = case (clkName, clks) of
(Just clkName, clks) | clkName `elem` clks -> Just clkName
(Nothing, [clk]) -> Just clk
_ -> Nothing
portInfo :: Text -> Port -> Value
portInfo tag (Port name width) = object
[ "cName" .= cName name
, "cType" .= cType ty
, "hsName" .= hsName tag name
, "hsType" .= hsType ty
]
where
ty = ffiType width
manifestInfo :: Maybe String -> FilePath -> FilePath -> Maybe Text -> Manifest -> Value
manifestInfo cflags srcDir outputDir clkName Manifest{..} = object
[ "inPorts" .= (markEnds $ map (portInfo "i") ins)
, "outPorts" .= (markEnds $ map (portInfo "o") outs)
, "clock" .= fmap (\clock -> object ["cName" .= cName clock]) clock
, "hdlDir" .= srcDir
, "srcs" .= [ object ["verilogPath" .= TL.pack (srcDir </> T.unpack component <.> "v")]
| component <- componentNames
]
, "verilator" .= fmap (\cflags -> object [ "cflags" .= TL.strip (TL.pack cflags) ]) cflags
, "outputDir" .= outputDir
]
where
#if MIN_VERSION_clash_lib(1, 6, 0)
inPorts = [ port | port@ManifestPort{..} <- ports, mpDirection `elem` [In, InOut] ]
outPorts = [ port | port@ManifestPort{..} <- ports, mpDirection `elem` [InOut, Out] ]
#endif
(clock, ins) = getClockAndInPorts clkName inPorts
outs = [ Port mpName mpWidth | ManifestPort{..} <- outPorts ]
markEnds :: [Value] -> [Value]
markEnds [] = []
markEnds (v:vs) = markStart v : vs
where
markStart (Object o) = Object $ o <> Aeson.fromList [ "first" .= True ]
templates =
[ ("src/Interface.h", $(TH.compileMustacheFile "template/Interface.h.mustache"))
, ("src/Impl.cpp", $(TH.compileMustacheFile "template/Impl.cpp.mustache"))
, ("src/Impl.h", $(TH.compileMustacheFile "template/Impl.h.mustache"))
, ("Makefile", $(TH.compileMustacheFile "template/Makefile.mustache"))
, ("src/Clash/Clashilator/FFI.hsc", $(TH.compileMustacheFile "template/FFI.hsc.mustache"))
]
generateFiles :: Maybe String -> FilePath -> FilePath -> Maybe Text -> Manifest -> IO ()
generateFiles cflags inputDir outputDir clkName manifest = do
let vals = manifestInfo cflags inputDir outputDir clkName manifest
forM_ templates $ \(fname, template) -> do
writeFileChanged (outputDir </> fname) $ TL.unpack $ renderMustache template vals