diff --git a/clash-vexriscv-sim/src/Utils/Cpu.hs b/clash-vexriscv-sim/src/Utils/Cpu.hs index 325492c..71da334 100644 --- a/clash-vexriscv-sim/src/Utils/Cpu.hs +++ b/clash-vexriscv-sim/src/Utils/Cpu.hs @@ -61,8 +61,8 @@ cpu bootIMem bootDMem = (output, writes, iS2M, dS2M) { timerInterrupt = low, externalInterrupt = low, softwareInterrupt = low, - iBusWbS2M = makeDefined iBus, - dBusWbS2M = makeDefined dBus + iBusWbS2M = iBus, + dBusWbS2M = dBus } ) <$> iS2M diff --git a/clash-vexriscv/clash-vexriscv.cabal b/clash-vexriscv/clash-vexriscv.cabal index d761071..3c01433 100644 --- a/clash-vexriscv/clash-vexriscv.cabal +++ b/clash-vexriscv/clash-vexriscv.cabal @@ -102,24 +102,36 @@ library default-language: Haskell2010 exposed-modules: VexRiscv + VexRiscv.BlackBox VexRiscv.ClockTicks VexRiscv.FFI VexRiscv.TH VexRiscv.VecToTuple + -- See https://github.com/clash-lang/clash-compiler/pull/2511 + if impl(ghc >= 9.4) + CPP-Options: -DCLASH_OPAQUE=OPAQUE + else + CPP-Options: -DCLASH_OPAQUE=NOINLINE + build-depends: base, bytestring >= 0.10 && < 0.13, + clash-lib, clash-prelude, clash-protocols, containers, directory >= 1.3 && < 1.4, filepath, Glob, + infinite-list, + mtl, + pretty-show, process >= 1.6 && < 1.8, string-interpolate, tagged, template-haskell, + text, extra-libraries: VexRiscvFFI, stdc++ include-dirs: src/ diff --git a/clash-vexriscv/src/VexRiscv.hs b/clash-vexriscv/src/VexRiscv.hs index 16c2963..ed7ba37 100644 --- a/clash-vexriscv/src/VexRiscv.hs +++ b/clash-vexriscv/src/VexRiscv.hs @@ -2,11 +2,12 @@ -- -- SPDX-License-Identifier: Apache-2.0 +{-# LANGUAGE CPP #-} +{-# LANGUAGE MagicHash #-} {-# LANGUAGE NamedFieldPuns #-} +{-# LANGUAGE QuasiQuotes #-} {-# LANGUAGE RecordWildCards #-} -{-# LANGUAGE MagicHash #-} {-# LANGUAGE TemplateHaskellQuotes #-} -{-# LANGUAGE QuasiQuotes #-} {-# OPTIONS_GHC -fconstraint-solver-iterations=10 #-} @@ -27,10 +28,10 @@ import GHC.Stack (HasCallStack) import Language.Haskell.TH.Syntax import Protocols.Wishbone +import VexRiscv.BlackBox (vexRiscvBBF) import VexRiscv.ClockTicks import VexRiscv.FFI import VexRiscv.TH -import VexRiscv.VecToTuple import qualified VexRiscv.FFI as FFI @@ -43,19 +44,67 @@ data Input = Input } deriving (Generic, NFDataX, ShowX, Eq, BitPack) +inputToNonCombInput :: Bool -> Input -> NON_COMB_INPUT +inputToNonCombInput reset Input{..} = NON_COMB_INPUT + { FFI.reset = boolToBit reset + , FFI.timerInterrupt = timerInterrupt + , FFI.externalInterrupt = externalInterrupt + , FFI.softwareInterrupt = softwareInterrupt + } + +inputToCombInput :: Input -> COMB_INPUT +inputToCombInput Input{iBusWbS2M, dBusWbS2M} = makeDefined $ COMB_INPUT + { FFI.iBusWishbone_ACK = boolToBit (acknowledge iBusWbS2M) + , FFI.iBusWishbone_DAT_MISO = unpack (readData iBusWbS2M) + , FFI.iBusWishbone_ERR = boolToBit (err iBusWbS2M) + + , FFI.dBusWishbone_ACK = boolToBit (acknowledge dBusWbS2M) + , FFI.dBusWishbone_DAT_MISO = unpack (readData dBusWbS2M) + , FFI.dBusWishbone_ERR = boolToBit (err dBusWbS2M) + } + data Output = Output { iBusWbM2S :: "IBUS_OUT_" ::: WishboneM2S 30 4 (BitVector 32) , dBusWbM2S :: "DBUS_OUT_" ::: WishboneM2S 30 4 (BitVector 32) } deriving (Generic, NFDataX, ShowX, Eq, BitPack) +outputToOutput :: OUTPUT -> Output +outputToOutput OUTPUT{..} = Output + { iBusWbM2S = WishboneM2S + { addr = truncateB (pack iBusWishbone_ADR) + , writeData = pack (iBusWishbone_DAT_MOSI) + , busSelect = unpack (truncateB (pack iBusWishbone_SEL)) + , lock = False + , busCycle = bitToBool iBusWishbone_CYC + , strobe = bitToBool iBusWishbone_STB + , writeEnable = bitToBool iBusWishbone_WE + , cycleTypeIdentifier = unpack (truncateB (pack iBusWishbone_CTI)) + , burstTypeExtension = unpack (truncateB (pack iBusWishbone_BTE)) + } + , dBusWbM2S = WishboneM2S + { addr = truncateB (pack dBusWishbone_ADR) + , writeData = pack (dBusWishbone_DAT_MOSI) + , busSelect = unpack (truncateB (pack dBusWishbone_SEL)) + , lock = False + , busCycle = bitToBool dBusWishbone_CYC + , strobe = bitToBool dBusWishbone_STB + , writeEnable = bitToBool dBusWishbone_WE + , cycleTypeIdentifier = unpack (truncateB (pack dBusWishbone_CTI)) + , burstTypeExtension = unpack (truncateB (pack dBusWishbone_BTE)) + } + } + -- When passing S2M values from Haskell to VexRiscv over the FFI, undefined -- bits/values cause errors when forcing their evaluation to something that can -- be passed through the FFI. -- -- This function makes sure the Wishbone S2M values are free from undefined bits. -makeDefined :: WishboneS2M (BitVector 32) -> WishboneS2M (BitVector 32) -makeDefined wb = wb {readData = defaultX 0 (readData wb)} +makeDefined :: COMB_INPUT -> COMB_INPUT +makeDefined ci@COMB_INPUT{iBusWishbone_DAT_MISO, dBusWishbone_DAT_MISO} = ci + { FFI.iBusWishbone_DAT_MISO = defaultX 0 iBusWishbone_DAT_MISO + , FFI.dBusWishbone_DAT_MISO = defaultX 0 dBusWishbone_DAT_MISO + } defaultX :: (NFDataX a) => a -> a -> a defaultX dflt val @@ -63,356 +112,71 @@ defaultX dflt val | otherwise = val vexRiscv :: (HasCallStack, HiddenClockResetEnable dom) => Signal dom Input -> Signal dom Output -vexRiscv input = - Output <$> - (WishboneM2S - <$> iBus_ADR - <*> iBus_DAT_MOSI - <*> iBus_SEL - <*> pure False - <*> iBus_CYC - <*> iBus_STB - <*> iBus_WE - <*> (unpack <$> iBus_CTI) - <*> (unpack <$> iBus_BTE) - ) - <*> - (WishboneM2S - <$> dBus_ADR - <*> dBus_DAT_MOSI - <*> dBus_SEL - <*> pure False - <*> dBus_CYC - <*> dBus_STB - <*> dBus_WE - <*> (unpack <$> dBus_CTI) - <*> (unpack <$> dBus_BTE) - ) - +vexRiscv = vexRiscv# sourcePath hasClock hasReset where - (unbundle -> (timerInterrupt, externalInterrupt, softwareInterrupt, iBusS2M, dBusS2M)) - = (\(Input a b c d e) -> (a, b, c, d, e)) <$> input - - (unbundle -> (iBus_DAT_MISO, iBus_ACK, iBus_ERR)) - = (\(WishboneS2M a b c _ _) -> (a, b, c)) - -- A hack that enables us to both generate synthesizable HDL and simulate vexRisc in Haskell/Clash - . (if clashSimulation then makeDefined else id) - <$> iBusS2M - - (unbundle -> (dBus_DAT_MISO, dBus_ACK, dBus_ERR)) - = (\(WishboneS2M a b c _ _) -> (a, b, c)) - -- A hack that enables us to both generate synthesizable HDL and simulate vexRisc in Haskell/Clash - . (if clashSimulation then makeDefined else id) - <$> dBusS2M - sourcePath = $(do - cpuSrcPath <- runIO $ getPackageRelFilePath "example-cpu/VexRiscv.v" - pure $ LitE $ StringL cpuSrcPath - ) - - ( iBus_CYC - , iBus_STB - , iBus_WE - , iBus_ADR - , iBus_DAT_MOSI - , iBus_SEL - , iBus_CTI - , iBus_BTE - , dBus_CYC - , dBus_STB - , dBus_WE - , dBus_ADR - , dBus_DAT_MOSI - , dBus_SEL - , dBus_CTI - , dBus_BTE - ) = vexRiscv# sourcePath hasClock hasReset - timerInterrupt - externalInterrupt - softwareInterrupt - - iBus_ACK - iBus_ERR - iBus_DAT_MISO - - dBus_ACK - dBus_ERR - dBus_DAT_MISO - - - - + cpuSrcPath <- runIO $ getPackageRelFilePath "example-cpu/VexRiscv.v" + pure $ LitE $ StringL cpuSrcPath) vexRiscv# :: KnownDomain dom => String -> Clock dom -> Reset dom - -- input signals - -> Signal dom Bit -- ^ timerInterrupt - -> Signal dom Bit -- ^ externalInterrupt - -> Signal dom Bit -- ^ softwareInterrupt - -- iBusWbS2M - -> Signal dom Bool -- ^ iBus_ACK - -> Signal dom Bool -- ^ iBus_ERR - -> Signal dom (BitVector 32) -- ^ iBus_DAT_MISO - -- dBusWbS2M - -> Signal dom Bool -- ^ dBus_ACK - -> Signal dom Bool -- ^ dBus_ERR - -> Signal dom (BitVector 32) -- ^ dBus_DAT_MISO - - -- output signals - -> - ( - -- iBus M2S - Signal dom Bool -- ^ iBus_CYC - , Signal dom Bool -- ^ iBus_STB - , Signal dom Bool -- ^ iBus_WE - , Signal dom (BitVector 30) -- ^ iBus_ADR - , Signal dom (BitVector 32) -- ^ iBus_DAT_MOSI - , Signal dom (BitVector 4) -- ^ iBus_SEL - , Signal dom (BitVector 3) -- ^ iBus_CTI - , Signal dom (BitVector 2) -- ^ iBus_BTE - - -- dBus M2S - , Signal dom Bool -- ^ dBus_CYC - , Signal dom Bool -- ^ dBus_STB - , Signal dom Bool -- ^ dBus_WE - , Signal dom (BitVector 30) -- ^ dBus_ADR - , Signal dom (BitVector 32) -- ^ dBus_DAT_MOSI - , Signal dom (BitVector 4) -- ^ dBus_SEL - , Signal dom (BitVector 3) -- ^ dBus_CTI - , Signal dom (BitVector 2) -- ^ dBus_BTE - ) -vexRiscv# !_sourcePath clk rst0 - timerInterrupt - externalInterrupt - softwareInterrupt - iBus_ACK - iBus_ERR - iBus_DAT_MISO - - dBus_ACK - dBus_ERR - dBus_DAT_MISO - - = + -> Signal dom Input + -> Signal dom Output +vexRiscv# !_sourcePath clk rst input = + fmap outputToOutput $ + simInitThenCycles + (inputToNonCombInput <$> unsafeToActiveHigh rst <*> input) + (inputToCombInput <$> input) + where + (v, initStage1, initStage2, stepRising, stepFalling, _shutDown) = unsafePerformIO vexCPU + + simInitThenCycles :: + Signal dom NON_COMB_INPUT -> + Signal dom COMB_INPUT -> + Signal dom OUTPUT + simInitThenCycles (cnc :- cncs) ~(cc :- ccs) = let - (v, initStage1, initStage2, stepRising, stepFalling, _shutDown) = unsafePerformIO vexCPU - - nonCombInput = NON_COMB_INPUT - <$> (boolToBit <$> unsafeToActiveHigh rst0) - <*> timerInterrupt - <*> externalInterrupt - <*> softwareInterrupt - - combInput = COMB_INPUT - <$> (boolToBit <$> iBus_ACK) - <*> (unpack <$> iBus_DAT_MISO) - <*> (boolToBit <$> iBus_ERR) - <*> (boolToBit <$> dBus_ACK) - <*> (unpack <$> dBus_DAT_MISO) - <*> (boolToBit <$> dBus_ERR) - - wordCast = fromInteger . toInteger - - simInitThenCycles :: - Signal dom NON_COMB_INPUT -> - Signal dom COMB_INPUT -> - Signal dom OUTPUT - simInitThenCycles (cnc :- cncs) ~(cc :- ccs) = - let - -- Note: we don't need @ticks@ for the initialization stages, because this - -- first cycle of a 'Signal' is meant to model what happens _before_ a - -- clock edge. - out0 = unsafePerformIO (initStage1 v cnc) - stage2Out = unsafePerformIO (initStage2 v cc) - ticks = first wordCast <$> singleClockEdgesRelative clk - out1 = simCycles ticks cncs ccs - in - out0 :- (out0 `seq` (stage2Out `seq` out1)) - - simCycles :: - [(Word64, ActiveEdge)] -> - Signal dom NON_COMB_INPUT -> - Signal dom COMB_INPUT -> - Signal dom OUTPUT - simCycles ((fsSinceLastEvent, Rising) : ts) (cnc :- cncs) ccs = - let - out0 = unsafePerformIO (stepRising v fsSinceLastEvent cnc) - out1 = simCycles ts cncs ccs - in - out0 :- (out0 `seq` out1) - - simCycles ((fsSinceLastEvent, Falling) : ts) cncs (cc :- ccs) = - let !() = unsafePerformIO (stepFalling v fsSinceLastEvent cc) - in simCycles ts cncs ccs - - simCycles [] _ _ = error "Empty ticks: should never happen" - - output = simInitThenCycles nonCombInput combInput - - iBus_CYC = FFI.iBusWishbone_CYC <$> output - iBus_STB = FFI.iBusWishbone_STB <$> output - iBus_WE = FFI.iBusWishbone_WE <$> output - iBus_ADR = FFI.iBusWishbone_ADR <$> output - iBus_DAT_MOSI = FFI.iBusWishbone_DAT_MOSI <$> output - iBus_SEL = FFI.iBusWishbone_SEL <$> output - iBus_CTI = FFI.iBusWishbone_CTI <$> output - iBus_BTE = FFI.iBusWishbone_BTE <$> output - - dBus_CYC = FFI.dBusWishbone_CYC <$> output - dBus_STB = FFI.dBusWishbone_STB <$> output - dBus_WE = FFI.dBusWishbone_WE <$> output - dBus_ADR = FFI.dBusWishbone_ADR <$> output - dBus_DAT_MOSI = FFI.dBusWishbone_DAT_MOSI <$> output - dBus_SEL = FFI.dBusWishbone_SEL <$> output - dBus_CTI = FFI.dBusWishbone_CTI <$> output - dBus_BTE = FFI.dBusWishbone_BTE <$> output + -- Note: we don't need @ticks@ for the initialization stages, because this + -- first cycle of a 'Signal' is meant to model what happens _before_ a + -- clock edge. + out0 = unsafePerformIO (initStage1 v cnc) + stage2Out = unsafePerformIO (initStage2 v cc) + ticks = first (fromInteger . toInteger) <$> singleClockEdgesRelative clk + out1 = simCycles ticks cncs ccs in - ( -- iBus - bitToBool <$> iBus_CYC - , bitToBool <$> iBus_STB - , bitToBool <$> iBus_WE - , truncateB . pack <$> iBus_ADR - , pack <$> iBus_DAT_MOSI - , truncateB . pack <$> iBus_SEL - , truncateB . pack <$> iBus_CTI - , truncateB . pack <$> iBus_BTE - - -- dBus - , bitToBool <$> dBus_CYC - , bitToBool <$> dBus_STB - , bitToBool <$> dBus_WE - , truncateB . pack <$> dBus_ADR - , pack <$> dBus_DAT_MOSI - , truncateB . pack <$> dBus_SEL - , truncateB . pack <$> dBus_CTI - , truncateB . pack <$> dBus_BTE - ) -{-# NOINLINE vexRiscv# #-} -{-# ANN vexRiscv# ( + out0 :- (out0 `seq` (stage2Out `seq` out1)) + + simCycles :: + [(Word64, ActiveEdge)] -> + Signal dom NON_COMB_INPUT -> + Signal dom COMB_INPUT -> + Signal dom OUTPUT + simCycles ((fsSinceLastEvent, Rising) : ts) (cnc :- cncs) ccs = let - primName = 'vexRiscv# - ( _ - , srcPath - , clk - , rst - , timerInterrupt - , externalInterrupt - , softwareInterrupt - , iBus_ACK - , iBus_ERR - , iBus_DAT_MISO - , dBus_ACK - , dBus_ERR - , dBus_DAT_MISO - ) = vecToTuple (indicesI @13) - - ( iBus_CYC - , iBus_STB - , iBus_WE - , iBus_ADR - , iBus_DAT_MOSI - , iBus_SEL - , iBus_CTI - , iBus_BTE - , dBus_CYC - , dBus_STB - , dBus_WE - , dBus_ADR - , dBus_DAT_MOSI - , dBus_SEL - , dBus_CTI - , dBus_BTE - ) = vecToTuple $ (\x -> extend @_ @16 @13 x + 1) <$> indicesI @16 - - cpu = extend @_ @_ @1 dBus_BTE + 1 + out0 = unsafePerformIO (stepRising v fsSinceLastEvent cnc) + out1 = simCycles ts cncs ccs in - InlineYamlPrimitive [Verilog] [__i| - BlackBox: - name: #{primName} - kind: Declaration - template: |- - // vexRiscv begin - - ~DEVNULL[~FILE[~LIT[#{srcPath}]]] - - wire ~GENSYM[iBus_CYC][#{iBus_CYC}]; - wire ~GENSYM[iBus_STB][#{iBus_STB}]; - wire ~GENSYM[iBus_WE][#{iBus_WE}]; - wire [29:0] ~GENSYM[iBus_ADR][#{iBus_ADR}]; - wire [31:0] ~GENSYM[iBus_DAT_MOSI][#{iBus_DAT_MOSI}]; - wire [3:0] ~GENSYM[iBus_SEL][#{iBus_SEL}]; - wire [2:0] ~GENSYM[iBus_CTI][#{iBus_CTI}]; - wire [1:0] ~GENSYM[iBus_BTE][#{iBus_BTE}]; - - wire ~GENSYM[dBus_CYC][#{dBus_CYC}]; - wire ~GENSYM[dBus_STB][#{dBus_STB}]; - wire ~GENSYM[dBus_WE][#{dBus_WE}]; - wire [29:0] ~GENSYM[dBus_ADR][#{dBus_ADR}]; - wire [31:0] ~GENSYM[dBus_DAT_MOSI][#{dBus_DAT_MOSI}]; - wire [3:0] ~GENSYM[dBus_SEL][#{dBus_SEL}]; - wire [2:0] ~GENSYM[dBus_CTI][#{dBus_CTI}]; - wire [1:0] ~GENSYM[dBus_BTE][#{dBus_BTE}]; - - VexRiscv ~GENSYM[cpu][#{cpu}] ( - .timerInterrupt ( ~ARG[#{timerInterrupt}] ), - .externalInterrupt ( ~ARG[#{externalInterrupt}] ), - .softwareInterrupt ( ~ARG[#{softwareInterrupt}] ), - - .iBusWishbone_CYC ( ~SYM[#{iBus_CYC}] ), - .iBusWishbone_STB ( ~SYM[#{iBus_STB}] ), - .iBusWishbone_ACK ( ~ARG[#{iBus_ACK}] ), - .iBusWishbone_WE ( ~SYM[#{iBus_WE}] ), - .iBusWishbone_ADR ( ~SYM[#{iBus_ADR}] ), - .iBusWishbone_DAT_MISO ( ~ARG[#{iBus_DAT_MISO}] ), - .iBusWishbone_DAT_MOSI ( ~SYM[#{iBus_DAT_MOSI}] ), - .iBusWishbone_SEL ( ~SYM[#{iBus_SEL}] ), - .iBusWishbone_ERR ( ~ARG[#{iBus_ERR}] ), - .iBusWishbone_CTI ( ~SYM[#{iBus_CTI}] ), - .iBusWishbone_BTE ( ~SYM[#{iBus_BTE}] ), - - .dBusWishbone_CYC ( ~SYM[#{dBus_CYC}] ), - .dBusWishbone_STB ( ~SYM[#{dBus_STB}] ), - .dBusWishbone_ACK ( ~ARG[#{dBus_ACK}] ), - .dBusWishbone_WE ( ~SYM[#{dBus_WE}] ), - .dBusWishbone_ADR ( ~SYM[#{dBus_ADR}] ), - .dBusWishbone_DAT_MISO ( ~ARG[#{dBus_DAT_MISO}] ), - .dBusWishbone_DAT_MOSI ( ~SYM[#{dBus_DAT_MOSI}] ), - .dBusWishbone_SEL ( ~SYM[#{dBus_SEL}] ), - .dBusWishbone_ERR ( ~ARG[#{dBus_ERR}] ), - .dBusWishbone_CTI ( ~SYM[#{dBus_CTI}] ), - .dBusWishbone_BTE ( ~SYM[#{dBus_BTE}] ), - - .clk ( ~ARG[#{clk}] ), - .reset ( ~ARG[#{rst}] ) - ); - - assign ~RESULT = { - ~SYM[#{iBus_CYC}], - ~SYM[#{iBus_STB}], - ~SYM[#{iBus_WE}], - ~SYM[#{iBus_ADR}], - ~SYM[#{iBus_DAT_MOSI}], - ~SYM[#{iBus_SEL}], - ~SYM[#{iBus_CTI}], - ~SYM[#{iBus_BTE}], - ~SYM[#{dBus_CYC}], - ~SYM[#{dBus_STB}], - ~SYM[#{dBus_WE}], - ~SYM[#{dBus_ADR}], - ~SYM[#{dBus_DAT_MOSI}], - ~SYM[#{dBus_SEL}], - ~SYM[#{dBus_CTI}], - ~SYM[#{dBus_BTE}] - }; - - // vexRiscv end - - |] ) #-} + out0 :- (out0 `seq` out1) + simCycles ((fsSinceLastEvent, Falling) : ts) cncs (cc :- ccs) = + let !() = unsafePerformIO (stepFalling v fsSinceLastEvent cc) + in simCycles ts cncs ccs + simCycles [] _ _ = error "Empty ticks: should never happen" +{-# CLASH_OPAQUE vexRiscv# #-} +{-# ANN vexRiscv# hasBlackBox #-} +{-# ANN vexRiscv# ( + let primName = 'vexRiscv# + tfName = 'vexRiscvBBF + in InlineYamlPrimitive [Verilog] [__i| + BlackBoxHaskell: + name: #{primName} + templateFunction: #{tfName} + workInfo: Always + |]) #-} -- | Return a function that performs an execution step and a function to free -- the internal CPU state diff --git a/clash-vexriscv/src/VexRiscv/BlackBox.hs b/clash-vexriscv/src/VexRiscv/BlackBox.hs new file mode 100644 index 0000000..29e5454 --- /dev/null +++ b/clash-vexriscv/src/VexRiscv/BlackBox.hs @@ -0,0 +1,172 @@ +-- SPDX-FileCopyrightText: 2024 Google LLC +-- +-- SPDX-License-Identifier: Apache-2.0 + +{-# LANGUAGE MagicHash #-} +{-# LANGUAGE OverloadedStrings #-} +{-# LANGUAGE PostfixOperators #-} +{-# LANGUAGE QuasiQuotes #-} +{-# LANGUAGE ViewPatterns #-} + +{-# OPTIONS_HADDOCK hide #-} + +module VexRiscv.BlackBox where + +import Prelude + +import Control.Monad.State (State) +import Data.List.Infinite (Infinite(..), (...)) +import Data.Text (Text) +import Data.Text.Prettyprint.Doc.Extra (Doc) +import Text.Show.Pretty (ppShow) +import GHC.Stack (HasCallStack) + +import Clash.Backend (Backend) +import Clash.Netlist.Types (TemplateFunction(..), BlackBoxContext) + +import qualified Clash.Netlist.BlackBox.Types as N +import qualified Clash.Netlist.Id as Id +import qualified Clash.Netlist.Types as N +import qualified Clash.Primitives.DSL as DSL + +listToTup5 :: [a] -> (a, a, a, a, a) +listToTup5 [a, b, c, d, e] = (a, b, c, d, e) +listToTup5 _ = error "listToTup5: list must have 5 elements" + +vexRiscvBBF :: HasCallStack => N.BlackBoxFunction +vexRiscvBBF _isD _primName _args _resTys = pure $ Right (bbMeta, bb) + where + bbMeta = N.emptyBlackBoxMeta + { N.bbKind = N.TDecl + , N.bbIncludes = [] + -- TODO: + -- [ ( ("VexRiscv", "v") + -- , BBFunction (show 'vexRiscvVerilogTF) 0 (vexRiscvVerilogTF path)) + -- ] + } + + bb :: N.BlackBox + bb = N.BBFunction (show 'vexRiscvTF) 0 vexRiscvTF + +vexRiscvTF :: TemplateFunction +vexRiscvTF = + let _knownDomain :< srcPath :< clk :< rst :< inp :< _ = (0...) + in TemplateFunction [srcPath, clk, rst, inp] (const True) vexRiscvTF# + +vexRiscvTF# :: Backend backend => BlackBoxContext -> State backend Doc +vexRiscvTF# bbCtx + | [_knownDomain, clk, rst, inp] <- map fst (DSL.tInputs bbCtx) + , [outputTy@(N.Product _ _ [iWishboneM2Sty, dWishboneM2Sty])] <- map snd (N.bbResults bbCtx) + , N.Product _ _ [adrTy, datMosiTy, selTy, _lockTy, cycTy, stbTy, weTy, ctiTy, bteTy] <- iWishboneM2Sty + = do + let + compName :: Text + compName = "VexRiscv" + + instName <- Id.make (compName <> "_inst") + DSL.declarationReturn bbCtx (compName <> "_block") $ do + ( timerInterrupt + , externalInterrupt + , softwareInterrupt + , iBusWbS2M + , dBusWbS2M + ) <- listToTup5 <$> DSL.deconstructProduct inp ["timerInt", "extInt", "softInt", "iBusWbS2M", "dBusWbS2M"] + + ( iBusWishbone_DAT_MISO + , iBusWishbone_ACK + , iBusWishbone_ERR + , _iBusWishbone_STL + , _iBusWishbone_RTY + ) <- listToTup5 <$> DSL.deconstructProduct iBusWbS2M ["i_rdata", "i_ack", "i_err", "i_stall", "i_retry"] + + ( dBusWishbone_DAT_MISO + , dBusWishbone_ACK + , dBusWishbone_ERR + , _dBusWishbone_STL + , _dBusWishbone_RTY + ) <- listToTup5 <$> DSL.deconstructProduct dBusWbS2M ["d_rdata", "d_ack", "d_err", "d_stall", "d_retry"] + + iBusWishbone_CYC <- DSL.declare "i_cyc" cycTy + iBusWishbone_STB <- DSL.declare "i_stb" stbTy + iBusWishbone_WE <- DSL.declare "i_we" weTy + iBusWishbone_ADR <- DSL.declare "i_adr" adrTy + iBusWishbone_DAT_MOSI <- DSL.declare "i_dat_mosi" datMosiTy + iBusWishbone_SEL <- DSL.declare "i_sel" selTy + iBusWishbone_CTI <- DSL.declare "i_cti" ctiTy + iBusWishbone_BTE <- DSL.declare "i_bte" bteTy + + dBusWishbone_CYC <- DSL.declare "d_cyc" cycTy + dBusWishbone_STB <- DSL.declare "d_stb" stbTy + dBusWishbone_WE <- DSL.declare "d_we" weTy + dBusWishbone_ADR <- DSL.declare "d_adr" adrTy + dBusWishbone_DAT_MOSI <- DSL.declare "d_dat_mosi" datMosiTy + dBusWishbone_SEL <- DSL.declare "d_sel" selTy + dBusWishbone_CTI <- DSL.declare "d_cti" ctiTy + dBusWishbone_BTE <- DSL.declare "d_bte" bteTy + + let + generics = [] + + inps :: [(Text, DSL.TExpr)] + inps = + [ ("clk", clk) + , ("reset", rst) + , ("timerInterrupt", timerInterrupt) + , ("externalInterrupt", externalInterrupt) + , ("softwareInterrupt", softwareInterrupt) + , ("iBusWishbone_DAT_MISO", iBusWishbone_DAT_MISO) + , ("iBusWishbone_ACK", iBusWishbone_ACK) + , ("iBusWishbone_ERR", iBusWishbone_ERR) + , ("dBusWishbone_DAT_MISO", dBusWishbone_DAT_MISO) + , ("dBusWishbone_ACK", dBusWishbone_ACK) + , ("dBusWishbone_ERR", dBusWishbone_ERR) + ] + + outs :: [(Text, DSL.TExpr)] + outs = + [ ("iBusWishbone_CYC", iBusWishbone_CYC) + , ("iBusWishbone_STB", iBusWishbone_STB) + , ("iBusWishbone_WE", iBusWishbone_WE) + , ("iBusWishbone_ADR", iBusWishbone_ADR) + , ("iBusWishbone_DAT_MOSI", iBusWishbone_DAT_MOSI) + , ("iBusWishbone_SEL", iBusWishbone_SEL) + , ("iBusWishbone_CTI", iBusWishbone_CTI) + , ("iBusWishbone_BTE", iBusWishbone_BTE) + , ("dBusWishbone_CYC", dBusWishbone_CYC) + , ("dBusWishbone_STB", dBusWishbone_STB) + , ("dBusWishbone_WE", dBusWishbone_WE) + , ("dBusWishbone_ADR", dBusWishbone_ADR) + , ("dBusWishbone_DAT_MOSI", dBusWishbone_DAT_MOSI) + , ("dBusWishbone_SEL", dBusWishbone_SEL) + , ("dBusWishbone_CTI", dBusWishbone_CTI) + , ("dBusWishbone_BTE", dBusWishbone_BTE) + ] + + DSL.instDecl N.Empty (Id.unsafeMake compName) instName generics inps outs + + pure [DSL.constructProduct outputTy + [ DSL.constructProduct iWishboneM2Sty + [ iBusWishbone_ADR + , iBusWishbone_DAT_MOSI + , iBusWishbone_SEL + , DSL.litTExpr (DSL.B False) + , iBusWishbone_CYC + , iBusWishbone_STB + , iBusWishbone_WE + , iBusWishbone_CTI + , iBusWishbone_BTE + ] + , DSL.constructProduct dWishboneM2Sty + [ dBusWishbone_ADR + , dBusWishbone_DAT_MOSI + , dBusWishbone_SEL + , DSL.litTExpr (DSL.B False) + , dBusWishbone_CYC + , dBusWishbone_STB + , dBusWishbone_WE + , dBusWishbone_CTI + , dBusWishbone_BTE + ] + ]] + +vexRiscvTF# bbCtx = error (ppShow bbCtx)