From 5327ba2079e3c9a35f4fa2f0989bcf20143a3cbe Mon Sep 17 00:00:00 2001 From: Ryan Slawson Date: Thu, 6 Feb 2025 19:42:25 +0100 Subject: [PATCH] Add demo DUT. --- .github/synthesis/debug.json | 3 + bittide-instances/bittide-instances.cabal | 5 + .../data/constraints/demoTest.xdc | 44 + bittide-instances/data/openocd/start.sh | 14 +- .../data/openocd/vexriscv-2chain.tcl | 48 ++ .../src/Bittide/Instances/Hitl/Demo.hs | 774 ++++++++++++++++++ .../src/Bittide/Instances/Hitl/Driver/Demo.hs | 314 +++++++ .../src/Bittide/Instances/Hitl/Tests.hs | 4 +- .../Bittide/Instances/Hitl/Utils/Program.hs | 30 +- .../src/Bittide/Instances/Hitl/VexRiscv.hs | 2 + .../src/Bittide/Instances/Pnr/Ethernet.hs | 2 + .../Instances/Pnr/ProcessingElement.hs | 1 + bittide-instances/src/Project/Handle.hs | 2 +- .../tests/Tests/ClockControlWb.hs | 1 + bittide-instances/tests/Wishbone/Axi.hs | 1 + .../tests/Wishbone/CaptureUgn.hs | 1 + bittide-instances/tests/Wishbone/DnaPortE2.hs | 1 + .../tests/Wishbone/ScatterGather.hs | 1 + bittide-instances/tests/Wishbone/Time.hs | 1 + bittide-instances/tests/Wishbone/Watchdog.hs | 1 + .../Bittide/ClockControl/Callisto/Types.hs | 20 + .../src/Bittide/ClockControl/CallistoSw.hs | 128 ++- bittide/src/Bittide/Node.hs | 6 +- bittide/src/Bittide/ProcessingElement.hs | 21 +- bittide/src/Bittide/Transceiver.hs | 20 + bittide/src/Bittide/Wishbone.hs | 25 + cabal.project | 4 +- firmware-binaries/Cargo.lock | 8 + firmware-binaries/Cargo.toml | 1 + firmware-binaries/management-unit/Cargo.toml | 16 + firmware-binaries/management-unit/build.rs | 23 + firmware-binaries/management-unit/memory.x | 18 + firmware-binaries/management-unit/src/main.rs | 23 + 33 files changed, 1531 insertions(+), 32 deletions(-) create mode 100644 .github/synthesis/debug.json create mode 100644 bittide-instances/data/constraints/demoTest.xdc create mode 100644 bittide-instances/data/openocd/vexriscv-2chain.tcl create mode 100644 bittide-instances/src/Bittide/Instances/Hitl/Demo.hs create mode 100644 bittide-instances/src/Bittide/Instances/Hitl/Driver/Demo.hs create mode 100644 firmware-binaries/management-unit/Cargo.toml create mode 100644 firmware-binaries/management-unit/build.rs create mode 100644 firmware-binaries/management-unit/memory.x create mode 100644 firmware-binaries/management-unit/src/main.rs diff --git a/.github/synthesis/debug.json b/.github/synthesis/debug.json new file mode 100644 index 000000000..62c9a9fc6 --- /dev/null +++ b/.github/synthesis/debug.json @@ -0,0 +1,3 @@ +[ + {"top": "demoTest", "stage": "test"} +] diff --git a/bittide-instances/bittide-instances.cabal b/bittide-instances/bittide-instances.cabal index f4debd0e5..444d1c92c 100644 --- a/bittide-instances/bittide-instances.cabal +++ b/bittide-instances/bittide-instances.cabal @@ -110,6 +110,9 @@ common common-options ghc-typelits-knownnat, ghc-typelits-natnormalise, lift-type, + lifted-base, + monad-control, + mtl, network-simple, pretty-simple, process, @@ -137,7 +140,9 @@ library Bittide.Instances.Domains Bittide.Instances.Hacks Bittide.Instances.Hitl.BoardTest + Bittide.Instances.Hitl.Demo Bittide.Instances.Hitl.DnaOverSerial + Bittide.Instances.Hitl.Driver.Demo Bittide.Instances.Hitl.Driver.DnaOverSerial Bittide.Instances.Hitl.Driver.SwCcTopologies Bittide.Instances.Hitl.Driver.VexRiscv diff --git a/bittide-instances/data/constraints/demoTest.xdc b/bittide-instances/data/constraints/demoTest.xdc new file mode 100644 index 000000000..92f9053bf --- /dev/null +++ b/bittide-instances/data/constraints/demoTest.xdc @@ -0,0 +1,44 @@ +# SPDX-FileCopyrightText: 2025 Google LLC +# +# SPDX-License-Identifier: Apache-2.0 + +set_property BOARD_PART_PIN sysclk_125_n [get_ports SYSCLK_125_n] +set_property BOARD_PART_PIN sysclk_125_p [get_ports SYSCLK_125_p] +set_property BOARD_PART_PIN sma_mgt_refclk_n [get_ports SMA_MGT_REFCLK_C_n] +set_property BOARD_PART_PIN sma_mgt_refclk_p [get_ports SMA_MGT_REFCLK_C_p] + +set_property BOARD_PART_PIN GPIO_LED_0_LS [get_ports spiDone] + +set_clock_groups \ + -asynchronous \ + -group [get_clocks -include_generated_clocks {SYSCLK_125_p}] \ + -group [get_clocks -include_generated_clocks {SMA_MGT_REFCLK_C_p}] + +# Color | FPGA pin | LVLSHFT | Connection +# --------|---------------|---------------|------------------ +# Grey | PMOD0_0 | IO1 | SYNC_OUT (legacy) +# Blue | PMOD0_1 | IO2 | FINC +# Yellow | PMOD0_2 | IO3 | MOSI/SDIO +# Red | PMOD0_3 | IO4 | SCLK +# White | PMOD0_4 | IO5 | SYNC_IN (legacy) +# Purple | PMOD0_5 | IO6 | FDEC +# Green | PMOD0_6 | IO7 | CSB +# Orange | PMOD0_7 | IO8 | MISO/SDO +# Black | Not connected | Not connected | +# Brown | PMOD_GND | GND | GND (SPI) + +# PMOD1_[0..7] +set_property -dict {IOSTANDARD LVCMOS12 PACKAGE_PIN AN21} [get_ports {FINC}] +set_property -dict {IOSTANDARD LVCMOS12 PACKAGE_PIN AH18} [get_ports {MOSI}] +set_property -dict {IOSTANDARD LVCMOS12 PACKAGE_PIN AM19} [get_ports {SCLK}] +set_property -dict {IOSTANDARD LVCMOS12 PACKAGE_PIN AF25} [get_ports {FDEC}] +set_property -dict {IOSTANDARD LVCMOS12 PACKAGE_PIN AE21} [get_ports {CSB}] +set_property -dict {IOSTANDARD LVCMOS12 PACKAGE_PIN AM17} [get_ports {MISO}] + +# PMOD0_3 +# set_property -dict {IOSTANDARD LVCMOS12 PACKAGE_PIN AM19} [get_ports {shared_reset_btn}] + +# USER SMA GPIO_P +set_property -dict {IOSTANDARD LVCMOS18 PACKAGE_PIN H27} [get_ports {SYNC_IN}] +# USER_SMA_GPIO_N (connected on node 0 to SYNC_IN of all nodes) +set_property -dict {IOSTANDARD LVCMOS18 PACKAGE_PIN G27} [get_ports {SYNC_OUT}] diff --git a/bittide-instances/data/openocd/start.sh b/bittide-instances/data/openocd/start.sh index c9a7a6bf4..9a7fd6293 100755 --- a/bittide-instances/data/openocd/start.sh +++ b/bittide-instances/data/openocd/start.sh @@ -2,21 +2,21 @@ # SPDX-FileCopyrightText: 2024 Google LLC # # SPDX-License-Identifier: Apache-2.0 -set -e +set -xe # Default stdout to /dev/null OPENOCD_STDOUT_LOG="${OPENOCD_STDOUT_LOG:-/dev/null}" -stdout_dir=$(dirname "${OPENOCD_STDOUT_LOG}") +stdout_dir="$(dirname "${OPENOCD_STDOUT_LOG}")" mkdir -p "${stdout_dir}" -OPENOCD_STDOUT_LOG="$(realpath ${OPENOCD_STDOUT_LOG})" +OPENOCD_STDOUT_LOG="$(realpath "${OPENOCD_STDOUT_LOG}")" # Default stderr to /dev/null OPENOCD_STDERR_LOG="${OPENOCD_STDERR_LOG:-/dev/null}" -stderr_dir=$(dirname "${OPENOCD_STDERR_LOG}") +stderr_dir="$(dirname "${OPENOCD_STDERR_LOG}")" mkdir -p "${stderr_dir}" -OPENOCD_STDERR_LOG="$(realpath ${OPENOCD_STDERR_LOG})" +OPENOCD_STDERR_LOG="$(realpath "${OPENOCD_STDERR_LOG}")" -cd $(dirname $0) -exec openocd-vexriscv -f ports.tcl -f sipeed.tcl -f vexriscv_init.tcl $@ \ +cd "$(dirname "$0")" +exec openocd-vexriscv $@ \ > >(tee "${OPENOCD_STDOUT_LOG}") \ 2> >(tee "${OPENOCD_STDERR_LOG}" >&2) diff --git a/bittide-instances/data/openocd/vexriscv-2chain.tcl b/bittide-instances/data/openocd/vexriscv-2chain.tcl new file mode 100644 index 000000000..6a215f1d7 --- /dev/null +++ b/bittide-instances/data/openocd/vexriscv-2chain.tcl @@ -0,0 +1,48 @@ +# SPDX-FileCopyrightText: 2025 Google LLC +# +# SPDX-License-Identifier: Apache-2.0 +set user_gdb_port_a [env DEV_A_GDB] +if { $user_gdb_port_a == "" } { + error "Required environment variable 'DEV_A_GDB' not set." +} + +set user_gdb_port_b [env DEV_B_GDB] +if { $user_gdb_port_b == "" } { + error "Required environment variable 'DEV_B_GDB' not set." +} + +set git_top_level [string trim [exec git rev-parse --show-toplevel]] + +set _ENDIAN little +set _TAP_TYPE 1234 + +bindto 0.0.0.0 + +if { [info exists CPUTAPID] } { + set _CPUTAPID $CPUTAPID +} else { + set _CPUTAPID 0x10001fff +} + +set _CHIPNAME vexrisc_ocd + +jtag newtap $_CHIPNAME chain0 -expected-id $_CPUTAPID -irlen 4 -ircapture 0x1 -irmask 0x0F +jtag newtap $_CHIPNAME chain1 -expected-id $_CPUTAPID -irlen 4 -ircapture 0x1 -irmask 0x03 + +target create $_CHIPNAME.cpu1 vexriscv -endian $_ENDIAN -chain-position $_CHIPNAME.bridge1 -gdb-port $user_gdb_port_b +vexriscv readWaitCycles 10 +vexriscv cpuConfigFile [file join $git_top_level clash-vexriscv clash-vexriscv example-cpu ExampleCpu.yaml] + +target create $_CHIPNAME.cpu0 vexriscv -endian $_ENDIAN -chain-position $_CHIPNAME.bridge0 -gdb-port $user_gdb_port_a +vexriscv readWaitCycles 10 +vexriscv cpuConfigFile [file join $git_top_level clash-vexriscv clash-vexriscv example-cpu ExampleCpu.yaml] + +poll_period 50 + +init + +echo "Halting processor" + +halt + +sleep 1000 diff --git a/bittide-instances/src/Bittide/Instances/Hitl/Demo.hs b/bittide-instances/src/Bittide/Instances/Hitl/Demo.hs new file mode 100644 index 000000000..ebe7cac17 --- /dev/null +++ b/bittide-instances/src/Bittide/Instances/Hitl/Demo.hs @@ -0,0 +1,774 @@ +-- SPDX-FileCopyrightText: 2025 Google LLC +-- +-- SPDX-License-Identifier: Apache-2.0 +{-# LANGUAGE GADTs #-} +{-# LANGUAGE NamedFieldPuns #-} +{-# LANGUAGE NumericUnderscores #-} +{-# LANGUAGE OverloadedRecordDot #-} +{-# LANGUAGE OverloadedStrings #-} +{-# LANGUAGE RecordWildCards #-} +{-# LANGUAGE UndecidableInstances #-} +{-# OPTIONS_GHC -Wno-orphans -fconstraint-solver-iterations=10 #-} +{-# OPTIONS_GHC -fplugin=Protocols.Plugin #-} + +module Bittide.Instances.Hitl.Demo (dut, demoTest, tests) where + +import Clash.Explicit.Prelude hiding (PeriodToCycles) +import Clash.Prelude (HiddenClockResetEnable, withClockResetEnable) + +import Bittide.Arithmetic.Time (PeriodToCycles, trueFor) +import Bittide.ClockControl hiding (speedChangeToFincFdec) +import Bittide.ClockControl.Callisto.Types (CallistoCResult) +import Bittide.ClockControl.CallistoSw ( + SwControlCConfig (SwControlCConfig), + callistoSwClockControlC, + ) +import Bittide.ClockControl.Si5395J +import Bittide.ClockControl.Si539xSpi (ConfigState (Error, Finished), si539xSpi) +import Bittide.Counter +import Bittide.DoubleBufferedRam +import Bittide.ElasticBuffer ( + EbMode, + Overflow, + Underflow, + resettableXilinxElasticBuffer, + sticky, + ) +import Bittide.Hitl +import Bittide.Instances.Domains +import Bittide.Instances.Hitl.IlaPlot (syncInRecover, syncOutGenerator) +import Bittide.Instances.Hitl.Setup +import Bittide.ProcessingElement +import Bittide.SharedTypes +import Bittide.Simulate.Config (CcConf (..)) +import Bittide.Topology (Topology, TopologyType (Complete), complete) +import Bittide.Transceiver (transceiverPrbsN) +import Bittide.Wishbone + +import Clash.Annotations.TH (makeTopEntity) +import Clash.Cores.Xilinx.GTH +import Clash.Cores.Xilinx.Unisim.DnaPortE2 (simDna2) +import Clash.Cores.Xilinx.VIO +import Clash.Cores.Xilinx.Xpm.Cdc (xpmCdcSingle) +import Clash.Functor.Extra +import Clash.Signal.TH.Extra (deriveSignalHasFields) +import Clash.Xilinx.ClockGen (clockWizardDifferential) +import Data.Maybe (fromMaybe, isJust) +import Data.Proxy +import LiftType (liftTypeQ) +import Protocols +import Protocols.Idle +import Protocols.Wishbone +import System.FilePath (()) +import VexRiscv + +import qualified Bittide.Instances.Hitl.Driver.Demo as D +import qualified Bittide.Transceiver as Transceiver +import qualified Data.Map.Strict as Map (fromList) + +data TestConfig = TestConfig + { fpgaEnabled :: Bool + -- ^ Enables or disables an FPGA depending on the selected + -- topology. Disabled FPGAs immediediatly succeed after the test + -- start. + -- + -- Also note that the flag only disables clock control, while + -- other functionality, as for example SYNC_IN/SYNC_OUT time + -- synchronization, needs to stay alive. + , mask :: BitVector LinkCount + -- ^ The link mask depending on the selected topology. + } + deriving (Generic, NFDataX, BitPack, Show) + +deriveSignalHasFields ''TestConfig + +disabled :: TestConfig +disabled = + TestConfig + { fpgaEnabled = False + , mask = 0 + } + +clockControlConfig :: + $(case (instancesClockConfig (Proxy @Basic125)) of (_ :: t) -> liftTypeQ @t) +clockControlConfig = + $(lift (instancesClockConfig (Proxy @Basic125))) + +txCounterStartUgn :: BitVector 64 +txCounterStartUgn = 0xaabb_ccdd_eeff_1234 + +rxCounterStartUgn :: BitVector 64 +rxCounterStartUgn = 0x1122_3344_1122_3344 + +type AllStablePeriod = Seconds 5 + +type FifoSize = 5 -- = 2^5 = 32 + +{- | The number of FINCs (if positive) or FDECs (if negative) applied +prior to the test start leading to some desired initial clock +offset. +-} +type FincFdecCount = Signed 32 + +data SyncCfg dom = SyncCfg + { refClk :: Clock dom + , refRst :: Reset dom + , allReady :: Signal dom Bool + , startTest :: Signal dom Bool + , syncIn :: Signal dom Bool + } + +data SyncCtl dom = SyncCtl + { syncRst :: Reset dom + , syncOut :: Signal dom Bool + , syncStart :: Signal dom Bool + } + +syncCtlSetup :: + forall dom. + (HasDefinedInitialValues dom, HasSynchronousReset dom) => + SyncCfg dom -> + SyncCtl dom +syncCtlSetup SyncCfg{..} = SyncCtl{..} + where + syncOut = + dflipflop refClk + $ syncOutGenerator refClk startTest + $ trueFor (SNat @(Seconds 5)) refClk syncRst allReady + + syncInFiltered = + unsafeToActiveLow + $ resetGlitchFilter (SNat @128) refClk + $ unsafeFromActiveLow + $ xpmCdcSingle refClk refClk syncIn + + (syncActive, syncStart) = + unbundle $ syncInRecover refClk refRst startTest syncInFiltered + + syncRst = refRst `orReset` unsafeFromActiveLow syncActive + +{- Internal busses: + - Instruction memory + - Data memory + - `timeWb` +-} +type NmuInternalBusses = 3 +type NmuRemBusWidth nodeBusses = 30 - CLog 2 (nodeBusses + NmuInternalBusses) + +data SimpleManagementConfig nodeBusses where + SimpleManagementConfig :: + (KnownNat nodeBusses) => + PeConfig (nodeBusses + NmuInternalBusses) -> + DumpVcd -> + SimpleManagementConfig nodeBusses + +simpleManagementUnitC :: + forall bitDom nodeBusses. + ( KnownDomain bitDom + , HiddenClockResetEnable bitDom + , 1 <= DomainPeriod bitDom + , KnownNat nodeBusses + , CLog 2 (nodeBusses + NmuInternalBusses) <= 30 + ) => + SimpleManagementConfig nodeBusses -> + Circuit + (Jtag bitDom, CSignal bitDom (BitVector 64)) + (Vec nodeBusses (Wishbone bitDom 'Standard (NmuRemBusWidth nodeBusses) (Bytes 4))) +simpleManagementUnitC (SimpleManagementConfig peConfig dumpVcd) = + circuit $ \(jtag, _linkIn) -> do + peWbs <- processingElement dumpVcd peConfig -< jtag + ([timeWbBus], nmuWbs) <- splitAtC d1 -< peWbs + timeWb -< timeWbBus + idC -< nmuWbs + +jtagChain :: + forall dom n. + (KnownDomain dom, KnownNat n) => + Circuit (Jtag dom) (Vec (n + 1) (Jtag dom)) +jtagChain = Circuit go + where + go :: + (Fwd (Jtag dom), Bwd (Vec (n + 1) (Jtag dom))) -> + (Bwd (Jtag dom), Fwd (Vec (n + 1) (Jtag dom))) + go (fwd, bwds) = (bwd, fwds) + where + tcks = repeat $ testClock <$> fwd + tmss = repeat $ testModeSelect <$> fwd + tdis = (testDataIn <$> fwd) :> (testDataOut <<$>> init bwds) + fwds = zipWith3 (liftA3 JtagIn) tcks tmss tdis + bwd = (head bwds) + +unsafeJtagSynchronizer :: + forall dom1 dom2. + (KnownDomain dom1, KnownDomain dom2) => + Clock dom1 -> + Clock dom2 -> + Circuit (Jtag dom1) (Jtag dom2) +unsafeJtagSynchronizer clk1 clk2 = Circuit go + where + go :: + (Fwd (Jtag dom1), Bwd (Jtag dom2)) -> + (Bwd (Jtag dom1), Fwd (Jtag dom2)) + go (jtagIn1, jtagOut2) = (jtagOut1, jtagIn2) + where + jtagOut1 = unsafeSynchronizer clk2 clk1 jtagOut2 + jtagIn2 = unsafeSynchronizer clk1 clk2 jtagIn1 + +-- | Availabe step size configurations. +data StepSizeSelect + = PPB_1 + | PPB_10 + | PPB_100 + | PPB_500 + | PPM_1 + deriving (Generic, NFDataX, BitPack, Eq, Enum, Bounded, Show) + +{- | The step size, as it is used by all tests. Note that changing the +step size for individual tests requires recalibration of the clock +offsets, which is why we fix it to a single and common value here. +-} +commonStepSizeSelect :: StepSizeSelect +commonStepSizeSelect = + -- Don't forget to update the value of f_step this value in "Callisto.hs" and + -- "callisto.rs". + PPB_100 + +commonSpiConfig :: TestConfig6_200_on_0a_RegisterMap +commonSpiConfig = case commonStepSizeSelect of + PPB_1 -> testConfig6_200_on_0a_1ppb + PPB_10 -> testConfig6_200_on_0a_10ppb + PPB_100 -> testConfig6_200_on_0a_100ppb + PPB_500 -> testConfig6_200_on_0a_500ppb + PPM_1 -> testConfig6_200_on_0a_1ppm + +foldWithBitMask :: + forall n a. + (KnownNat n) => + (a -> a -> a) -> + a -> + Vec (n + 1) a -> + BitVector (n + 1) -> + a +foldWithBitMask foldFn dflt foldVec mask = output + where + maskVec :: Vec (n + 1) Bit + maskVec = bv2v mask + go :: a -> Bit -> a + go val b = if b == high then val else dflt + defaultMasked :: Vec (n + 1) a + defaultMasked = zipWith go foldVec maskVec + output :: a + output = fold foldFn defaultMasked + +dut :: + "REFCLK" ::: Clock Basic125 -> + "SKYCLK" ::: Clock Ext200 -> + "TEST_CFG" ::: Signal Basic125 TestConfig -> + "SYNC_CFG" ::: SyncCtl Basic125 -> + "GTH_RX_NS" ::: TransceiverWires GthRxS LinkCount -> + "GTH_RX_PS" ::: TransceiverWires GthRxS LinkCount -> + "MISO" ::: Signal Basic125 Bit -> + "JTAG_IN" ::: Signal Basic125 JtagIn -> + ( "GTH_TX_NS" ::: TransceiverWires GthTxS LinkCount + , "GTH_TX_PS" ::: TransceiverWires GthTxS LinkCount + , "handshakesDone" ::: Signal Basic125 Bool + , "FINC_FDEC" ::: Signal Basic125 (FINC, FDEC) + , "spiDone" ::: Signal Basic125 Bool + , "" + ::: ( "SCLK" ::: Signal Basic125 Bool + , "MOSI" ::: Signal Basic125 Bit + , "CSB" ::: Signal Basic125 Bool + ) + , "JTAG_OUT" ::: Signal Basic125 JtagOut + , "transceiversFailedAfterUp" ::: Signal Basic125 Bool + , "ALL_READY" ::: Signal Basic125 Bool + , "ALL_STABLE" ::: Signal Basic125 Bool + , "noFifoOverflows" ::: Signal Basic125 Bool + , "noFifoUnderflows" ::: Signal Basic125 Bool + ) +dut refClk skyClk cfg SyncCtl{..} rxNs rxPs miso jtagIn = + ( transceivers.txNs + , transceivers.txPs + , allHandshakesComplete + , frequencyAdjustments + , spiDone + , spiOut + , jtagOut + , transceiversFailedAfterUp + , allReady + , allStable0 + , noFifoOverflows + , noFifoUnderflows + ) + where + spiState :: Signal Basic125 (ConfigState Basic125 TestConfig6_200_on_0a_TotalRegs) + spiOut :: (Signal Basic125 Bool, Signal Basic125 Bit, Signal Basic125 Bool) + (_, _, spiState, spiOut) = + withClockResetEnable refClk spiRst enableGen + $ si539xSpi commonSpiConfig (SNat @(Microseconds 10)) (pure Nothing) miso + + spiDone :: Signal Basic125 Bool + spiDone = dflipflop refClk $ (== Finished) <$> spiState + spiErr :: Signal Basic125 Bool + spiErr = dflipflop refClk $ isErr <$> spiState + gthAllReset :: Reset Basic125 + gthAllReset = unsafeFromActiveLow spiDone + + spiRst :: Reset Basic125 + spiRst = syncRst `orReset` unsafeFromActiveHigh spiErr + + isErr :: ConfigState dom n -> Bool + isErr (Error _) = True + isErr _ = False + + transceivers :: Transceiver.Outputs LinkCount GthTx GthRx GthTxS Basic125 + transceivers = + transceiverPrbsN + @GthTx + @GthRx + @Ext200 + @Basic125 + @GthTxS + @GthRxS + Transceiver.defConfig + Transceiver.Inputs + { clock = refClk + , reset = gthAllReset + , refClock = skyClk + , channelNames + , clockPaths + , rxNs + , rxPs + , -- controlled by management unit, should come from combination of ?? + txDatas = repeat txCounter + , txStarts = repeat (pure True) + , rxReadys = repeat (pure True) + } + + bittideClk :: Clock GthTx + bittideClk = transceivers.txClock + + allHandshakesComplete :: Signal Basic125 Bool + allHandshakesComplete = + foldWithBitMask + (&&) + True + <$> (bundle transceivers.handshakesCompleteFree) + <*> cfg.mask + handshakeRst :: Reset Basic125 + handshakeRst = unsafeFromActiveLow allHandshakesComplete + handshakeRstTx :: Reset GthTx + handshakeRstTx = + unsafeFromActiveLow + $ foldWithBitMask + (&&) + True + <$> (bundle transceivers.handshakesCompleteTx) + <*> (unsafeSynchronizer refClk bittideClk cfg.mask) + + allReady :: Signal Basic125 Bool + allReady = + trueFor (SNat @(Milliseconds 500)) refClk spiRst (and <$> bundle transceivers.linkReadys) + transceiversFailedAfterUp :: Signal Basic125 Bool + transceiversFailedAfterUp = + sticky refClk syncRst (isFalling refClk spiRst enableGen False allReady) + + othersNotInStartReset :: Vec LinkCount (Signal GthRx Bool) + othersNotInStartReset = maybe False (\val -> msb val == high) <<$>> transceivers.rxDatas + othersNotInStartResetRefC :: Vec LinkCount (Signal Basic125 Bool) + othersNotInStartResetRefC = + zipWith + (\s rxClk -> xpmCdcSingle rxClk refClk s) + othersNotInStartReset + transceivers.rxClocks + + startRst :: Reset Basic125 + startRst = + orReset spiRst + $ orReset (unsafeFromActiveLow allReady) + $ orReset (unsafeFromActiveLow syncStart) + $ orReset + (unsafeFromActiveHigh transceiversFailedAfterUp) + (unsafeFromActiveLow $ and <$> bundle othersNotInStartResetRefC) + startEna :: Enable Basic125 + startEna = toEnable $ unsafeToActiveLow startRst + startEnaTx :: Enable GthTx + startEnaTx = toEnable $ xpmCdcSingle refClk bittideClk $ fromEnable startEna + + notInStartReset :: Signal Basic125 Bool + notInStartReset = unsafeToActiveLow startRst + + muConfig :: SimpleManagementConfig 1 + muConfig = + SimpleManagementConfig + PeConfig + { memMapConfig = 0b100 :> 0b010 :> 0b110 :> 0b101 :> Nil + , initI = Undefined @(Div (64 * 1024) 4) + , initD = Undefined @(Div (64 * 1024) 4) + , iBusTimeout = d0 + , dBusTimeout = d0 + , includeIlaWb = False + } + NoDumpVcd + + ccConfig :: + SwControlCConfig CccStabilityCheckerMargin (CccStabilityCheckerFramesize Basic125) 0 + ccConfig = + SwControlCConfig + SNat + SNat + PeConfig + { memMapConfig = 0b100 :> 0b010 :> 0b110 :> 0b101 :> Nil + , initI = Undefined @(Div (64 * 1024) 4) + , initD = Undefined @(Div (64 * 1024) 4) + , iBusTimeout = d0 + , dBusTimeout = d0 + , includeIlaWb = True + } + + circuitFn :: + ( Fwd + ( Jtag Basic125 + , CSignal GthTx (BitVector 64) + , CSignal Basic125 Bool + , CSignal Basic125 (BitVector LinkCount) + , Vec LinkCount (CSignal Basic125 (RelDataCount CccBufferSize)) + ) + , Bwd (CSignal Basic125 (CallistoCResult LinkCount)) + ) -> + ( Bwd + ( Jtag Basic125 + , CSignal GthTx (BitVector 64) + , CSignal Basic125 Bool + , CSignal Basic125 (BitVector LinkCount) + , Vec LinkCount (CSignal Basic125 (RelDataCount CccBufferSize)) + ) + , Fwd (CSignal Basic125 (CallistoCResult LinkCount)) + ) + Circuit circuitFn = circuit $ \(jtag, linkIn, reframe, mask, dc) -> do + [muJtag0, ccJtag] <- jtagChain -< jtag + muJtag1 <- unsafeJtagSynchronizer refClk bittideClk -< muJtag0 + + [muTimeWb] <- + withClockResetEnable + bittideClk + handshakeRstTx + startEnaTx + (simpleManagementUnitC muConfig) + -< (muJtag1, linkIn) + withClockResetEnable bittideClk handshakeRstTx startEnaTx (readDnaPortE2Wb simDna2) + -< muTimeWb + + (swCcOut, ccM2S) <- + withClockResetEnable + refClk + handshakeRst + startEna + (callistoSwClockControlC @LinkCount @CccBufferSize NoDumpVcd ccConfig) + -< (ccJtag, reframe, mask, dc) + idleSink -< ccM2S + + idC -< swCcOut + + ((jtagOut, _, _, _, _), callistoResult) = + circuitFn ((jtagIn, pure 0, pure False, cfg.mask, (resize <<$>> domainDiffs)), pure ()) + + allStable0 :: Signal Basic125 Bool + allStable0 = callistoResult.allStableC + allStable1 :: Signal Basic125 Bool + allStable1 = sticky refClk spiRst allStable0 + + frequencyAdjustments :: Signal Basic125 (FINC, FDEC) + frequencyAdjustments = + delay refClk enableGen minBound + $ speedChangeToStickyPins + refClk + startRst + enableGen + (SNat @Si539xHoldTime) + callistoResult.maybeSpeedChangeC + + domainDiffs :: Vec LinkCount (Signal Basic125 FincFdecCount) + domainDiffs = + zipWith3 + (domainDiffCounterExt refClk) + (repeat startRst) + transceivers.rxClocks + (repeat transceivers.txClock) + + txAllStable :: Signal GthTx Bool + txAllStable = xpmCdcSingle refClk bittideClk allStable1 + txReset2 :: Reset GthTx + txReset2 = orReset transceivers.txReset (unsafeFromActiveLow txAllStable) + + txNotInStartReset :: Signal GthTx Bool + txNotInStartReset = unsafeSynchronizer refClk bittideClk notInStartReset + + txCounter :: Signal GthTx (BitVector 64) + txCounter = + regEn + bittideClk + txReset2 + enableGen + txCounterStartUgn + txNotInStartReset + (txCounter + 1) + + rxFifos :: + Vec + LinkCount + ( Signal GthTx (RelDataCount FifoSize) + , Signal GthTx Underflow + , Signal GthTx Overflow + , Signal GthTx EbMode + , Signal GthTx (Maybe (BitVector 64)) + ) + rxFifos = zipWith go transceivers.rxClocks transceivers.rxDatas + where + go :: + Clock GthRx -> + Signal GthRx (Maybe (BitVector 64)) -> + ( Signal GthTx (RelDataCount FifoSize) + , Signal GthTx Underflow + , Signal GthTx Overflow + , Signal GthTx EbMode + , Signal GthTx (Maybe (BitVector 64)) + ) + go rxClk rxData = + resettableXilinxElasticBuffer + @FifoSize + @_ + @_ + @(Maybe (BitVector 64)) + bittideClk + rxClk + transceivers.txReset + rxData + + fifoUnderflowsTx :: Vec LinkCount (Signal GthTx Underflow) + fifoOverflowsTx :: Vec LinkCount (Signal GthTx Overflow) + mRxCntrs :: Vec LinkCount (Signal GthTx (Maybe (BitVector 64))) + (_, fifoUnderflowsTx, fifoOverflowsTx, _, mRxCntrs) = unzip5 rxFifos + _rxCntrs :: Vec LinkCount (Signal GthTx (BitVector 64)) + _rxCntrs = + (regMaybe bittideClk (unsafeFromActiveLow txNotInStartReset) enableGen rxCounterStartUgn) + <$> mRxCntrs + + fifoOverflowsFree :: Vec LinkCount (Signal Basic125 Overflow) + fifoOverflowsFree = (xpmCdcSingle bittideClk refClk) <$> fifoOverflowsTx + fifoUnderflowsFree :: Vec LinkCount (Signal Basic125 Underflow) + fifoUnderflowsFree = (xpmCdcSingle bittideClk refClk) <$> fifoUnderflowsTx + + maskWithCfg :: + Bool -> + Vec LinkCount (Signal Basic125 Bool) -> + Signal Basic125 (Vec LinkCount Bool) + maskWithCfg dflt = liftA2 go1 (mask <$> cfg) . bundle + where + go1 m = zipWith go2 (bitCoerce m) + go2 m val = if m then val else dflt + + findFifoError :: Vec LinkCount (Signal Basic125 Bool) -> Signal Basic125 Bool + findFifoError bits = result + where + masked = maskWithCfg False bits + observingError = or <$> masked + observedError = sticky refClk startRst observingError + result = not <$> observedError + + noFifoOverflows :: Signal Basic125 Bool + noFifoOverflows = findFifoError fifoOverflowsFree + noFifoUnderflows :: Signal Basic125 Bool + noFifoUnderflows = findFifoError fifoUnderflowsFree + +demoTest :: + "SMA_MGT_REFCLK_C" ::: DiffClock Ext200 -> + "SYSCLK_125" ::: DiffClock Ext125 -> + "SYNC_IN" ::: Signal Basic125 Bool -> + "GTH_RX_NS" ::: TransceiverWires GthRxS LinkCount -> + "GTH_RX_PS" ::: TransceiverWires GthRxS LinkCount -> + "MISO" ::: Signal Basic125 Bit -> + "JTAG" ::: Signal Basic125 JtagIn -> + ( "GTH_TX_NS" ::: TransceiverWires GthTxS LinkCount + , "GTH_TX_PS" ::: TransceiverWires GthTxS LinkCount + , "" + ::: ( "FINC" ::: Signal Basic125 Bool + , "FDEC" ::: Signal Basic125 Bool + ) + , "SYNC_OUT" ::: Signal Basic125 Bool + , "spiDone" ::: Signal Basic125 Bool + , "" + ::: ( "SCLK" ::: Signal Basic125 Bool + , "MOSI" ::: Signal Basic125 Bit + , "CSB" ::: Signal Basic125 Bool + ) + , "JTAG" ::: Signal Basic125 JtagOut + ) +demoTest boardClkDiff refClkDiff syncIn rxns rxps miso jtagIn = + (txns, txps, unbundle swFincFdecs, syncOut, spiDone, spiOut, jtagOut) + where + boardClk :: Clock Ext200 + boardClk = ibufds_gte3 boardClkDiff + refClk :: Clock Basic125 + refRst :: Reset Basic125 + (refClk, refRst) = clockWizardDifferential refClkDiff noReset + + testStart :: Signal Basic125 Bool + testConfig0 :: Signal Basic125 TestConfig + syncEn :: Signal Basic125 Bool + (unbundle -> (testStart, testConfig0, syncEn)) = + setName @"vioHitlt" + $ vioProbe + ("probe_test_done" :> "probe_test_success" :> "probe_handshakes_done" :> Nil) + ("probe_test_start" :> "probe_test_data" :> "probe_sync_enable" :> Nil) + (False, disabled, False) + refClk + testDone + testSuccess + handshakesDone + + SyncCtl{syncOut = unmaskedSyncOut, ..} = syncCtlSetup SyncCfg{..} + syncOut :: Signal Basic125 Bool + syncOut = unmaskedSyncOut .&&. syncEn + + testConfig :: Signal Basic125 (Maybe TestConfig) + testConfig = mux testStart (Just <$> testConfig0) (pure Nothing) + + startTest :: Signal Basic125 Bool + startTest = isJust <$> testConfig + cfg :: Signal Basic125 TestConfig + cfg = fromMaybe disabled <$> testConfig + + ( txns :: TransceiverWires GthTxS LinkCount + , txps :: TransceiverWires GthTxS LinkCount + , handshakesDone :: Signal Basic125 Bool + , swFincFdecs :: Signal Basic125 (Bool, Bool) + , spiDone :: Signal Basic125 Bool + , spiOut :: (Signal Basic125 Bool, Signal Basic125 Bit, Signal Basic125 Bool) + , jtagOut :: Signal Basic125 JtagOut + , transceiversFailedAfterUp :: Signal Basic125 Bool + , allReady :: Signal Basic125 Bool + , allStable :: Signal Basic125 Bool + , noFifoOverflows :: Signal Basic125 Bool + , noFifoUnderflows :: Signal Basic125 Bool + ) = + dut + refClk + boardClk + cfg + SyncCtl{..} + rxns + rxps + miso + jtagIn + + skip :: Signal Basic125 Bool + skip = maybe False (not . fpgaEnabled) <$> testConfig + + startBeforeAllReady :: Signal Basic125 Bool + startBeforeAllReady = + sticky + refClk + syncRst + (syncStart .&&. ((not <$> allReady) .||. transceiversFailedAfterUp)) + + fifoSuccess :: Signal Basic125 Bool + fifoSuccess = noFifoUnderflows .&&. noFifoOverflows + + endSuccess :: Signal Basic125 Bool + endSuccess = + trueFor (SNat @(Seconds 5)) refClk syncRst + $ allStable + .&&. fifoSuccess + + testDone :: Signal Basic125 Bool + testDone = + startTest + .&&. ( skip + .||. endSuccess + .||. transceiversFailedAfterUp + .||. startBeforeAllReady + .||. (not <$> fifoSuccess) + ) + + testSuccess :: Signal Basic125 Bool + testSuccess = + skip + .||. ( allStable + .&&. fifoSuccess + .&&. (not <$> (transceiversFailedAfterUp .||. startBeforeAllReady)) + ) +{-# OPAQUE demoTest #-} + +makeTopEntity 'demoTest + +type DemoTopologySize = 3 + +tests :: [HitlTestGroup] +tests = [testGroup] + where + ClockControlConfig{..} = clockControlConfig + + defSimCfg :: CcConf + defSimCfg = + def + { samples = 1000 + , duration = natToNum @(PeriodToCycles Basic125 (Seconds 60)) + , stabilityMargin = snatToNum cccStabilityCheckerMargin + , stabilityFrameSize = snatToNum cccStabilityCheckerFramesize + , waitTime = fromEnum cccReframingWaitTime + , stopAfterStable = + Just + $ natToNum @(PeriodToCycles Basic125 AllStablePeriod) + } + + demoTopology :: Topology DemoTopologySize + demoTopology = complete SNat + + testData :: + forall n. + (KnownNat n, n <= FpgaCount) => + Index n -> + BitVector LinkCount -> + (HwTargetRef, TestConfig) + testData i mask = + ( HwTargetByIndex (fromIntegral i) + , TestConfig{fpgaEnabled = True, ..} + ) + + testCase :: HitlTestCase HwTargetRef TestConfig CcConf + testCase = + HitlTestCase + { name = "Bittide Demo DUT" + , parameters = + Map.fromList + $ toList (zipWith testData indicesI (linkMasks demoTopology)) + <> [ (HwTargetByIndex (fromInteger i), disabled) + | let n = topologySize demoTopology + , i <- [n, n + 1 .. natToNum @LinkCount] + ] + , postProcData = + defSimCfg + { ccTopologyType = Complete $ natToInteger @DemoTopologySize + , clockOffsets = Nothing + , startupDelays = toList $ repeat @DemoTopologySize 0 + } + } + + topologySize :: + forall n. + (KnownNat n) => + Topology n -> + Integer + topologySize _ = natToNum @n + + testGroup :: HitlTestGroup + testGroup = + HitlTestGroup + { topEntity = 'demoTest + , extraXdcFiles = ["jtag" "config.xdc", "jtag" "pmod1.xdc"] + , externalHdl = [] + , testCases = [testCase] + , mDriverProc = Just D.driverFunc + , mPostProc = Nothing + } diff --git a/bittide-instances/src/Bittide/Instances/Hitl/Driver/Demo.hs b/bittide-instances/src/Bittide/Instances/Hitl/Driver/Demo.hs new file mode 100644 index 000000000..29186e976 --- /dev/null +++ b/bittide-instances/src/Bittide/Instances/Hitl/Driver/Demo.hs @@ -0,0 +1,314 @@ +-- SPDX-FileCopyrightText: 2025 Google LLC +-- +-- SPDX-License-Identifier: Apache-2.0 +{-# LANGUAGE FlexibleContexts #-} +{-# LANGUAGE NumericUnderscores #-} +{-# LANGUAGE OverloadedRecordDot #-} +{-# LANGUAGE QuasiQuotes #-} + +module Bittide.Instances.Hitl.Driver.Demo where + +import Clash.Prelude + +import Bittide.Hitl +import Bittide.Instances.Hitl.Setup (demoRigInfo) +import Bittide.Instances.Hitl.Utils.Program +import Bittide.Instances.Hitl.Utils.Vivado + +import Control.Exception (catch) +import Control.Monad (zipWithM) +import Control.Monad.IO.Class +import Data.Maybe (fromMaybe) +import Data.String.Interpolate (i) +import Numeric (showHex) +import Project.FilePath +import Project.Handle +import System.Clock (Clock (Monotonic), diffTimeSpec, getTime, toNanoSecs) +import System.Exit +import System.FilePath +import System.IO +import Vivado.Tcl (HwTarget) +import Vivado.VivadoM + +import qualified Bittide.Instances.Hitl.Utils.Gdb as Gdb +import qualified Control.Monad.Trans.Control as CMTC +import qualified Data.List as L +import qualified System.Timeout.Lifted as STL + +-- connect EBs over WB +-- read/write regs with MU, ensure that correct values show up +-- check over GDB too +-- no input data +-- just one test case + +data OcdInitData = OcdInitData + { muPort :: Int + , ccPort :: Int + , handles :: ProcessStdIoHandles + , cleanup :: IO () + } + +data TestStatus = TestRunning | TestDone Bool | TestTimeout deriving (Eq) + +driverFunc :: + String -> + [(HwTarget, DeviceInfo)] -> + VivadoM ExitCode +driverFunc testName targets = do + liftIO + . putStrLn + $ "Running driver function for targets " + <> show ((\(_, info) -> info.deviceId) <$> targets) + + startTime <- liftIO $ getTime Monotonic + projectDir <- liftIO $ findParentContaining "cabal.project" + + let + hitlDir = projectDir "_build/hitl/" <> testName + + calcTimeSpentMs = (`div` 1_000_000) . toNanoSecs . diffTimeSpec startTime <$> getTime Monotonic + + getTargetIndex :: HwTarget -> Int + getTargetIndex hwT = fromMaybe 9 $ L.findIndex (\di -> di.deviceId == idFromHwT hwT) demoRigInfo + + tryWithTimeout :: + forall m a. + (CMTC.MonadBaseControl IO m, MonadFail m) => + String -> + Int -> + m a -> + m a + tryWithTimeout actionName dur action = do + result <- STL.timeout dur action + case result of + Nothing -> do + fail $ "Timeout while performing action: " <> actionName + Just r -> pure r + + getHandshakesStatus :: [(HwTarget, DeviceInfo)] -> [Bool] -> VivadoM [Bool] + getHandshakesStatus [] _ = return [] + getHandshakesStatus _ [] = return [] + getHandshakesStatus ((hwT, d) : hwtdRest) (handsShaken : hsRest) = do + let getRest = getHandshakesStatus hwtdRest hsRest + if handsShaken + then getRest + else do + openHardwareTarget hwT + vals <- readVio "vioHitlt" ["probe_handshakes_done"] + let + getResult = + case vals of + [("probe_handshakes_done", "1")] -> do + liftIO + $ putStrLn + $ "!!!!! Handshake completed on device " + <> show d.deviceId + <> " (" + <> show (getTargetIndex hwT) + <> ") !!!!!" + return True + _ -> return False + result <- getResult + rest <- getRest + return $ result : rest + + awaitHandshakes :: VivadoM () + awaitHandshakes = do + let + innerInit = L.repeat False + inner prev = do + new <- getHandshakesStatus targets prev + if and new + then return () + else inner new + inner innerInit + + initOpenOcds :: (HwTarget, DeviceInfo) -> Int -> IO OcdInitData + initOpenOcds (_, d) targetIndex = do + putStrLn $ "Starting OpenOCD for target " <> show d.deviceId + + let + gdbPortMU = 3333 + targetIndex * 2 + gdbPortCC = gdbPortMU + 1 + tclPortMU = 6666 + targetIndex * 2 + tclPortCC = tclPortMU + 1 + telnetPortMU = 4444 + targetIndex * 2 + telnetPortCC = telnetPortMU + 1 + ocdStdout = hitlDir "openocd-" <> show targetIndex <> "-stdout.log" + ocdStderr = hitlDir "openocd-" <> show targetIndex <> "-stderr.log" + putStrLn $ "logging OpenOCD stdout to `" <> ocdStdout <> "`" + putStrLn $ "logging OpenOCD stderr to `" <> ocdStderr <> "`" + + putStrLn "Starting OpenOCD..." + (ocd, ocdPh, ocdClean0) <- + startOpenOcdWithEnvAndArgs + ["-f", "sipeed.tcl", "-f", "vexriscv-2chain.tcl"] + [ ("OPENOCD_STDOUT_LOG", ocdStdout) + , ("OPENOCD_STDERR_LOG", ocdStderr) + , ("USB_DEVICE", d.usbAdapterLocation) + , ("DEV_A_GDB", show gdbPortMU) + , ("DEV_B_GDB", show gdbPortCC) + , ("DEV_A_TCL", show tclPortMU) + , ("DEV_B_TCL", show tclPortCC) + , ("DEV_A_TEL", show telnetPortMU) + , ("DEV_B_TEL", show telnetPortCC) + ] + hSetBuffering ocd.stderrHandle LineBuffering + flip + catch + ( \(e :: IOError) -> do + putStrLn $ "Failed on reading OpenOCD stderr: " <> show e + flip catch (\(_ :: IOError) -> return ()) $ do + so <- hGetContents ocd.stdoutHandle + putStrLn $ "OpenOCD leftover stdout:\n" <> so + flip catch (\(_ :: IOError) -> return ()) $ do + se <- hGetContents ocd.stderrHandle + putStrLn $ "OpenOCD leftover stderr:\n" <> se + fail (show e) + ) + $ tryWithTimeout "Waiting for OpenOCD to start" 15_000_000 + $ expectLine ocd.stderrHandle openOcdWaitForHalt + + let + ocdProcName = "OpenOCD (" <> show d.deviceId <> ")" + ocdClean1 = do + flip catch (\(e :: IOError) -> putStrLn $ "Failed to read from stdout: " <> show e) $ do + so <- hGetContents ocd.stdoutHandle + putStrLn $ "OpenOCD leftover stdout:\n" <> so + flip catch (\(e :: IOError) -> putStrLn $ "Failed to read from stderr: " <> show e) $ do + se <- hGetContents ocd.stderrHandle + putStrLn $ "OpenOCD leftover stderr:\n" <> se + ocdClean0 + awaitProcessTermination ocdProcName ocdPh (Just 5_000_000) + + return $ OcdInitData gdbPortMU gdbPortCC ocd ocdClean1 + + initGdbs :: String -> Int -> (HwTarget, DeviceInfo) -> IO (ProcessStdIoHandles, IO ()) + initGdbs binName gdbPort (hwT, d) = do + putStrLn $ "Starting GDB for target " <> show d.deviceId <> " with bin name " <> binName + + (gdb, gdbPh, gdbClean0) <- Gdb.startGdbH + hSetBuffering gdb.stdinHandle LineBuffering + + Gdb.setLogging gdb $ hitlDir + "gdb-" <> binName <> "-" <> show (getTargetIndex hwT) <> ".log" + Gdb.setFile gdb $ firmwareBinariesDir "riscv32imc" Release binName + Gdb.setTarget gdb gdbPort + + let + gdbProcName = "GDB (" <> binName <> ", " <> show d.deviceId <> ")" + gdbClean1 = gdbClean0 >> awaitProcessTermination gdbProcName gdbPh (Just 5_000_000) + + return (gdb, gdbClean1) + + getTestsStatus :: + [(HwTarget, DeviceInfo)] -> [TestStatus] -> Integer -> VivadoM [TestStatus] + getTestsStatus [] _ _ = return [] + getTestsStatus _ [] _ = return [] + getTestsStatus ((hwT, _) : hwtdRest) (status : statusRest) dur = do + let getRestStatus = getTestsStatus hwtdRest statusRest dur + case status of + TestRunning -> do + timeSpent <- liftIO $ calcTimeSpentMs + if timeSpent < dur + then do + openHardwareTarget hwT + vals <- readVio "vioHitlt" ["probe_test_done", "probe_test_success"] + rest <- getRestStatus + return $ case vals of + [("probe_test_done", "1"), ("probe_test_success", success)] -> + TestDone (success == "1") : rest + _ -> TestRunning : rest + else do + rest <- getRestStatus + return $ TestTimeout : rest + other -> do + rest <- getRestStatus + return $ other : rest + + awaitTestCompletions :: Integer -> VivadoM [ExitCode] + awaitTestCompletions dur = do + let + innerInit = L.repeat TestRunning + inner prev = do + new <- getTestsStatus targets prev dur + if not (TestRunning `L.elem` new) + then do + let + go :: (HwTarget, DeviceInfo) -> TestStatus -> IO ExitCode + go (_, d) status = case status of + TestDone True -> do + putStrLn $ "Test passed on target " <> show d.deviceId + return ExitSuccess + TestDone False -> do + putStrLn $ "Test finished unsuccessfully on target " <> show d.deviceId + return $ ExitFailure 2 + _ -> do + putStrLn $ "Test timed out on target " <> show d.deviceId + return $ ExitFailure 2 + liftIO $ zipWithM go targets new + else inner new + inner innerInit + + foldExitCodes :: VivadoM (Int, ExitCode) -> ExitCode -> VivadoM (Int, ExitCode) + foldExitCodes prev code = do + (count, acc) <- prev + return + $ if code == ExitSuccess + then (count + 1, acc) + else (count, code) + + muGdbCheck :: (HwTarget, DeviceInfo) -> ProcessStdIoHandles -> VivadoM ExitCode + muGdbCheck (_, d) gdb = do + let + devIdString = show d.deviceId + expectedDna = showHex d.dna "" + -- Need to find out what the appropriate invocation is for GDB to get the correct string + -- out from MMIO. + liftIO $ Gdb.runCommands gdb.stdinHandle ["x/12sb 0xa0000000", "echo END OF DNA\\n"] + gdbRead <- + liftIO + $ tryWithTimeout "Reading DNA over GDB" 60_000_000 + $ readUntil gdb.stdoutHandle "END OF DNA" + -- Also need to find out what the output looks like in order to write a proper comparison. + -- For now, just print what each of them are. + liftIO $ putStrLn [i|Expected DNA for device #{devIdString}: #{expectedDna}|] + liftIO $ putStrLn [i|Output from DNA probe:\n#{gdbRead}|] + return ExitSuccess + + tryWithTimeout "Wait for handshakes successes from all boards" 15_000_000 awaitHandshakes + brackets (liftIO <$> L.zipWith initOpenOcds targets [0 ..]) (liftIO . (.cleanup)) $ \initOcdsData -> do + let + muPorts = (.muPort) <$> initOcdsData + ccPorts = (.ccPort) <$> initOcdsData + brackets + (liftIO <$> L.zipWith (initGdbs "clock-control") ccPorts targets) + (liftIO . snd) + $ \initCCGdbsData -> do + let ccGdbs = fst <$> initCCGdbsData + liftIO $ mapM_ ((errorToException =<<) . Gdb.loadBinary) ccGdbs + brackets + (liftIO <$> L.zipWith (initGdbs "management-unit") muPorts targets) + (liftIO . snd) + $ \initMUGdbsData -> do + let muGdbs = fst <$> initMUGdbsData + liftIO $ mapM_ ((errorToException =<<) . Gdb.loadBinary) muGdbs + + testResults <- awaitTestCompletions 60_000 + (sCount, stabilityExitCode) <- + L.foldl foldExitCodes (pure (0, ExitSuccess)) testResults + liftIO + $ putStrLn + [i|Test case #{testName} stabilised on #{sCount} of #{L.length targets} targets|] + liftIO $ putStrLn "Checking for MMIO access over GDB..." + gdbExitCodes <- zipWithM muGdbCheck targets ccGdbs + (gdbCount, gdbExitCode) <- + L.foldl foldExitCodes (pure (0, ExitSuccess)) gdbExitCodes + liftIO + $ putStrLn + [i|GDB testing passed on #{gdbCount} of #{L.length targets} targets|] + let + finalExit = + fromMaybe ExitSuccess + $ L.find (/= ExitSuccess) [stabilityExitCode, gdbExitCode] + return finalExit diff --git a/bittide-instances/src/Bittide/Instances/Hitl/Tests.hs b/bittide-instances/src/Bittide/Instances/Hitl/Tests.hs index b6f623063..c3dfac1a2 100644 --- a/bittide-instances/src/Bittide/Instances/Hitl/Tests.hs +++ b/bittide-instances/src/Bittide/Instances/Hitl/Tests.hs @@ -21,6 +21,7 @@ import Bittide.Hitl (ClashTargetName, HitlTestCase (..), HitlTestGroup (..)) import Prelude import qualified Bittide.Instances.Hitl.BoardTest as BoardTest +import qualified Bittide.Instances.Hitl.Demo as Demo import qualified Bittide.Instances.Hitl.DnaOverSerial as DnaOverSerial import qualified Bittide.Instances.Hitl.Ethernet as Ethernet import qualified Bittide.Instances.Hitl.FincFdec as FincFdec @@ -36,12 +37,13 @@ hitlTests = [] <> [BoardTest.testSimple] <> [BoardTest.testExtended] + <> Demo.tests <> [DnaOverSerial.tests] <> [Ethernet.tests] <> [FincFdec.tests] <> [LinkConfiguration.tests] + <> SwCcTopologies.tests <> [SyncInSyncOut.tests] <> [TemperatureMonitor.tests] - <> SwCcTopologies.tests <> [Transceivers.tests] <> [VexRiscv.tests] diff --git a/bittide-instances/src/Bittide/Instances/Hitl/Utils/Program.hs b/bittide-instances/src/Bittide/Instances/Hitl/Utils/Program.hs index baf3d782e..615ddfc58 100644 --- a/bittide-instances/src/Bittide/Instances/Hitl/Utils/Program.hs +++ b/bittide-instances/src/Bittide/Instances/Hitl/Utils/Program.hs @@ -101,25 +101,31 @@ startOpenOcdWithEnv :: -- | Telnet port Int -> IO (ProcessStdIoHandles, ProcessHandle, IO ()) -startOpenOcdWithEnv extraEnv usbLoc gdbPort tclPort telnetPort = do +startOpenOcdWithEnv extraEnv usbLoc gdbPort tclPort telnetPort = + startOpenOcdWithEnvAndArgs + ["-f", "ports.tcl", "-f", "sipeed.tcl", "-f", "vexriscv_init.tcl"] + ( [ ("USB_DEVICE", usbLoc) + , ("GDB_PORT", show gdbPort) + , ("TCL_PORT", show tclPort) + , ("TELNET_PORT", show telnetPort) + ] + <> extraEnv + ) + +startOpenOcdWithEnvAndArgs :: + [String] -> + [(String, String)] -> + IO (ProcessStdIoHandles, ProcessHandle, IO ()) +startOpenOcdWithEnvAndArgs args extraEnv = do startOpenOcdPath <- getOpenOcdStartPath currentEnv <- getEnvironment let openOcdProc = - (proc startOpenOcdPath []) + (proc startOpenOcdPath args) { std_in = CreatePipe , std_out = CreatePipe , std_err = CreatePipe - , env = - Just - ( currentEnv - <> extraEnv - <> [ ("USB_DEVICE", usbLoc) - , ("GDB_PORT", show gdbPort) - , ("TCL_PORT", show tclPort) - , ("TELNET_PORT", show telnetPort) - ] - ) + , env = Just (currentEnv <> extraEnv) } ocdHandles@(openOcdStdin, openOcdStdout, openOcdStderr, openOcdPh) <- diff --git a/bittide-instances/src/Bittide/Instances/Hitl/VexRiscv.hs b/bittide-instances/src/Bittide/Instances/Hitl/VexRiscv.hs index 64d043fa0..2f192d4b3 100644 --- a/bittide-instances/src/Bittide/Instances/Hitl/VexRiscv.hs +++ b/bittide-instances/src/Bittide/Instances/Hitl/VexRiscv.hs @@ -159,6 +159,7 @@ vexRiscvInner jtagIn0 uartRx = , initD = Reloadable (Vec dMem) , iBusTimeout = d0 -- No timeouts on the instruction bus , dBusTimeout = d0 -- No timeouts on the data bus + , includeIlaWb = True } peConfigRtl = PeConfig @@ -167,6 +168,7 @@ vexRiscvInner jtagIn0 uartRx = , initD = Undefined @IMemWords , iBusTimeout = d0 , dBusTimeout = d0 + , includeIlaWb = True } type DMemWords = DivRU (64 * 1024) 4 diff --git a/bittide-instances/src/Bittide/Instances/Pnr/Ethernet.hs b/bittide-instances/src/Bittide/Instances/Pnr/Ethernet.hs index 03dd12aa2..41cd4b8ea 100644 --- a/bittide-instances/src/Bittide/Instances/Pnr/Ethernet.hs +++ b/bittide-instances/src/Bittide/Instances/Pnr/Ethernet.hs @@ -172,6 +172,7 @@ vexRiscGmii SNat sysClk sysRst rxClk rxRst txClk txRst fwd = , initD = Reloadable (Vec dMem) , iBusTimeout = d0 , dBusTimeout = d0 + , includeIlaWb = True } peConfigRtl = @@ -181,6 +182,7 @@ vexRiscGmii SNat sysClk sysRst rxClk rxRst txClk txRst fwd = , initD = Undefined @DMemWords , iBusTimeout = d0 , dBusTimeout = d0 + , includeIlaWb = True } type DMemWords = DivRU (256 * 1024) 4 diff --git a/bittide-instances/src/Bittide/Instances/Pnr/ProcessingElement.hs b/bittide-instances/src/Bittide/Instances/Pnr/ProcessingElement.hs index b28b80b9d..c3ea74163 100644 --- a/bittide-instances/src/Bittide/Instances/Pnr/ProcessingElement.hs +++ b/bittide-instances/src/Bittide/Instances/Pnr/ProcessingElement.hs @@ -85,6 +85,7 @@ vexRiscUartHello diffClk rst_in = , initD = Reloadable $ Blob dMem , iBusTimeout = d0 -- No timeouts on the instruction bus , dBusTimeout = d0 -- No timeouts on the data bus + , includeIlaWb = True } makeTopEntity 'vexRiscUartHello diff --git a/bittide-instances/src/Project/Handle.hs b/bittide-instances/src/Project/Handle.hs index 81c65cb24..6b762a402 100644 --- a/bittide-instances/src/Project/Handle.hs +++ b/bittide-instances/src/Project/Handle.hs @@ -40,7 +40,7 @@ expectLine = expectLine' "" let byteLine1 = filter (\c -> isAscii c && not (isControl c)) byteLine0 line = w2c <$> unpack byteLine1 - trimmed = trimEnd line + trimmed = (\l -> trace ("expectLine: " <> l) l) $ trimEnd line s1 = s0 <> "\n" <> line cont = expectLine' s1 h f if null trimmed diff --git a/bittide-instances/tests/Tests/ClockControlWb.hs b/bittide-instances/tests/Tests/ClockControlWb.hs index ec8a6ce86..24e4e13e9 100644 --- a/bittide-instances/tests/Tests/ClockControlWb.hs +++ b/bittide-instances/tests/Tests/ClockControlWb.hs @@ -167,6 +167,7 @@ dut = , initD = Reloadable $ Blob dMem , iBusTimeout = d0 -- No timeouts on the instruction bus , dBusTimeout = d0 -- No timeouts on the data bus + , includeIlaWb = True } -- | Parse the output of the UART diff --git a/bittide-instances/tests/Wishbone/Axi.hs b/bittide-instances/tests/Wishbone/Axi.hs index 5539a9eba..7bd3930ba 100644 --- a/bittide-instances/tests/Wishbone/Axi.hs +++ b/bittide-instances/tests/Wishbone/Axi.hs @@ -102,6 +102,7 @@ dut = , initD = Reloadable $ Blob dMem , iBusTimeout = d0 -- No timeouts on the instruction bus , dBusTimeout = d0 -- No timeouts on the data bus + , includeIlaWb = True } data TestResult = TestResult String (Maybe String) deriving (Show, Eq) diff --git a/bittide-instances/tests/Wishbone/CaptureUgn.hs b/bittide-instances/tests/Wishbone/CaptureUgn.hs index e57e3fb2f..e9fa222e6 100644 --- a/bittide-instances/tests/Wishbone/CaptureUgn.hs +++ b/bittide-instances/tests/Wishbone/CaptureUgn.hs @@ -114,6 +114,7 @@ dut eb localCounter = circuit $ do , initD = Reloadable $ Blob dMem , iBusTimeout = d0 -- No timeouts on the instruction bus , dBusTimeout = d0 -- No timeouts on the data bus + , includeIlaWb = True } {- | Simulation function which matches the remote counter to the correct sample diff --git a/bittide-instances/tests/Wishbone/DnaPortE2.hs b/bittide-instances/tests/Wishbone/DnaPortE2.hs index 7c50e178e..1c32234a8 100644 --- a/bittide-instances/tests/Wishbone/DnaPortE2.hs +++ b/bittide-instances/tests/Wishbone/DnaPortE2.hs @@ -80,6 +80,7 @@ dut = circuit $ \_unit -> do , initD = Reloadable $ Blob dMem , iBusTimeout = d0 -- No timeouts on the instruction bus , dBusTimeout = d0 -- No timeouts on the data bus + , includeIlaWb = True } parseResult :: String -> BitVector 96 diff --git a/bittide-instances/tests/Wishbone/ScatterGather.hs b/bittide-instances/tests/Wishbone/ScatterGather.hs index ec96f19cd..a162b434f 100644 --- a/bittide-instances/tests/Wishbone/ScatterGather.hs +++ b/bittide-instances/tests/Wishbone/ScatterGather.hs @@ -105,6 +105,7 @@ dut scatterConfig gatherConfig = circuit $ do , initD = Reloadable (Vec dMem) , iBusTimeout = d0 -- No timeouts on the instruction bus , dBusTimeout = d0 -- No timeouts on the data bus + , includeIlaWb = True } type DMemWords = DivRU (64 * 1024) 4 diff --git a/bittide-instances/tests/Wishbone/Time.hs b/bittide-instances/tests/Wishbone/Time.hs index 1b2ee1f61..3ecc11a15 100644 --- a/bittide-instances/tests/Wishbone/Time.hs +++ b/bittide-instances/tests/Wishbone/Time.hs @@ -90,6 +90,7 @@ dut = withClockResetEnable clockGen resetGen enableGen , initD = Reloadable $ Blob dMem , iBusTimeout = d0 -- No timeouts on the instruction bus , dBusTimeout = d0 -- No timeouts on the data bus + , includeIlaWb = True } data TestResult = TestResult String (Maybe String) deriving (Show) diff --git a/bittide-instances/tests/Wishbone/Watchdog.hs b/bittide-instances/tests/Wishbone/Watchdog.hs index 0f9b9f474..78b4c1d14 100644 --- a/bittide-instances/tests/Wishbone/Watchdog.hs +++ b/bittide-instances/tests/Wishbone/Watchdog.hs @@ -83,6 +83,7 @@ dut = withClockResetEnable clockGen resetGen enableGen , initD = Reloadable (Vec dMem) , iBusTimeout = d0 -- No timeouts on the instruction bus , dBusTimeout = d0 -- No timeouts on the data bus + , includeIlaWb = True } type DMemWords = DivRU (32 * 1024) 4 diff --git a/bittide/src/Bittide/ClockControl/Callisto/Types.hs b/bittide/src/Bittide/ClockControl/Callisto/Types.hs index 33a1441b0..1c2f30e3d 100644 --- a/bittide/src/Bittide/ClockControl/Callisto/Types.hs +++ b/bittide/src/Bittide/ClockControl/Callisto/Types.hs @@ -8,6 +8,7 @@ module Bittide.ClockControl.Callisto.Types ( CallistoResult (..), + CallistoCResult (..), ReframingState (..), ControlConfig (..), ControlSt (..), @@ -40,6 +41,24 @@ data CallistoResult (n :: Nat) = CallistoResult } deriving (Generic, NFDataX) +-- | Result of the clock control algorithm. +data CallistoCResult (n :: Nat) = CallistoCResult + { maybeSpeedChangeC :: Maybe SpeedChange + -- ^ Speed change requested for clock multiplier. This is 'Just' for a single + -- cycle. + , stabilityC :: Vec n StabilityIndication + -- ^ All stability indicators for all of the elastic buffers. + , allStableC :: Bool + -- ^ Joint stability indicator signaling that all elastic buffers + -- are stable. + , allSettledC :: Bool + -- ^ Joint "being-settled" indicator signaling that all elastic + -- buffers have been settled. + , reframingStateC :: ReframingState + -- ^ State of the Reframing detector + } + deriving (Generic, NFDataX) + -- | Callisto specific control configuration options. data ControlConfig (m :: Nat) = ControlConfig { reframingEnabled :: Bool @@ -89,5 +108,6 @@ data ControlSt = ControlSt deriving (Generic, NFDataX) deriveSignalHasFields ''CallistoResult +deriveSignalHasFields ''CallistoCResult deriveSignalHasFields ''ControlConfig deriveSignalHasFields ''ControlSt diff --git a/bittide/src/Bittide/ClockControl/CallistoSw.hs b/bittide/src/Bittide/ClockControl/CallistoSw.hs index 727852d06..d102fe199 100644 --- a/bittide/src/Bittide/ClockControl/CallistoSw.hs +++ b/bittide/src/Bittide/ClockControl/CallistoSw.hs @@ -9,7 +9,10 @@ module Bittide.ClockControl.CallistoSw ( callistoSwClockControl, + callistoSwClockControlC, SwControlConfig (..), + SwControlCConfig (..), + SwcccRemBusWidth, ) where import Clash.Prelude hiding (PeriodToCycles) @@ -18,18 +21,25 @@ import Clash.Cores.Xilinx.Ila (Depth (..), IlaConfig (..), ila, ilaConfig) import Data.Maybe (fromMaybe, isJust) import Protocols import Protocols.Idle -import VexRiscv (DumpVcd (NoDumpVcd), JtagIn) +import Protocols.Wishbone +import VexRiscv import Bittide.CircuitUtils import Bittide.ClockControl (RelDataCount) -import Bittide.ClockControl.Callisto.Types (CallistoResult (..), ReframingState (Done)) +import Bittide.ClockControl.Callisto.Types ( + CallistoCResult (CallistoCResult), + CallistoResult (CallistoResult), + ReframingState (Done), + ) import Bittide.ClockControl.DebugRegister ( DebugRegisterCfg (DebugRegisterCfg), + DebugRegisterData, debugRegisterWb, ) import Bittide.ClockControl.Registers (ClockControlData (..), clockControlWb) import Bittide.DoubleBufferedRam (InitialContent (Undefined)) -import Bittide.ProcessingElement (PeConfig (..), processingElement) +import Bittide.ProcessingElement (PeConfig (..), processingElement, splitAtC) +import Bittide.SharedTypes -- | Configuration type for software clock control. data SwControlConfig dom mgn fsz where @@ -133,4 +143,116 @@ callistoSwClockControl (SwControlConfig jtagIn reframe mgn fsz) mask ebs = , initD = Undefined @(Div (64 * 1024) 4) , iBusTimeout = d0 -- No timeouts on the instruction bus , dBusTimeout = d0 -- No timeouts on the data bus + , includeIlaWb = True } + +type SwcccInternalBusses = 4 +type SwcccRemBusWidth n = 30 - CLog 2 (n + SwcccInternalBusses) + +data SwControlCConfig mgn fsz otherWb where + SwControlCConfig :: + ( KnownNat mgn + , KnownNat fsz + , KnownNat otherWb + , 1 <= fsz + ) => + SNat mgn -> + SNat fsz -> + PeConfig (otherWb + 4) -> + SwControlCConfig mgn fsz otherWb + +callistoSwClockControlC :: + forall nLinks eBufBits dom margin framesize otherWb. + ( HiddenClockResetEnable dom + , KnownNat nLinks + , KnownNat eBufBits + , KnownNat otherWb + , 1 <= nLinks + , 1 <= eBufBits + , nLinks + eBufBits <= 32 + , 1 <= framesize + , CLog 2 (otherWb + SwcccInternalBusses) <= 30 + , 1 <= DomainPeriod dom + ) => + DumpVcd -> + SwControlCConfig margin framesize otherWb -> + Circuit + ( Jtag dom + , CSignal dom Bool -- reframing enable + , CSignal dom (BitVector nLinks) -- link mask + , Vec nLinks (CSignal dom (RelDataCount eBufBits)) -- diff counters + ) + ( CSignal dom (CallistoCResult nLinks) + , Vec otherWb (Wishbone dom 'Standard (SwcccRemBusWidth otherWb) (Bytes 4)) + ) +callistoSwClockControlC dumpVcd (SwControlCConfig mgn fsz peConfig) = Circuit go + where + go :: + ( Fwd + ( Jtag dom + , CSignal dom Bool + , CSignal dom (BitVector nLinks) + , Vec nLinks (CSignal dom (RelDataCount eBufBits)) + ) + , Bwd + ( CSignal dom (CallistoCResult nLinks) + , Vec otherWb (Wishbone dom 'Standard (SwcccRemBusWidth otherWb) (Bytes 4)) + ) + ) -> + ( Bwd + ( Jtag dom + , CSignal dom Bool + , CSignal dom (BitVector nLinks) + , Vec nLinks (CSignal dom (RelDataCount eBufBits)) + ) + , Fwd + ( CSignal dom (CallistoCResult nLinks) + , Vec otherWb (Wishbone dom 'Standard (SwcccRemBusWidth otherWb) (Bytes 4)) + ) + ) + go ((jtagIn, reframe, linkMask, diffCounters), (_, otherS2M)) = + ( (jtagOut, pure (), pure (), repeat $ pure ()) + , (callistoCResult, otherM2S) + ) + where + debugRegisterCfg :: Signal dom DebugRegisterCfg + debugRegisterCfg = DebugRegisterCfg <$> reframe + + peFn :: + ( Fwd (Jtag dom) + , Bwd + ( CSignal dom (ClockControlData nLinks) + , CSignal dom DebugRegisterData + , Vec otherWb (Wishbone dom 'Standard (SwcccRemBusWidth otherWb) (Bytes 4)) + ) + ) -> + ( Bwd (Jtag dom) + , Fwd + ( CSignal dom (ClockControlData nLinks) + , CSignal dom DebugRegisterData + , Vec otherWb (Wishbone dom 'Standard (SwcccRemBusWidth otherWb) (Bytes 4)) + ) + ) + Circuit peFn = circuit $ \jtag -> do + allWishbone <- processingElement dumpVcd peConfig -< jtag + ([wbClockControl, wbDebug], wbRest) <- splitAtC d2 -< allWishbone + [ccd0, ccd1] <- + cSignalDupe + <| clockControlWb mgn fsz linkMask diffCounters + -< wbClockControl + cm <- cSignalMap clockMod -< ccd0 + dbg <- debugRegisterWb debugRegisterCfg -< (wbDebug, cm) + idC -< (ccd1, dbg, wbRest) + + (jtagOut, (clockControlData, debugData, otherM2S)) = peFn (jtagIn, (pure (), pure (), otherS2M)) + + resultRfs = fromMaybe Done <$> debugData.reframingState + + callistoCResult :: Signal dom (CallistoCResult nLinks) + callistoCResult = + CallistoCResult + <$> clockControlData.clockMod + <*> clockControlData.stabilityIndications + <*> clockControlData.allStable + <*> clockControlData.allSettled + <*> resultRfs diff --git a/bittide/src/Bittide/Node.hs b/bittide/src/Bittide/Node.hs index 5f63f21fd..724c2c166 100644 --- a/bittide/src/Bittide/Node.hs +++ b/bittide/src/Bittide/Node.hs @@ -13,7 +13,7 @@ import Clash.Sized.Vector.ToTuple (vecToTuple) import Protocols import Protocols.Idle -import Protocols.Internal (vecCircuits) +import Protocols.Vec (vecCircuits) import Protocols.Wishbone import VexRiscv @@ -40,8 +40,8 @@ simpleNodeConfig = where switchCal = CalendarConfig (SNat @1024) (switchEntry :> Nil) (switchEntry :> Nil) sgConfig = CalendarConfig (SNat @1024) (sgEntry :> Nil) (sgEntry :> Nil) - peConfig = PeConfig memMapPe (Undefined @8192) (Undefined @8192) d0 d0 - nmuConfig = PeConfig memMapNmu (Undefined @8192) (Undefined @8192) d0 d0 + peConfig = PeConfig memMapPe (Undefined @8192) (Undefined @8192) d0 d0 True + nmuConfig = PeConfig memMapNmu (Undefined @8192) (Undefined @8192) d0 d0 False memMapPe = iterateI (+ 0x1000) 0 memMapNmu = iterateI (+ 0x1000) 0 switchEntry = ValidEntry{veEntry = repeat 0, veRepeat = 0 :: Unsigned 0} diff --git a/bittide/src/Bittide/ProcessingElement.hs b/bittide/src/Bittide/ProcessingElement.hs index a22193a4a..d76f27398 100644 --- a/bittide/src/Bittide/ProcessingElement.hs +++ b/bittide/src/Bittide/ProcessingElement.hs @@ -47,6 +47,8 @@ data PeConfig nBusses where , dBusTimeout :: SNat dBusTimeout -- ^ Number of clock cycles after which the a transaction on the data bus times out. -- Set to 0 to disable timeouts on the data bus. + , includeIlaWb :: Bool + -- ^ Prefix for the Wishbone ILA component probes. } -> PeConfig nBusses @@ -61,13 +63,26 @@ processingElement :: Circuit (Jtag dom) (Vec (nBusses - 2) (Wishbone dom 'Standard (MappedBusAddrWidth 30 nBusses) (Bytes 4))) -processingElement dumpVcd PeConfig{memMapConfig, initI, initD, iBusTimeout, dBusTimeout} = circuit $ \jtagIn -> do +processingElement dumpVcd PeConfig{memMapConfig, initI, initD, iBusTimeout, dBusTimeout, includeIlaWb} = circuit $ \jtagIn -> do (iBus0, dBus0) <- rvCircuit dumpVcd (pure low) (pure low) (pure low) -< jtagIn iBus1 <- - ilaWb (SSymbol @"instructionBus") 2 D4096 onTransactionWb onTransactionWb -< iBus0 + maybeIlaWb + includeIlaWb + (SSymbol @"instructionBus") + 2 + D4096 + onTransactionWb + onTransactionWb + -< iBus0 dBus1 <- watchDogWb "dBus" iBusTimeout - <| ilaWb (SSymbol @"dataBus") 2 D4096 onTransactionWb onTransactionWb + <| maybeIlaWb + includeIlaWb + (SSymbol @"dataBus") + 2 + D4096 + onTransactionWb + onTransactionWb -< dBus0 ([iMemBus, dMemBus], extBusses) <- (splitAtC d2 <| singleMasterInterconnect memMapConfig) -< dBus1 diff --git a/bittide/src/Bittide/Transceiver.hs b/bittide/src/Bittide/Transceiver.hs index 02e4b2530..164b99b1c 100644 --- a/bittide/src/Bittide/Transceiver.hs +++ b/bittide/src/Bittide/Transceiver.hs @@ -178,6 +178,8 @@ data Outputs n tx rx txS free = Outputs -- ^ See 'Output.txReady' , txSamplings :: Vec n (Signal tx Bool) -- ^ See 'Output.txSampling' + , handshakesCompleteTx :: Vec n (Signal tx Bool) + -- ^ See 'Output.handshakeCompleteTx' , txPs :: Signal txS (BitVector n) -- ^ See 'Output.txP' , txNs :: Signal txS (BitVector n) @@ -188,10 +190,14 @@ data Outputs n tx rx txS free = Outputs -- ^ See 'Output.rxReset' , rxDatas :: Vec n (Signal rx (Maybe (BitVector 64))) -- ^ See 'Output.rxData' + , handshakesComplete :: Vec n (Signal rx Bool) + -- ^ See 'Output.handshakeComplete' , linkUps :: Vec n (Signal free Bool) -- ^ See 'Output.linkUp' , linkReadys :: Vec n (Signal free Bool) -- ^ See 'Output.linkReady' + , handshakesCompleteFree :: Vec n (Signal free Bool) + -- ^ See 'Output.handshakeCompleteFree' , stats :: Vec n (Signal free ResetManager.Statistics) -- ^ See 'Output.stats' } @@ -207,6 +213,9 @@ data Output tx rx tx1 rx1 txS free serializedData = Output -- 'Input.txStart' to be asserted before starting to send 'txData'. , txSampling :: Signal tx Bool -- ^ Data is sampled from 'Input.txData' + , handshakeCompleteTx :: Signal tx Bool + -- ^ Asserted when link has been established, but not necessarily handling user data. + -- This signal is native to the 'rx' domain. If you need that, use 'handshakeComplete'. , txP :: Signal txS serializedData -- ^ Transmit data (and implicitly a clock), positive , txN :: Signal txS serializedData @@ -218,11 +227,16 @@ data Output tx rx tx1 rx1 txS free serializedData = Output -- is deasserted. , rxData :: Signal rx (Maybe (BitVector 64)) -- ^ User data received from the neighbor + , handshakeComplete :: Signal rx Bool + -- ^ Asserted when link has been established, but not necessarily handling user data. , linkUp :: Signal free Bool -- ^ True if both the transmit and receive side are either handling user data , linkReady :: Signal free Bool -- ^ True if both the transmit and receive side ready to handle user data or -- doing so. I.e., 'linkUp' implies 'linkReady'. Note that this + , handshakeCompleteFree :: Signal free Bool + -- ^ Asserted when link has been established, but not necessarily handling user data. + -- This signal is native to the 'rx' domain. If you need that, use 'handshakeComplete'. , stats :: Signal free ResetManager.Statistics -- ^ Statistics exported by 'ResetManager.resetManager'. Useful for debugging. } @@ -325,16 +339,19 @@ transceiverPrbsN opts inputs@Inputs{clock, reset, refClock} = , txReset = fold orReset $ map (.txReset) outputs , txReadys = map (.txReady) outputs , txSamplings = map (.txSampling) outputs + , handshakesCompleteTx = map (.handshakeCompleteTx) outputs , -- rx rxClocks = rxClocks , rxResets = map (.rxReset) outputs , rxDatas = map (.rxData) outputs + , handshakesComplete = map (.handshakeComplete) outputs , -- transceiver txPs = pack <$> bundle (map (.txP) outputs) , txNs = pack <$> bundle (map (.txN) outputs) , -- free linkUps = map (.linkUp) outputs , linkReadys = map (.linkReady) outputs + , handshakesCompleteFree = map (.handshakeCompleteFree) outputs , stats = map (.stats) outputs } where @@ -524,6 +541,9 @@ transceiverPrbsWith gthCore opts args@Input{clock, reset} = { txSampling = txUserData , rxData = mux rxUserData (Just <$> alignedRxData0) (pure Nothing) , txReady = withLockRxTx rxReadyNeighborSticky + , handshakeCompleteTx = withLockRxTx prbsOkDelayed + , handshakeComplete = prbsOkDelayed + , handshakeCompleteFree = withLockRxFree prbsOkDelayed , txN , txP , txOutClock diff --git a/bittide/src/Bittide/Wishbone.hs b/bittide/src/Bittide/Wishbone.hs index 7bc20df34..d7a2e1fa8 100644 --- a/bittide/src/Bittide/Wishbone.hs +++ b/bittide/src/Bittide/Wishbone.hs @@ -199,6 +199,31 @@ ilaWb SSymbol stages0 depth0 trigger capture = Circuit $ \(m2s, s2m) -> in ilaInst `hwSeqX` (s2m, m2s) +maybeIlaWb :: + forall name dom addrW a. + (HiddenClock dom) => + -- | Whether or not this ILA instance should be real or not. 'True' actually creates + -- the ILA, 'False' makes this circuit element a no-op. + Bool -> + -- | Name of the module of the `ila` wrapper. Naming the internal ILA is + -- unreliable when more than one ILA is used with the same arguments, but the + -- module name can be set reliably. + SSymbol name -> + -- | Number of registers to insert at each probe. Supported values: 0-6. + -- Corresponds to @C_INPUT_PIPE_STAGES@. Default is @0@. + Index 7 -> + -- | Number of samples to store. Corresponds to @C_DATA_DEPTH@. Default set + -- by 'ilaConfig' equals 'D4096'. + Depth -> + WbToBool dom 'Standard addrW a -> + WbToBool dom 'Standard addrW a -> + Circuit + (Wishbone dom 'Standard addrW a) + (Wishbone dom 'Standard addrW a) +maybeIlaWb True a b c d e = ilaWb a b c d e +maybeIlaWb False _ _ _ _ _ = circuit $ \left -> do + idC -< left + {- | Given a vector with elements and a mask, promote all values with a corresponding 'True' to 'Just', others to 'Nothing'. diff --git a/cabal.project b/cabal.project index 82890007b..117fd3722 100644 --- a/cabal.project +++ b/cabal.project @@ -169,13 +169,13 @@ source-repository-package source-repository-package type: git location: https://github.com/clash-lang/clash-protocols.git - tag: 0832a422e77422739401896f6612620d17baa289 + tag: ec001d4a3d553ed92d0bee125cb9c86e9e47ba99 subdir: clash-protocols source-repository-package type: git location: https://github.com/clash-lang/clash-protocols.git - tag: 0832a422e77422739401896f6612620d17baa289 + tag: ec001d4a3d553ed92d0bee125cb9c86e9e47ba99 subdir: clash-protocols-base source-repository-package diff --git a/firmware-binaries/Cargo.lock b/firmware-binaries/Cargo.lock index c177fad9e..75d6842f9 100644 --- a/firmware-binaries/Cargo.lock +++ b/firmware-binaries/Cargo.lock @@ -231,6 +231,14 @@ version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0ca88d725a0a943b096803bd34e73a4437208b6077654cc4ecb2947a5f91618d" +[[package]] +name = "management-unit" +version = "0.1.0" +dependencies = [ + "bittide-sys", + "riscv-rt", +] + [[package]] name = "memchr" version = "2.7.4" diff --git a/firmware-binaries/Cargo.toml b/firmware-binaries/Cargo.toml index 88a2af2ab..caaf05454 100644 --- a/firmware-binaries/Cargo.toml +++ b/firmware-binaries/Cargo.toml @@ -27,6 +27,7 @@ members = [ "clock-control-reg-cpy", "clock-control", "clock-control", + "management-unit", "processing-element-test", ] resolver = "2" diff --git a/firmware-binaries/management-unit/Cargo.toml b/firmware-binaries/management-unit/Cargo.toml new file mode 100644 index 000000000..a65c6f265 --- /dev/null +++ b/firmware-binaries/management-unit/Cargo.toml @@ -0,0 +1,16 @@ +# SPDX-FileCopyrightText: 2025 Google LLC +# +# SPDX-License-Identifier: CC0-1.0 + +[package] +name = "management-unit" +version = "0.1.0" +edition = "2021" +license = "Apache-2.0" +authors = ["Google LLC"] + +[dependencies] +riscv-rt = "0.11.0" + +[dependencies.bittide-sys] +path = "../../firmware-support/bittide-sys" diff --git a/firmware-binaries/management-unit/build.rs b/firmware-binaries/management-unit/build.rs new file mode 100644 index 000000000..0d6ad00a9 --- /dev/null +++ b/firmware-binaries/management-unit/build.rs @@ -0,0 +1,23 @@ +// SPDX-FileCopyrightText: 2022 Google LLC +// +// SPDX-License-Identifier: Apache-2.0 + +use std::env; +use std::fs; +use std::path::Path; + +/// Put the linker script somewhere the linker can find it. +fn main() { + let out_dir = env::var("OUT_DIR").expect("No out dir"); + let dest_path = Path::new(&out_dir).join("memory.x"); + fs::write(dest_path, include_bytes!("memory.x")).expect("Could not write file"); + + if env::var("CARGO_CFG_TARGET_ARCH").unwrap() == "riscv32" { + println!("cargo:rustc-link-arg=-Tmemory.x"); + println!("cargo:rustc-link-arg=-Tlink.x"); // linker script from riscv-rt + } + println!("cargo:rustc-link-search={out_dir}"); + + println!("cargo:rerun-if-changed=memory.x"); + println!("cargo:rerun-if-changed=build.rs"); +} diff --git a/firmware-binaries/management-unit/memory.x b/firmware-binaries/management-unit/memory.x new file mode 100644 index 000000000..5efdfa0ce --- /dev/null +++ b/firmware-binaries/management-unit/memory.x @@ -0,0 +1,18 @@ +/* +SPDX-FileCopyrightText: 2025 Google LLC + +SPDX-License-Identifier: CC0-1.0 +*/ + +MEMORY +{ + IMEM : ORIGIN = 0x80000000, LENGTH = 64K + DMEM : ORIGIN = 0x40000000, LENGTH = 64K +} + +REGION_ALIAS("REGION_TEXT", IMEM); +REGION_ALIAS("REGION_RODATA", DMEM); +REGION_ALIAS("REGION_DATA", DMEM); +REGION_ALIAS("REGION_BSS", DMEM); +REGION_ALIAS("REGION_HEAP", DMEM); +REGION_ALIAS("REGION_STACK", DMEM); diff --git a/firmware-binaries/management-unit/src/main.rs b/firmware-binaries/management-unit/src/main.rs new file mode 100644 index 000000000..695e8d274 --- /dev/null +++ b/firmware-binaries/management-unit/src/main.rs @@ -0,0 +1,23 @@ +#![no_std] +#![cfg_attr(not(test), no_main)] + +// SPDX-FileCopyrightText: 2025 Google LLC +// +// SPDX-License-Identifier: Apache-2.0 + +use core::panic::PanicInfo; + +#[cfg(not(test))] +use riscv_rt::entry; + +#[cfg_attr(not(test), entry)] +fn main() -> ! { + #[allow(clippy::empty_loop)] + loop {} +} + +#[panic_handler] +fn panic_handler(_: &PanicInfo) -> ! { + #[allow(clippy::empty_loop)] + loop {} +}