clashilator (empty) → 0.1.0
raw patch · 12 files changed
+756/−0 lines, 12 filesdep +Cabaldep +aesondep +base
Dependencies added: Cabal, aeson, base, clash-ghc, clash-lib, containers, filepath, ghc, lens, optparse-applicative, shake, stache, text, unordered-containers
Files
- LICENSE +21/−0
- README.md +64/−0
- clashilator.cabal +85/−0
- src/Clash/Clashilator.hs +114/−0
- src/Clash/Clashilator/Cabal.hs +79/−0
- src/Clash/Clashilator/Setup.hs +151/−0
- src/main.hs +50/−0
- template/FFI.hsc.mustache +70/−0
- template/Impl.cpp.mustache +43/−0
- template/Impl.h.mustache +11/−0
- template/Interface.h.mustache +19/−0
- template/Makefile.mustache +49/−0
+ LICENSE view
@@ -0,0 +1,21 @@+MIT License++Copyright (c) 2020 Gergő Érdi (http://gergo.erdi.hu/)++Permission is hereby granted, free of charge, to any person obtaining a copy+of this software and associated documentation files (the "Software"), to deal+in the Software without restriction, including without limitation the rights+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell+copies of the Software, and to permit persons to whom the Software is+furnished to do so, subject to the following conditions:++The above copyright notice and this permission notice shall be included in all+copies or substantial portions of the Software.++THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE+SOFTWARE.
+ README.md view
@@ -0,0 +1,64 @@+# Clashilator: Automated Clash - Verilator integration++This package provides Cabal `Setup.hs` functionality to automatically+integrate Verilator into your Clash project.++* Detailed introduction: https://unsafeperform.io/blog/2020-05-07-integrating_verilator_and_clash_via_cabal/+* Example project: https://github.com/gergoerdi/clashilator-example++## Usage++Suppose you have a Clash circuit that you want to simulate using+Verilator, and then write Haskell code to interact with that+simulation. If your Clash code looks like this:++```+topEntity+ :: "CLK" ::: Clock System+ -> "FOO" ::: Signal System Bit+ -> "BAR" ::: Signal System (Unsigned 4)+ -> ("BAZ" ::: Signal System (Unsigned 10), "QUUX" ::: Signal System Bit)+topEntity = ...+makeTopEntity 'topEntity+```++and you put this in your Cabal file:++```+custom-setup+ setup-depends: clashilator++executable MySim+ main-is: simulator.hs+ x-clashilator-clock: CLK+ x-clashilator-top-is: MyCircuit+```++then in your `simulator.hs`, you can import the "virtual" module+`Clash.Clashilator.FFI` which provides the following definitions:++```+data INPUT = INPUT+ { iFOO :: Bit+ , iBAR :: Word8+ }+ deriving (Show)+instance Storable INPUT++data OUTPUT = OUTPUT+ { oBAZ :: Word16+ , oQUUX :: Bit+ }+ deriving (Show)+instance Storable OUTPUT++data Sim++simInit :: IO (Ptr Sim)+simShutdown :: Ptr Sim -> IO ()+simStep :: Ptr Sim -> Ptr INPUT -> Ptr OUTPUT -> IO ()+```++Note that input and output buses are represented as the smallest+possible `Word` type, to improve marshalling cost when crossing the+Haskell-C++ barrier.
+ clashilator.cabal view
@@ -0,0 +1,85 @@+cabal-version: 1.12++-- This file has been generated from package.yaml by hpack version 0.34.4.+--+-- see: https://github.com/sol/hpack+--+-- hash: 2a7cd4d926c1778ae4d73a15781b9025b1afc514077e52df91e0841f7543c1f8++name: clashilator+version: 0.1.0+synopsis: Automated Clash to Verilator bridge+description: Code generator and @Setup.hs@ hooks to generate a Verilator simulation and access it from Clash+category: Hardware, Development+homepage: https://github.com/gergoerdi/clashilator#readme+bug-reports: https://github.com/gergoerdi/clashilator/issues+author: Gergő Érdi+maintainer: gergo@erdi.hu+license: MIT+license-file: LICENSE+build-type: Simple+extra-source-files:+ README.md+ template/Makefile.mustache+ template/Interface.h.mustache+ template/Impl.cpp.mustache+ template/Impl.h.mustache+ template/FFI.hsc.mustache++source-repository head+ type: git+ location: https://github.com/gergoerdi/clashilator++library+ exposed-modules:+ Clash.Clashilator+ Clash.Clashilator.Setup+ other-modules:+ Clash.Clashilator.Cabal+ Paths_clashilator+ hs-source-dirs:+ src+ ghc-options: -Wunused-imports+ build-depends:+ Cabal >=3.2.1.0+ , aeson+ , base >=4.14 && <5+ , clash-ghc >=1.4.2 && <1.5+ , clash-lib >=1.4.2 && <1.5+ , containers+ , filepath+ , ghc+ , lens+ , optparse-applicative+ , shake+ , stache+ , text+ , unordered-containers+ default-language: Haskell2010++executable clashilator+ main-is: main.hs+ other-modules:+ Clash.Clashilator+ Clash.Clashilator.Cabal+ Clash.Clashilator.Setup+ Paths_clashilator+ hs-source-dirs:+ src+ ghc-options: -Wunused-imports+ build-depends:+ Cabal >=3.2.1.0+ , aeson+ , base >=4.14 && <5+ , clash-ghc >=1.4.2 && <1.5+ , clash-lib >=1.4.2 && <1.5+ , containers+ , filepath+ , ghc+ , lens+ , optparse-applicative+ , shake+ , stache+ , text+ , unordered-containers+ default-language: Haskell2010
+ src/Clash/Clashilator.hs view
@@ -0,0 +1,114 @@+{-# LANGUAGE RecordWildCards, OverloadedStrings, ViewPatterns #-}+{-# LANGUAGE TemplateHaskell #-}+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)+import qualified Data.HashMap.Strict as H++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+ (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 <> H.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
+ src/Clash/Clashilator/Cabal.hs view
@@ -0,0 +1,79 @@+{-# LANGUAGE GADTs, RankNTypes #-}+module Clash.Clashilator.Cabal+ ( BuildHook+ , ComponentHook+ , withComponentHook+ , packageDBs+ ) where++import Distribution.Simple+import Distribution.Simple.LocalBuildInfo+import Distribution.Simple.BuildTarget+import Distribution.Simple.Setup+import Distribution.Simple.Register+import Distribution.Types.ComponentRequestedSpec++import Distribution.Types.Lens+import Control.Lens hiding ((<.>))+import Control.Monad (unless, when)++type BuildHook = PackageDescription -> LocalBuildInfo -> UserHooks -> BuildFlags -> IO ()+type ComponentHook = LocalBuildInfo -> BuildFlags -> Component -> IO BuildInfo++withComponentHook :: ComponentHook -> BuildHook -> BuildHook+withComponentHook componentHook nextBuildHook pkg lbi userHooks flags = do+ let reqSpec = componentEnabledSpec lbi+ withAllComponentsInBuildOrder pkg lbi $ \c clbi -> do+ flags <- return $ restrictBuildFlags pkg c flags+ when (componentEnabled reqSpec c && not (null $ buildArgs flags)) $ do+ bi <- componentHook lbi flags c+ pkg <- return $ updateBuildInfo c bi pkg+ nextBuildHook pkg lbi userHooks flags++packageDBs :: LocalBuildInfo -> BuildFlags -> IO [PackageDB]+packageDBs lbi flags = do+ pkgdb0 <- do+ let dbPath = internalPackageDBPath lbi distPref+ existsAlready <- doesPackageDBExist dbPath+ unless existsAlready $ do+ createPackageDB verbosity (compiler lbi) (withPrograms lbi) False dbPath+ return $ SpecificPackageDB dbPath+ pkgdbs <- absolutePackageDBPaths $ withPackageDB lbi++ return $ pkgdb0 : pkgdbs+ where+ verbosity = fromFlag (buildVerbosity flags)+ distPref = fromFlag (buildDistPref flags)++data NamedComponent where+ NamedComponent+ :: (HasBuildInfo a)+ => Traversal' PackageDescription a+ -> (a -> ComponentName)+ -> NamedComponent++namedComponents :: [NamedComponent]+namedComponents =+ [ NamedComponent (library . each) (CLibName . view libName)+ , NamedComponent (subLibraries . each) (CLibName . view libName)+ , NamedComponent (executables . each) (CExeName . view exeName)+ , NamedComponent (testSuites . each) (CTestName . view testName)+ , NamedComponent (benchmarks . each) (CBenchName . view benchmarkName)+ ]++itagged :: Traversal' s a -> (a -> b) -> IndexedTraversal' b s a+itagged l f = reindexed f (l . selfIndex)++updateBuildInfo :: Component -> BuildInfo -> PackageDescription -> PackageDescription+updateBuildInfo c bi pkg = foldr ($) pkg $+ [ iover focus $ \ name -> if name == componentName c then const bi else id+ | NamedComponent component getName <- namedComponents+ , let focus = itagged component getName <. buildInfo+ ]++restrictBuildFlags :: PackageDescription -> Component -> BuildFlags -> BuildFlags+restrictBuildFlags pkg c buildFlags = buildFlags+ { buildArgs = selectedArgs+ }+ where+ selectedArgs = [showBuildTarget (packageId pkg) $ BuildTargetComponent $ componentName c]
+ src/Clash/Clashilator/Setup.hs view
@@ -0,0 +1,151 @@+{-# LANGUAGE CPP, GADTs, RankNTypes, FlexibleContexts #-}+module Clash.Clashilator.Setup+ ( clashToVerilog+ , buildVerilator+ , clashilate++ , clashilatorMain+ , clashilatorBuildHook+ ) where++import qualified Clash.Main as Clash+import qualified Clash.Clashilator as Clashilator+import Clash.Clashilator.Cabal+import Clash.Driver.Manifest (Manifest, readManifest)++import Distribution.Simple+import Distribution.Simple.LocalBuildInfo+import Distribution.Simple.Setup+import Distribution.Simple.Program+import Distribution.Simple.Utils (infoNoWrap)+import Distribution.Verbosity+import Distribution.ModuleName+import Distribution.Types.UnqualComponentName++import Distribution.Types.Lens+import Control.Lens hiding ((<.>))+import Data.List (intercalate, sort, nub)+import Data.Maybe (fromMaybe)+import System.FilePath+import GHC (Ghc)+#if MIN_VERSION_ghc(8,10,0)+import GHC (getSession, setSession)+import HscTypes (HscEnv (..))+import Linker+#endif++lookupX :: String -> BuildInfo -> Maybe String+lookupX key buildInfo = lookup ("x-clashilator-" <> key) (view customFieldsBI buildInfo)++clashilatorBuildHook :: BuildHook+clashilatorBuildHook = withComponentHook clashilate $ buildHook simpleUserHooks++clashToVerilog :: Ghc () -> LocalBuildInfo -> BuildFlags -> [FilePath] -> BuildInfo -> ModuleName -> String -> FilePath -> IO (FilePath, Manifest)+clashToVerilog startAction lbi flags srcDirs buildInfo mod entity outDir = do+ pkgdbs <- packageDBs lbi flags+ let dbpaths = nub . sort $ [ path | SpecificPackageDB path <- pkgdbs ]+ dbflags = concat [ ["-package-db", path] | path <- dbpaths ]+ iflags = [ "-i" <> dir | dir <- srcDirs ]+ clashflags = maybe [] words $ lookupX "clash-flags" buildInfo++ let args = concat+ [ [ "--verilog"+ , "-outputdir", outDir+ , "-main-is", entity+ , intercalate "." (components mod)+ ]+ , iflags+ , dbflags+ , clashflags+ ]+ infoNoWrap verbosity $ unwords $ "Clash.defaultMain" : args+ Clash.defaultMainWithAction startAction args++ let modDir = intercalate "." (components mod)+ verilogDir = outDir </> modDir <.> entity+ Just manifest <- readManifest (verilogDir </> "clash-manifest.json")++ return (verilogDir, manifest)+ where+ verbosity = fromFlagOrDefault normal (buildVerbosity flags)++buildVerilator :: Ghc () -> LocalBuildInfo -> BuildFlags -> Maybe UnqualComponentName -> BuildInfo -> IO BuildInfo+buildVerilator startAction lbi flags compName buildInfo = case top of+ Nothing -> return buildInfo+ Just mod -> buildVerilator' startAction lbi flags compName buildInfo (fromString mod) entity+ where+ top = lookupX "top-is" buildInfo+ entity = fromMaybe "topEntity" $ lookupX "entity" buildInfo++buildVerilator' :: Ghc () -> LocalBuildInfo -> BuildFlags -> Maybe UnqualComponentName -> BuildInfo -> ModuleName -> String -> IO BuildInfo+buildVerilator' startAction lbi flags compName buildInfo mod entity = do+ cflags <- do+ mpkgConfig <- needProgram verbosity pkgConfigProgram (withPrograms lbi)+ case mpkgConfig of+ Nothing -> error "Cannot find pkg-config program"+ Just (pkgConfig, _) -> getProgramOutput verbosity pkgConfig ["--cflags", "verilator"]++ -- TODO: dependency tracking+ (verilogDir, manifest) <- clashToVerilog startAction lbi flags srcDirs buildInfo mod entity synDir+ Clashilator.generateFiles (Just cflags) verilogDir verilatorDir (fromString <$> clk) manifest++ -- TODO: get `make` location from configuration+ _ <- getProgramInvocationOutput verbosity $+ simpleProgramInvocation "make" ["-f", verilatorDir </> "Makefile"]++ let incDir = verilatorDir </> "src"+ libDir = verilatorDir </> "obj"+ lib = "VerilatorFFI"++ let fixupOptions f (PerCompilerFlavor x y) = PerCompilerFlavor (f x) (f y)++ compileFlags =+ [ "-fPIC"+ , "-pgml c++"+ ]++ ldFlags =+ [ "-Wl,--whole-archive"+ , "-Wl,-Bstatic"+ , "-Wl,-l" <> lib+ , "-Wl,-Bdynamic"+ , "-Wl,--no-whole-archive"+ ]++ return $ buildInfo+ & includeDirs %~ (incDir:)+ & extraLibDirs %~ (libDir:)+ & extraLibs %~ ("stdc++":)+ & options %~ fixupOptions (compileFlags++)+ & ldOptions %~ (ldFlags++)+ & hsSourceDirs %~ (incDir:)+ & otherModules %~ (fromComponents ["Clash", "Clashilator", "FFI"]:)+ where+ verbosity = fromFlagOrDefault normal (buildVerbosity flags)++ clk = lookup "x-clashilator-clock" $ view customFieldsBI buildInfo++ -- TODO: Maybe we could add extra source dirs from "x-clashilator-source-dirs"?+ srcDirs = view hsSourceDirs buildInfo+ outDir = case compName of+ Nothing -> buildDir lbi+ Just name -> buildDir lbi </> unUnqualComponentName name+ verilatorDir = outDir </> "_clashilator" </> "verilator"+ synDir = outDir </> "_clashilator" </> "clash-syn"++clashilate :: LocalBuildInfo -> BuildFlags -> Component -> IO BuildInfo+clashilate lbi flags c = do+#if MIN_VERSION_ghc(8,10,0)+ linker <- uninitializedLinker+ let startAction = do+ env <- getSession+ setSession (env {hsc_dynLinker = linker})+#else+ let startAction = return ()+#endif+ buildVerilator startAction lbi flags (componentNameString $ componentName c) (c ^. buildInfo)++clashilatorMain :: IO ()+clashilatorMain = defaultMainWithHooks simpleUserHooks+ { buildHook = clashilatorBuildHook+ }
+ src/main.hs view
@@ -0,0 +1,50 @@+{-# LANGUAGE RecordWildCards, ApplicativeDo #-}++import Clash.Driver.Manifest+import Clash.Clashilator+import System.FilePath+import Options.Applicative+import Data.String (fromString)++data Options = Options+ { manifestPath :: FilePath+ , outputDir :: FilePath+ , clkName :: Maybe String+ }++options :: Parser Options+options = do+ manifestPath <- strOption $ mconcat+ [ long "input"+ , short 'i'+ , metavar "FILENAME"+ , help "Clash manifest file"+ ]+ outputDir <- strOption $ mconcat+ [ long "output"+ , short 'o'+ , metavar "DIRECTORY"+ , help "Where to put generated files"+ ]+ clkName <- optional $ strOption $ mconcat+ [ long "clock"+ , short 'c'+ , metavar "NAME"+ , help "Clock signal name"+ ]+ pure Options{..}++optionsInfo = info (options <**> helper) $ mconcat+ [ fullDesc+ , header "Clashilator - Clash <-> Verilator interface"+ , progDesc "Generate Verilator source files and FFI interface from Clash manifest"+ ]++main :: IO ()+main = do+ Options{..} <- execParser optionsInfo++ Just manifest <- readManifest manifestPath+ let inputDir = takeDirectory manifestPath++ generateFiles Nothing inputDir outputDir (fromString <$> clkName) manifest
+ template/FFI.hsc.mustache view
@@ -0,0 +1,70 @@+{-# LANGUAGE RecordWildCards, ForeignFunctionInterface #-}+module Clash.Clashilator.FFI where++import Prelude+import Clash.Prelude++import Data.Word+import Data.Int+import Foreign.Storable+import Foreign.Ptr+import Foreign.Marshal.Alloc++data INPUT = INPUT {+{{#inPorts}}+ {{^first}}, {{/first}}{{hsName}} :: {{hsType}}+{{/inPorts}}+ }+ deriving (Show)++data OUTPUT = OUTPUT{+{{#outPorts}}+ {{^first}}, {{/first}}{{hsName}} :: {{hsType}}+{{/outPorts}}+ }+ deriving (Show)+++#include "Interface.h"++data Sim++foreign import ccall unsafe "vinit" simInit :: IO (Ptr Sim)+foreign import ccall unsafe "vshutdown" simShutdown :: Ptr Sim -> IO ()+foreign import ccall unsafe "vstep" simStep :: Ptr Sim -> Ptr INPUT -> Ptr OUTPUT -> IO ()++instance Storable Bit where+ alignment = alignment . bitToBool+ sizeOf = sizeOf . bitToBool+ peek = fmap boolToBit . peek . castPtr+ poke ptr = poke (castPtr ptr) . bitToBool++instance Storable INPUT where+ alignment _ = #alignment INPUT+ sizeOf _ = #size INPUT+ {-# INLINE peek #-}+ peek ptr = const INPUT <$> pure ()+{{#inPorts}}+ <*> (#peek INPUT, {{cName}}) ptr+{{/inPorts}}+ {-# INLINE poke #-}+ poke ptr this = do+{{#inPorts}}+ (#poke INPUT, {{cName}}) ptr ({{hsName}} this)+{{/inPorts}}+ return ()++instance Storable OUTPUT where+ alignment _ = #alignment OUTPUT+ sizeOf _ = #size OUTPUT+ {-# INLINE peek #-}+ peek ptr = const OUTPUT <$> pure ()+{{#outPorts}}+ <*> (#peek OUTPUT, {{cName}}) ptr+{{/outPorts}}+ {-# INLINE poke #-}+ poke ptr this = do+{{#outPorts}}+ (#poke OUTPUT, {{cName}}) ptr ({{hsName}} this)+{{/outPorts}}+ return ()
+ template/Impl.cpp.mustache view
@@ -0,0 +1,43 @@+#include "VSim.h"+#include "verilated.h"+#include "Impl.h"++vluint64_t main_time = 0;++double sc_time_stamp ()+{+ return main_time;+}++VSim* vinit()+{+ // Verilated::commandArgs(0, 0);+ return new VSim();+}++void vshutdown(VSim *top)+{+ delete top;+}++void vstep(VSim* top, const INPUT* input, OUTPUT* output)+{+ {{#inPorts}}+ top->{{cName}} = input->{{cName}};+ {{/inPorts}}++ {{#clock}}+ top->{{cName}} = true;+ {{/clock}}+ top->eval();+ ++main_time;+ {{#clock}}+ top->{{cName}} = false;+ top->eval();+ ++main_time;+ {{/clock}}++ {{#outPorts}}+ output->{{cName}} = top->{{cName}};+ {{/outPorts}}+}
+ template/Impl.h.mustache view
@@ -0,0 +1,11 @@+#pragma once++#include "VSim.h"+#include "verilated.h"+#include "Interface.h"++extern "C" {+ VSim* vinit();+ void vstep(VSim* top, const INPUT* input, OUTPUT* output);+ void vshutdown(VSim* top);+}
+ template/Interface.h.mustache view
@@ -0,0 +1,19 @@+#pragma once++#include <stdint.h>++typedef int bit;++typedef struct+{+{{#inPorts}}+ {{cType}} {{cName}};+{{/inPorts}}+} INPUT;++typedef struct+{+{{#outPorts}}+ {{cType}} {{cName}};+{{/outPorts}}+} OUTPUT;
+ template/Makefile.mustache view
@@ -0,0 +1,49 @@+VERILOG_SRCS = {{#srcs}}{{verilogPath}} {{/srcs}}++HDL_DIR = {{hdlDir}}+OUT_DIR = {{outputDir}}/obj+VERILATOR_HDRS = $(OUT_DIR)/VSim.h+VERILATOR_LIB = $(OUT_DIR)/VSim__ALL.a+VERILATOR_OBJS = $(OUT_DIR)/verilated.o++SRC_DIR = {{outputDir}}/src+SRCS = $(foreach file, Impl.cpp Impl.h Interface.h, $(SRC_DIR)/$(file))++{{#verilator}}+CPPFLAGS = {{cflags}}+{{/verilator}}+{{^verilator}}+CPPFLAGS = $(shell pkg-config --cflags verilator)+{{/verilator}}+CPPFLAGS += -I$(OUT_DIR)+CXXFLAGS = -fPIC -O3+VERILATOR_FLAGS = -CFLAGS '-O3 -fPIC' -Wno-fatal --prefix VSim +1364-2001ext+v -I$(HDL_DIR)++all: $(OUT_DIR)/libVerilatorFFI.a++clean:+ rm -rf $(OUT_DIR)++.PHONY: all clean++$(OUT_DIR)/VSim.mk: $(VERILOG_SRCS)+ verilator $(VERILATOR_FLAGS) --cc -Mdir $(OUT_DIR) $(VERILOG_SRCS)++$(OUT_DIR)/verilated.o: $(shell pkg-config --variable=includedir verilator)/verilated.cpp+ $(COMPILE.cc) $(OUTPUT_OPTION) $<++$(VERILATOR_LIB) $(VERILATOR_HDRS): $(OUT_DIR)/VSim.mk + cd $(OUT_DIR) && make -f VSim.mk CXX=$(CXX)+ +$(OUT_DIR)/VerilatorFFI.o: $(SRCS) $(VERILATOR_HDRS)+ $(COMPILE.cc) $(OUTPUT_OPTION) $<++$(OUT_DIR)/libVerilatorFFI.a: $(OUT_DIR)/VerilatorFFI.o $(VERILATOR_LIB) $(VERILATOR_OBJS)+ rm -f $@+ mkdir -p $(OUT_DIR)+ ar rcsT $@ $^++$(OUT_DIR)/%.o : %.cpp+ mkdir -p $(OUT_DIR)+ $(COMPILE.cc) $(OUTPUT_OPTION) $<+