From 67a6502ea229a06b571d6126234a347ae9e12d71 Mon Sep 17 00:00:00 2001 From: Ryan Slawson Date: Tue, 25 Feb 2025 14:14:58 +0100 Subject: [PATCH] Big commit, see full commit message for details. Okay, so a lot of things happened with this one. Let's start with the changes to the demo driver function. - I've reordered the function definitions in the big `let` to better reflect the order they're initially used in. - I've added in a function to deassert `probe_all_programmed`. Currently this is not necessary, however, if in the future more test cases are added to be run on this DUT, `probe_all_programmed` must be deasserted between each of them. However since the post function was removed from the HITL infra, the only way to do this currently is to make sure that the probe is deasserted before anything else happens in the driver function. - Unnecessary dumps of stdout/stderr of various handles were removed. These were only there from earlier debugging attempts and are no longer necessary. - Timeouts on cleanup for GDB and OpenOCD were extended from 5s to 10s. - GDB stdout is set to line buffering on our side. Hopefully GDB also does this. - Added in a whoami check to ensure that each of the CPUs reports its identity and that that report is what it should be. - THE TEST START PROBE IS ACTUALLY ASSERTED NOW, HOW ON EARTH DID I FORGET TO DO THIS PREVIOUSLY - `probe_all_programmed` is asserted. In `bittide/src/Bittide/Wishbone.hs`: - Added `wbAlwaysAckWith` and `whoAmIC`. The second one uses the first in order to report back a 4 byte identifier whenever accessed over the Wishbone bus. In `bittide-instances/src/Bittide/Instances/Hitl/Demo.hs`: - Each component was given a unique address in the management unit processing element config, and a `whoAmIC` component was added. - `whoAmIC` component added to SwCc at the same address as in the management unit. - A debug ILA was added to both the DUT and test top entity. - Naming and definitions of the FIFO overflow/underflow signals were changed. Previously they would only ever assert `True` if ALL FIFOs overflowed or underflowed, now they will assert if ANY do. Furthermore, in the test top entity they were used as `noFifoOverflows`/`noFifoUnderflows` without actually inverting the signals, so that was fixed. In `firmware-binaries/management-unit/memory.x`: - The address of data memory was changed to reflect the updated PE config. In `bittide-instances/data/openocd/vexriscv-2chain.tcl`: | - Added back in the environment variable grabbing for the TCL and telnet ports. - Updated target definitions so that they use ports in the opposite order as JTAG taps. Why do the taps and ports go in the opposite order that I expected? Excellent question, and I don't even have the beginnings of an answer to it. --- .../data/openocd/vexriscv-2chain.tcl | 28 +- .../src/Bittide/Instances/Hitl/Demo.hs | 239 +++++++++++++++--- .../src/Bittide/Instances/Hitl/Driver/Demo.hs | 163 +++++++----- bittide/src/Bittide/Wishbone.hs | 26 ++ firmware-binaries/management-unit/memory.x | 2 +- 5 files changed, 363 insertions(+), 95 deletions(-) diff --git a/bittide-instances/data/openocd/vexriscv-2chain.tcl b/bittide-instances/data/openocd/vexriscv-2chain.tcl index 6a215f1d7..30c904037 100644 --- a/bittide-instances/data/openocd/vexriscv-2chain.tcl +++ b/bittide-instances/data/openocd/vexriscv-2chain.tcl @@ -11,6 +11,26 @@ if { $user_gdb_port_b == "" } { error "Required environment variable 'DEV_B_GDB' not set." } +set user_tcl_port_a [env DEV_A_TCL] +if { $user_tcl_port_a == "" } { + error "Required environment variable 'DEV_A_TCL' not set." +} + +set user_tcl_port_b [env DEV_B_TCL] +if { $user_tcl_port_b == "" } { + error "Required environment variable 'DEV_B_TCL' not set." +} + +set user_tel_port_a [env DEV_A_TEL] +if { $user_tel_port_a == "" } { + error "Required environment variable 'DEV_A_TEL' not set." +} + +set user_tel_port_b [env DEV_B_TEL] +if { $user_tel_port_b == "" } { + error "Required environment variable 'DEV_B_TEL' not set." +} + set git_top_level [string trim [exec git rev-parse --show-toplevel]] set _ENDIAN little @@ -29,11 +49,15 @@ 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 +target create $_CHIPNAME.cpu1 vexriscv -endian $_ENDIAN -chain-position $_CHIPNAME.chain1 -gdb-port $user_gdb_port_a +tcl_port $user_tcl_port_a +telnet_port $user_tel_port_a 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 +target create $_CHIPNAME.cpu0 vexriscv -endian $_ENDIAN -chain-position $_CHIPNAME.chain0 -gdb-port $user_gdb_port_b +tcl_port $user_tcl_port_b +telnet_port $user_tel_port_b vexriscv readWaitCycles 10 vexriscv cpuConfigFile [file join $git_top_level clash-vexriscv clash-vexriscv example-cpu ExampleCpu.yaml] diff --git a/bittide-instances/src/Bittide/Instances/Hitl/Demo.hs b/bittide-instances/src/Bittide/Instances/Hitl/Demo.hs index 0fb50d18e..acafe33aa 100644 --- a/bittide-instances/src/Bittide/Instances/Hitl/Demo.hs +++ b/bittide-instances/src/Bittide/Instances/Hitl/Demo.hs @@ -53,17 +53,19 @@ import Bittide.SharedTypes (Bytes) import Bittide.Switch (switchC) import Bittide.SwitchDemoProcessingElement (switchDemoPeWb) import Bittide.Transceiver (transceiverPrbsN) -import Bittide.Wishbone (readDnaPortE2Wb, timeWb) +import Bittide.Wishbone (readDnaPortE2Wb, timeWb, whoAmIC) import Clash.Annotations.TH (makeTopEntity) import Clash.Cores.Xilinx.GTH (ibufds_gte3) +import Clash.Cores.Xilinx.Ila (Depth (..), IlaConfig (..), ila, ilaConfig) import Clash.Cores.Xilinx.Unisim.DnaPortE2 (simDna2) import Clash.Cores.Xilinx.VIO (vioProbe) import Clash.Cores.Xilinx.Xpm.Cdc (xpmCdcArraySingle, xpmCdcSingle) import Clash.Functor.Extra ((<<$>>)) +import Clash.Sized.Extra (unsignedToSigned) +import Clash.Sized.Vector.ToTuple (vecToTuple) import Clash.Xilinx.ClockGen (clockWizardDifferential) import Protocols -import Protocols.Idle import Protocols.Wishbone import System.FilePath (()) import VexRiscv (DumpVcd (..), Jtag, JtagIn (..), JtagOut (..)) @@ -130,11 +132,26 @@ calendarConfig = repetitions = natToNum @((LinkCount + 1) * 3) @(Unsigned 8) {- FOURMOLU_ENABLE -} -muConfig :: SimpleManagementConfig 10 +muConfig :: SimpleManagementConfig 11 muConfig = SimpleManagementConfig PeConfig - { memMapConfig = 0b100 :> 0b010 :> 0b110 :> 0b101 :> repeat 0 + { memMapConfig = + 0b1000 -- IMEM + :> 0b1100 -- DMEM + :> 0b1101 -- timeWb + :> 0b1110 -- whoAmI + :> 0b1001 -- peWb + :> 0b1010 -- switchWb + :> 0b1011 -- dnaWb + :> 0b0001 -- UGN0 + :> 0b0010 -- UGN1 + :> 0b0011 -- UGN2 + :> 0b0100 -- UGN3 + :> 0b0101 -- UGN4 + :> 0b0110 -- UGN5 + :> 0b0111 -- UGN6 + :> Nil , initI = Undefined @(Div (64 * 1024) 4) , initD = Undefined @(Div (64 * 1024) 4) , iBusTimeout = d0 @@ -144,13 +161,13 @@ muConfig = NoDumpVcd ccConfig :: - SwControlCConfig CccStabilityCheckerMargin (CccStabilityCheckerFramesize Basic125) 0 + SwControlCConfig CccStabilityCheckerMargin (CccStabilityCheckerFramesize Basic125) 1 ccConfig = SwControlCConfig SNat SNat PeConfig - { memMapConfig = 0b100 :> 0b010 :> 0b110 :> 0b101 :> Nil + { memMapConfig = 0b100 :> 0b010 :> 0b110 :> 0b101 :> 0b111 :> Nil , initI = Undefined @(Div (64 * 1024) 4) , initD = Undefined @(Div (64 * 1024) 4) , iBusTimeout = d0 @@ -201,19 +218,125 @@ dut :: , "fifoUnderflowsSticky" ::: Signal Basic125 Bool ) dut refClk refRst skyClk rxNs rxPs allProgrammed miso jtagIn = - ( transceivers.txNs - , transceivers.txPs - , handshakesCompleteFree - , frequencyAdjustments - , spiDone - , spiOut - , jtagOut - , transceiversFailedAfterUp - , allStable - , fifoOverflowsSticky - , fifoUnderflowsSticky - ) + hwSeqX + debugIla + ( transceivers.txNs + , transceivers.txPs + , handshakesCompleteFree + , frequencyAdjustments + , spiDone + , spiOut + , jtagOut + , transceiversFailedAfterUp + , allStable + , fifoOverflowsSticky + , fifoUnderflowsSticky + ) where + debugIla :: Signal Basic125 () + debugIla = + setName @"demoDebugIla" + ila + ( ilaConfig + $ "trigger_fdi_dd" + :> "capture_fdi_dd" + -- Important step 1 signals + :> "dd_spiDone" + :> "dd_spiErr" + -- Important step 2 signals + :> "dd_handshakesCompleteFree" + -- Important step 3 signals + :> "dd_txStarts" + -- Important step 4 signals + -- Important step 5 signals + :> "dd_allStable" + -- Important step 6 signals + :> "dd_ebReadys" + -- Other + :> "dd_transceiversFailedAfterUp" + :> "dd_nFincs" + :> "dd_nFdecs" + :> "dd_netFincs" + :> "dd_dDiff0" + :> "dd_dDiff1" + :> "dd_dDiff2" + :> "dd_dDiff3" + :> "dd_dDiff4" + :> "dd_dDiff5" + :> "dd_dDiff6" + :> Nil + ) + { depth = D32768 + } + refClk + (unsafeToActiveLow handshakeRstFree) + captureFlag + spiDone + spiErr + (bundle transceivers.handshakesCompleteFree) + (bundle $ xpmCdcArraySingle bittideClk refClk <$> txStarts) + allStable + (bundle $ xpmCdcArraySingle bittideClk refClk <$> ebReadys) + transceiversFailedAfterUp + nFincs + nFdecs + (fmap unsignedToSigned nFincs - fmap unsignedToSigned nFdecs) + dDiff0 + dDiff1 + dDiff2 + dDiff3 + dDiff4 + dDiff5 + dDiff6 + + captureFlag = + riseEvery + refClk + spiRst + enableGen + (SNat @(PeriodToCycles Basic125 (Milliseconds 2))) + + nFincs :: Signal Basic125 (Unsigned 32) + nFincs = + regEn + refClk + refRst + enableGen + (0 :: Unsigned 32) + ( isFalling + refClk + refRst + enableGen + False + ((== Just SpeedUp) <$> callistoResult.maybeSpeedChangeC) + ) + (satSucc SatBound <$> nFincs) + + nFdecs :: Signal Basic125 (Unsigned 32) + nFdecs = + regEn + refClk + refRst + enableGen + (0 :: Unsigned 32) + ( isFalling + refClk + refRst + enableGen + False + ((== Just SlowDown) <$> callistoResult.maybeSpeedChangeC) + ) + (satSucc SatBound <$> nFdecs) + + ( dDiff0 + , dDiff1 + , dDiff2 + , dDiff3 + , dDiff4 + , dDiff5 + , dDiff6 + ) = vecToTuple domainDiffs + -- Step 1, wait for SPI: (_, _, spiState, spiOut) = withClockResetEnable refClk spiRst enableGen @@ -351,7 +474,7 @@ dut refClk refRst skyClk rxNs rxPs allProgrammed miso jtagIn = [muJtagFree, ccJtag] <- jtagChain -< jtag muJtagTx <- unsafeJtagSynchronizer refClk bittideClk -< muJtagFree - [peWb, switchWb, dnaWb, ugn0, ugn1, ugn2, ugn3, ugn4, ugn5, ugn6] <- + [muWhoAmI, peWb, switchWb, dnaWb, ugn0, ugn1, ugn2, ugn3, ugn4, ugn5, ugn6] <- withClockResetEnable bittideClk handshakeRstTx @@ -399,7 +522,14 @@ dut refClk refRst skyClk rxNs rxPs allProgrammed miso jtagIn = (readDnaPortE2Wb simDna2) -< dnaWb - (swCcOut, ccM2S) <- + withClockResetEnable + bittideClk + handshakeRstTx + enableGen + (whoAmIC 0x746d_676d) + -< muWhoAmI + + (swCcOut, [ccWhoAmI]) <- withClockResetEnable refClk handshakeRstFree @@ -407,7 +537,12 @@ dut refClk refRst skyClk rxNs rxPs allProgrammed miso jtagIn = (callistoSwClockControlC @LinkCount @CccBufferSize NoDumpVcd ccConfig) -< (ccJtag, reframe, mask, dc) - idleSink -< ccM2S + withClockResetEnable + refClk + handshakeRstFree + enableGen + (whoAmIC 0x6363_7773) + -< ccWhoAmI idC -< (swCcOut, [tx0, tx1, tx2, tx3, tx4, tx5, tx6]) @@ -432,7 +567,7 @@ dut refClk refRst skyClk rxNs rxPs allProgrammed miso jtagIn = delay refClk enableGen minBound $ speedChangeToStickyPins refClk - handshakeRstFree + (unsafeFromActiveLow allProgrammed) enableGen (SNat @Si539xHoldTime) callistoResult.maybeSpeedChangeC @@ -465,10 +600,10 @@ dut refClk refRst skyClk rxNs rxPs allProgrammed miso jtagIn = (_, fifoUnderflowsTx, fifoOverflowsTx, ebModes, rxDatasEbs) = unzip5 rxFifos fifoOverflowsFree :: Signal Basic125 Overflow - fifoOverflowsFree = and <$> xpmCdcArraySingle bittideClk refClk (bundle fifoOverflowsTx) + fifoOverflowsFree = or <$> xpmCdcArraySingle bittideClk refClk (bundle fifoOverflowsTx) fifoUnderflowsFree :: Signal Basic125 Underflow - fifoUnderflowsFree = and <$> xpmCdcArraySingle bittideClk refClk (bundle fifoUnderflowsTx) + fifoUnderflowsFree = or <$> xpmCdcArraySingle bittideClk refClk (bundle fifoUnderflowsTx) fifoOverflowsSticky :: Signal Basic125 Bool fifoOverflowsSticky = sticky refClk handshakeRstFree fifoOverflowsFree @@ -498,7 +633,7 @@ demoTest :: , "JTAG" ::: Signal Basic125 JtagOut ) demoTest boardClkDiff refClkDiff rxns rxps miso jtagIn = - (txns, txps, unbundle swFincFdecs, spiDone, spiOut, jtagOut) + hwSeqX testIla (txns, txps, unbundle swFincFdecs, spiDone, spiOut, jtagOut) where boardClk :: Clock Ext200 boardClk = ibufds_gte3 boardClkDiff @@ -513,30 +648,31 @@ demoTest boardClkDiff refClkDiff rxns rxps miso jtagIn = unbundle $ setName @"vioHitlt" $ vioProbe - ("probe_test_done" :> "probe_test_success" :> Nil) + ("probe_test_done" :> "probe_test_success" :> "probe_handshakes_done" :> Nil) ("probe_test_start" :> "probe_all_programmed" :> Nil) (False, False) refClk (testStart .&&. testDone) -- done (testStart .&&. testSuccess) -- success + handshakesDone testReset :: Reset Basic125 testReset = unsafeFromActiveLow testStart `orReset` refRst ( txns :: TransceiverWires GthTxS LinkCount , txps :: TransceiverWires GthTxS LinkCount - , _handshakesDone :: Signal Basic125 Bool + , 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 , allStable :: Signal Basic125 Bool - , noFifoOverflows :: Signal Basic125 Bool - , noFifoUnderflows :: Signal Basic125 Bool + , fifoOverflows :: Signal Basic125 Bool + , fifoUnderflows :: Signal Basic125 Bool ) = dut refClk testReset boardClk rxns rxps allProgrammed miso jtagIn fifoSuccess :: Signal Basic125 Bool - fifoSuccess = noFifoUnderflows .&&. noFifoOverflows + fifoSuccess = not <$> (fifoUnderflows .||. fifoOverflows) endSuccess :: Signal Basic125 Bool endSuccess = trueFor (SNat @(Seconds 5)) refClk testReset (allStable .&&. fifoSuccess) @@ -546,6 +682,49 @@ demoTest boardClkDiff refClkDiff rxns rxps miso jtagIn = testSuccess :: Signal Basic125 Bool testSuccess = allStable .&&. fifoSuccess .&&. fmap not transceiversFailedAfterUp + + testIla :: Signal Basic125 () + testIla = + setName @"demoTestIla" + ila + ( ilaConfig + $ "trigger_fdi_dt" + :> "capture_fdi_dt" + :> "dt_handshakesDone" + :> "dt_all_programmed" + :> "dt_swFincFdecs" + :> "dt_spiDone" + :> "dt_spiOut" + :> "dt_jtagOut" + :> "dt_transceiversFailedAfterUp" + :> "dt_allStable" + :> "dt_fifoOverflows" + :> "dt_fifoUnderflows" + :> Nil + ) + { depth = D32768 + } + refClk + handshakesDone + captureFlag + handshakesDone + allProgrammed + swFincFdecs + spiDone + (bundle spiOut) + jtagOut + transceiversFailedAfterUp + allStable + fifoOverflows + fifoUnderflows + + captureFlag :: Signal Basic125 Bool + captureFlag = + riseEvery + refClk + testReset + enableGen + (SNat @(PeriodToCycles Basic125 (Milliseconds 2))) {-# OPAQUE demoTest #-} makeTopEntity 'demoTest diff --git a/bittide-instances/src/Bittide/Instances/Hitl/Driver/Demo.hs b/bittide-instances/src/Bittide/Instances/Hitl/Driver/Demo.hs index 29186e976..12f9b9531 100644 --- a/bittide-instances/src/Bittide/Instances/Hitl/Driver/Demo.hs +++ b/bittide-instances/src/Bittide/Instances/Hitl/Driver/Demo.hs @@ -15,12 +15,11 @@ 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 (forM_, zipWithM) import Control.Monad.IO.Class +import Data.List.Extra (trim) 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) @@ -85,13 +84,27 @@ driverFunc testName targets = do fail $ "Timeout while performing action: " <> actionName Just r -> pure r + deassertAllProgrammed :: (HwTarget, DeviceInfo) -> VivadoM () + deassertAllProgrammed (hwT, d) = do + liftIO $ putStrLn $ "Deasserting all programmed probe on " <> show d.deviceId + openHardwareTarget hwT + updateVio "vioHitlt" [("probe_all_programmed", "0")] + + assertTestStart :: (HwTarget, DeviceInfo) -> VivadoM () + assertTestStart (hwT, d) = do + liftIO $ putStrLn $ "Asserting test start probe on " <> show d.deviceId + openHardwareTarget hwT + updateVio "vioHitlt" [("probe_test_start", "1")] + 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 + then do + rest <- getRest + return $ handsShaken : rest else do openHardwareTarget hwT vals <- readVio "vioHitlt" ["probe_handshakes_done"] @@ -154,32 +167,12 @@ driverFunc testName targets = do , ("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 + 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) + ocdClean1 = ocdClean0 >> awaitProcessTermination ocdProcName ocdPh (Just 10_000_000) return $ OcdInitData gdbPortMU gdbPortCC ocd ocdClean1 @@ -189,18 +182,75 @@ driverFunc testName targets = do (gdb, gdbPh, gdbClean0) <- Gdb.startGdbH hSetBuffering gdb.stdinHandle LineBuffering + hSetBuffering gdb.stdoutHandle LineBuffering Gdb.setLogging gdb $ hitlDir "gdb-" <> binName <> "-" <> show (getTargetIndex hwT) <> ".log" Gdb.setFile gdb $ firmwareBinariesDir "riscv32imc" Release binName Gdb.setTarget gdb gdbPort + Gdb.setTimeout gdb Nothing + Gdb.runCommands gdb.stdinHandle ["echo connected to target device"] let gdbProcName = "GDB (" <> binName <> ", " <> show d.deviceId <> ")" - gdbClean1 = gdbClean0 >> awaitProcessTermination gdbProcName gdbPh (Just 5_000_000) + gdbClean1 = gdbClean0 >> awaitProcessTermination gdbProcName gdbPh (Just 10_000_000) return (gdb, gdbClean1) + ccGdbCheck :: (HwTarget, DeviceInfo) -> ProcessStdIoHandles -> VivadoM ExitCode + ccGdbCheck (_, _) gdb = do + liftIO + $ Gdb.runCommands + gdb.stdinHandle + ["echo START OF WHOAMI\\n", "x/4cb 0xE0000000", "echo END OF WHOAMI\\n"] + _ <- + liftIO + $ tryWithTimeout "Waiting for GDB to be ready to proceed" 15_000_000 + $ readUntil gdb.stdoutHandle "START OF WHOAMI" + gdbRead <- + liftIO + $ tryWithTimeout "Reading CC whoami over GDB" 15_000_000 + $ readUntil gdb.stdoutHandle "END OF WHOAMI" + let + idLine = trim . L.head . lines $ trim gdbRead + success = idLine == "(gdb) 0xe0000000:\t115 's'\t119 'w'\t99 'c'\t99 'c'" + liftIO $ putStrLn [i|Output from CC whoami probe:\n#{idLine}|] + return $ if success then ExitSuccess else ExitFailure 1 + + 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) + + assertAllProgrammed :: (HwTarget, DeviceInfo) -> VivadoM () + assertAllProgrammed (hwT, d) = do + liftIO $ putStrLn $ "Asserting all programmed probe on " <> show d.deviceId + openHardwareTarget hwT + updateVio "vioHitlt" [("probe_all_programmed", "1")] + + muGdbCheck :: (HwTarget, DeviceInfo) -> ProcessStdIoHandles -> VivadoM ExitCode + muGdbCheck (_, _) gdb = do + liftIO + $ Gdb.runCommands + gdb.stdinHandle + ["echo START OF WHOAMI\\n", "x/4cb 0xE0000000", "echo END OF WHOAMI\\n"] + _ <- + liftIO + $ tryWithTimeout "Waiting for GDB to be ready to proceed" 15_000_000 + $ readUntil gdb.stdoutHandle "START OF WHOAMI" + gdbRead <- + liftIO + $ tryWithTimeout "Reading MU whoami over GDB" 15_000_000 + $ readUntil gdb.stdoutHandle "END OF WHOAMI" + let + idLine = trim . L.head . lines $ trim gdbRead + success = idLine == "(gdb) 0xe0000000:\t109 'm'\t103 'g'\t109 'm'\t116 't'" + liftIO $ putStrLn [i|Output from MU whoami probe:\n#{idLine}|] + return $ if success then ExitSuccess else ExitFailure 1 + getTestsStatus :: [(HwTarget, DeviceInfo)] -> [TestStatus] -> Integer -> VivadoM [TestStatus] getTestsStatus [] _ _ = return [] @@ -250,33 +300,12 @@ driverFunc testName targets = do 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 + liftIO + $ putStrLn + "All programmed pin may be asserted from previous test - deasserting on all targets." + forM_ targets deassertAllProgrammed + forM_ targets assertTestStart + tryWithTimeout "Wait for handshakes successes from all boards" 30_000_000 awaitHandshakes brackets (liftIO <$> L.zipWith initOpenOcds targets [0 ..]) (liftIO . (.cleanup)) $ \initOcdsData -> do let muPorts = (.muPort) <$> initOcdsData @@ -286,29 +315,39 @@ driverFunc testName targets = do (liftIO . snd) $ \initCCGdbsData -> do let ccGdbs = fst <$> initCCGdbsData + liftIO $ putStrLn "Checking for MMIO access to SwCC CPUs over GDB..." + gdbExitCodes0 <- zipWithM ccGdbCheck targets ccGdbs + (gdbCount0, gdbExitCode0) <- + L.foldl foldExitCodes (pure (0, ExitSuccess)) gdbExitCodes0 + liftIO + $ putStrLn + [i|CC GDB testing passed on #{gdbCount0} of #{L.length targets} targets|] liftIO $ mapM_ ((errorToException =<<) . Gdb.loadBinary) ccGdbs brackets (liftIO <$> L.zipWith (initGdbs "management-unit") muPorts targets) (liftIO . snd) $ \initMUGdbsData -> do let muGdbs = fst <$> initMUGdbsData + liftIO $ putStrLn "Checking for MMIO access to MU CPUs over GDB..." + gdbExitCodes1 <- zipWithM muGdbCheck targets muGdbs + (gdbCount1, gdbExitCode1) <- + L.foldl foldExitCodes (pure (0, ExitSuccess)) gdbExitCodes1 + liftIO + $ putStrLn + [i|MU GDB testing passed on #{gdbCount1} of #{L.length targets} targets|] liftIO $ mapM_ ((errorToException =<<) . Gdb.loadBinary) muGdbs + liftIO $ mapM_ Gdb.continue ccGdbs + forM_ targets assertAllProgrammed 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] + $ L.find (/= ExitSuccess) [stabilityExitCode, gdbExitCode0, gdbExitCode1] return finalExit diff --git a/bittide/src/Bittide/Wishbone.hs b/bittide/src/Bittide/Wishbone.hs index 9bfeff36c..b9ff28b4d 100644 --- a/bittide/src/Bittide/Wishbone.hs +++ b/bittide/src/Bittide/Wishbone.hs @@ -706,3 +706,29 @@ watchDogWb name timeout@SNat | wdTimeout = trace ("watchDogWb - " <> name <> ": " <> show wbM2S0) 0 | wbM2S0.busCycle && wbM2S0.strobe = succ cnt0 | otherwise = 0 + +wbAlwaysAckWith :: + forall nBytes addrW. + ( KnownNat nBytes + , 1 <= nBytes + , KnownNat addrW + ) => + Bytes nBytes -> + WishboneM2S addrW nBytes (Bytes nBytes) -> + WishboneS2M (Bytes nBytes) +wbAlwaysAckWith dat _ = (emptyWishboneS2M @(Bytes nBytes)){acknowledge = True, readData = dat} + +whoAmIC :: + forall dom addrW. + ( KnownDomain dom + , HiddenClockResetEnable dom + , KnownNat addrW + ) => + BitVector 32 -> + Circuit (Wishbone dom 'Standard addrW (Bytes 4)) () +whoAmIC whoAmI = Circuit go + where + go :: + (Fwd (Wishbone dom 'Standard addrW (Bytes 4)), ()) -> + (Bwd (Wishbone dom 'Standard addrW (Bytes 4)), ()) + go (m2s, ()) = (wbAlwaysAckWith whoAmI <$> m2s, ()) diff --git a/firmware-binaries/management-unit/memory.x b/firmware-binaries/management-unit/memory.x index 5efdfa0ce..d40653969 100644 --- a/firmware-binaries/management-unit/memory.x +++ b/firmware-binaries/management-unit/memory.x @@ -7,7 +7,7 @@ SPDX-License-Identifier: CC0-1.0 MEMORY { IMEM : ORIGIN = 0x80000000, LENGTH = 64K - DMEM : ORIGIN = 0x40000000, LENGTH = 64K + DMEM : ORIGIN = 0xC0000000, LENGTH = 64K } REGION_ALIAS("REGION_TEXT", IMEM);