diff --git a/bittide-experiments/src/Bittide/Hitl.hs b/bittide-experiments/src/Bittide/Hitl.hs index 775f2f513..e7b88a7de 100644 --- a/bittide-experiments/src/Bittide/Hitl.hs +++ b/bittide-experiments/src/Bittide/Hitl.hs @@ -47,11 +47,13 @@ module Bittide.Hitl ( HitlTestGroup (..), HitlTestCase (..), CasePreProcessing (..), + TestStepResult (..), MayHavePostProcData (..), Done, Success, hitlVio, hitlVioBool, + noPreProcess, -- * Test construction convenience functions paramForHwTargets, @@ -103,6 +105,11 @@ data HwTargetRef | HwTargetById FpgaId deriving (Eq, Ord, Show) +data TestStepResult a + = TestStepSuccess a + | TestStepFailure String + deriving (Eq, Ord, Show) + {- | A definition of a test that should be performed with hardware in the loop. Such a HITL test definition can have one or more named test cases that may differ in what hardware targets (FPGAs) they involve and in what parameters they provide @@ -175,15 +182,17 @@ This must be accompanied by a @hitlVio \@NumberOfStages@ in the design. -} data HitlTestGroup where HitlTestGroup :: - (Typeable a, Typeable b) => + (Typeable a, Typeable b, Typeable c) => { topEntity :: ClashTargetName -- ^ Reference to the Design Under Test , extraXdcFiles :: [String] - , testCases :: [HitlTestCase HwTargetRef a b] + , testCases :: [HitlTestCase HwTargetRef a b c] -- ^ List of test cases - , mPreProc :: Maybe (VivadoHandle -> String -> HwTarget -> IO ()) - -- ^ Optional pre-processing step. First argument is the name of the test - , mPostProc :: Maybe (FilePath -> ExitCode -> IO ()) + , mPreProc :: (VivadoHandle -> String -> HwTarget -> IO (TestStepResult c)) + -- ^ Pre-processing step. First argument is the name of the test + , mMonitorProc :: Maybe (VivadoHandle -> String -> FilePath -> [(HwTarget, c)] -> IO ExitCode) + -- ^ Optional monitoring process. + , mPostProc :: Maybe (FilePath -> ExitCode -> IO (TestStepResult ())) -- ^ Optional post processing step. , externalHdl :: [String] -- ^ List of external HDL files to include in he project @@ -193,25 +202,23 @@ data HitlTestGroup where {- | A HITL test case. One HITL test group can have multiple test cases associated with it. -} -data HitlTestCase h a b where +data HitlTestCase h a b c where HitlTestCase :: - (Show h, Show a, BitPack a, Show b, Typeable h) => + (Show h, Show a, BitPack a, Show b, Typeable h, Typeable c) => { name :: String , parameters :: Map h a - , preProc :: CasePreProcessing + , preProc :: CasePreProcessing c , postProcData :: b } -> - HitlTestCase h a b + HitlTestCase h a b c -deriving instance Show (HitlTestCase h a b) +deriving instance Show (HitlTestCase h a b c) -data CasePreProcessing - = NoPreProcess - | InheritPreProcess - | CustomPreProcess (VivadoHandle -> HwTarget -> IO ()) +data CasePreProcessing c + = InheritPreProcess + | CustomPreProcess (VivadoHandle -> HwTarget -> IO (TestStepResult c)) -instance Show CasePreProcessing where - show NoPreProcess = "NoPreProcess" +instance Show (CasePreProcessing a) where show InheritPreProcess = "InheritPreProcess" show (CustomPreProcess _) = "CustomPreProcess " @@ -220,8 +227,8 @@ class MayHavePostProcData b where -- | Returns the test names with some post processing data of type @c@, -- if that data exists. mGetPPD :: - forall h a. - [HitlTestCase h a b] -> + forall h a c. + [HitlTestCase h a b c] -> Map String (Maybe b) instance MayHavePostProcData a where @@ -232,6 +239,9 @@ instance MayHavePostProcData a where instance MayHavePostProcData () where mGetPPD = Map.fromList . map ((,Nothing) . name) +noPreProcess :: VivadoHandle -> String -> HwTarget -> IO (TestStepResult ()) +noPreProcess _ _ _ = pure (TestStepSuccess ()) + -- | Obtain a list of the hardware targets that are relevant for a given HITL test. hwTargetRefsFromHitlTestGroup :: HitlTestGroup -> [HwTargetRef] hwTargetRefsFromHitlTestGroup HitlTestGroup{testCases} = @@ -259,11 +269,13 @@ to it and receives that constructur as test parameter. > testCases = testCasesFromEnum @ABC allHwTargets () -} testCasesFromEnum :: - forall a b. - (Show a, Bounded a, Enum a, BitPack a, Show b, Typeable a, Typeable b) => + forall a b c. + ( Show a, Bounded a, Enum a, BitPack a + , Show b, Show c + , Typeable a, Typeable b, Typeable c) => [HwTargetRef] -> b -> - [HitlTestCase HwTargetRef a b] + [HitlTestCase HwTargetRef a b c] testCasesFromEnum hwTs ppd = [ HitlTestCase { name = show constr diff --git a/bittide-instances/src/Bittide/Instances/Hitl/BoardTest.hs b/bittide-instances/src/Bittide/Instances/Hitl/BoardTest.hs index 3da06adf7..c996a311e 100644 --- a/bittide-instances/src/Bittide/Instances/Hitl/BoardTest.hs +++ b/bittide-instances/src/Bittide/Instances/Hitl/BoardTest.hs @@ -25,10 +25,12 @@ import Bittide.Hitl ( HitlTestCase (..), HitlTestGroup (..), CasePreProcessing (..), + TestStepResult (..), hitlVio, hitlVioBool, paramForHwTargets, testCasesFromEnum, + noPreProcess, ) import Bittide.Instances.Domains (Ext125) import Bittide.Instances.Hitl.Setup (allHwTargets) @@ -188,7 +190,8 @@ testSimple = , postProcData = () , preProc = InheritPreProcess }] - , mPreProc = Nothing + , mPreProc = noPreProcess + , mMonitorProc = Nothing , mPostProc = Nothing } @@ -199,12 +202,14 @@ testExtended = , extraXdcFiles = [] , externalHdl = [] , testCases = testCasesFromEnum @Test allHwTargets () - , mPreProc = Nothing + , mPreProc = noPreProcess + , mMonitorProc = Nothing , mPostProc = Just postBoardTestExtendedFunc } -postBoardTestExtendedFunc :: FilePath -> ExitCode -> IO () +postBoardTestExtendedFunc :: FilePath -> ExitCode -> IO (TestStepResult ()) postBoardTestExtendedFunc ilaDir exitCode = do csvPaths <- glob (ilaDir "*" "*" "*.csv") let ilaCsvPaths = toFlattenedIlaCsvPathList ilaDir csvPaths postBoardTestExtended exitCode ilaCsvPaths + pure $ TestStepSuccess () diff --git a/bittide-instances/src/Bittide/Instances/Hitl/FincFdec.hs b/bittide-instances/src/Bittide/Instances/Hitl/FincFdec.hs index e480b50bb..70f8ec642 100644 --- a/bittide-instances/src/Bittide/Instances/Hitl/FincFdec.hs +++ b/bittide-instances/src/Bittide/Instances/Hitl/FincFdec.hs @@ -26,6 +26,7 @@ import Bittide.Hitl ( HwTargetRef (HwTargetByIndex), hitlVio, testCasesFromEnum, + noPreProcess, ) import Bittide.Instances.Domains @@ -222,6 +223,7 @@ tests = , extraXdcFiles = [] , externalHdl = [] , testCases = testCasesFromEnum @Test [HwTargetByIndex 7] () - , mPreProc = Nothing + , mPreProc = noPreProcess + , mMonitorProc = Nothing , mPostProc = Nothing } diff --git a/bittide-instances/src/Bittide/Instances/Hitl/FullMeshHwCc.hs b/bittide-instances/src/Bittide/Instances/Hitl/FullMeshHwCc.hs index 73e128f66..8ad24ed05 100644 --- a/bittide-instances/src/Bittide/Instances/Hitl/FullMeshHwCc.hs +++ b/bittide-instances/src/Bittide/Instances/Hitl/FullMeshHwCc.hs @@ -522,7 +522,8 @@ mkTest topEntity = , preProc = InheritPreProcess } ] - , mPreProc = Nothing + , mPreProc = noPreProcess + , mMonitorProc = Nothing , mPostProc = Nothing } where diff --git a/bittide-instances/src/Bittide/Instances/Hitl/FullMeshSwCc.hs b/bittide-instances/src/Bittide/Instances/Hitl/FullMeshSwCc.hs index c1c9fca91..8ed389456 100644 --- a/bittide-instances/src/Bittide/Instances/Hitl/FullMeshSwCc.hs +++ b/bittide-instances/src/Bittide/Instances/Hitl/FullMeshSwCc.hs @@ -692,7 +692,8 @@ tests = } | n <- [0 .. testsToRun - 1] ] - , mPreProc = Nothing + , mPreProc = noPreProcess + , mMonitorProc = Nothing , mPostProc = Nothing } where diff --git a/bittide-instances/src/Bittide/Instances/Hitl/HwCcTopologies.hs b/bittide-instances/src/Bittide/Instances/Hitl/HwCcTopologies.hs index b246f38c5..78dd41784 100644 --- a/bittide-instances/src/Bittide/Instances/Hitl/HwCcTopologies.hs +++ b/bittide-instances/src/Bittide/Instances/Hitl/HwCcTopologies.hs @@ -818,7 +818,7 @@ tests = testGroup calibrateClockOffsets = calibrateCC False validateClockOffsetCalibration = calibrateCC True - calibrateCC :: Bool -> HitlTestCase HwTargetRef TestConfig CcConf + calibrateCC :: Bool -> HitlTestCase HwTargetRef TestConfig CcConf () calibrateCC validate = HitlTestCase { name = (if validate then "zzz_validate" else "0_calibrate") <> "_clock_offsets" @@ -850,7 +850,7 @@ tests = testGroup Maybe (Vec n PartsPer) -> Vec n StartupDelay -> Topology n -> - HitlTestCase HwTargetRef TestConfig CcConf + HitlTestCase HwTargetRef TestConfig CcConf () tt clockShifts startDelays t = HitlTestCase { name = topologyName t @@ -921,7 +921,8 @@ tests = testGroup -- make sure the clock offsets detected during calibration is still the same , validateClockOffsetCalibration ] - , mPreProc = Nothing + , mPreProc = noPreProcess + , mMonitorProc = Nothing , mPostProc = Nothing } {- FOURMOLU_ENABLE -} diff --git a/bittide-instances/src/Bittide/Instances/Hitl/LinkConfiguration.hs b/bittide-instances/src/Bittide/Instances/Hitl/LinkConfiguration.hs index 23af079da..617a7edcc 100644 --- a/bittide-instances/src/Bittide/Instances/Hitl/LinkConfiguration.hs +++ b/bittide-instances/src/Bittide/Instances/Hitl/LinkConfiguration.hs @@ -288,6 +288,7 @@ tests = , preProc = InheritPreProcess } ] - , mPreProc = Nothing + , mPreProc = noPreProcess + , mMonitorProc = Nothing , mPostProc = Nothing } diff --git a/bittide-instances/src/Bittide/Instances/Hitl/SyncInSyncOut.hs b/bittide-instances/src/Bittide/Instances/Hitl/SyncInSyncOut.hs index de976af26..99ec4c9f4 100644 --- a/bittide-instances/src/Bittide/Instances/Hitl/SyncInSyncOut.hs +++ b/bittide-instances/src/Bittide/Instances/Hitl/SyncInSyncOut.hs @@ -171,6 +171,7 @@ tests = , preProc = InheritPreProcess } ] - , mPreProc = Nothing + , mPreProc = noPreProcess + , mMonitorProc = Nothing , mPostProc = Nothing } diff --git a/bittide-instances/src/Bittide/Instances/Hitl/TemperatureMonitor.hs b/bittide-instances/src/Bittide/Instances/Hitl/TemperatureMonitor.hs index bbf836fa7..22a274973 100644 --- a/bittide-instances/src/Bittide/Instances/Hitl/TemperatureMonitor.hs +++ b/bittide-instances/src/Bittide/Instances/Hitl/TemperatureMonitor.hs @@ -23,6 +23,7 @@ import Bittide.Hitl ( hitlVioBool, paramForHwTargets, CasePreProcessing (..), + noPreProcess, ) import Bittide.Instances.Hitl.Setup (allHwTargets) @@ -115,6 +116,7 @@ tests = , preProc = InheritPreProcess } ] - , mPreProc = Nothing + , mPreProc = noPreProcess + , mMonitorProc = Nothing , mPostProc = Nothing } diff --git a/bittide-instances/src/Bittide/Instances/Hitl/Transceivers.hs b/bittide-instances/src/Bittide/Instances/Hitl/Transceivers.hs index e3f4ec1e1..71ebe04a5 100644 --- a/bittide-instances/src/Bittide/Instances/Hitl/Transceivers.hs +++ b/bittide-instances/src/Bittide/Instances/Hitl/Transceivers.hs @@ -233,7 +233,8 @@ tests = , externalHdl = [] , extraXdcFiles = [] , testCases = iters - , mPreProc = Nothing + , mPreProc = noPreProcess + , mMonitorProc = Nothing , mPostProc = Nothing } where diff --git a/bittide-instances/src/Bittide/Instances/Hitl/VexRiscv.hs b/bittide-instances/src/Bittide/Instances/Hitl/VexRiscv.hs index b39082124..a2242238e 100644 --- a/bittide-instances/src/Bittide/Instances/Hitl/VexRiscv.hs +++ b/bittide-instances/src/Bittide/Instances/Hitl/VexRiscv.hs @@ -21,8 +21,8 @@ import Protocols import Protocols.Wishbone import VexRiscv -import Vivado (VivadoHandle) -import Vivado.Tcl (HwTarget) +import Vivado +import Vivado.Tcl import Bittide.DoubleBufferedRam import Bittide.Hitl @@ -35,6 +35,7 @@ import Bittide.Wishbone import Paths_bittide_instances import System.IO +import System.Exit data TestStatus = Running | Success | Fail deriving (Enum, Eq, Generic, NFDataX, BitPack) @@ -171,13 +172,17 @@ tests = , preProc = InheritPreProcess } ] - , mPreProc = Just preProcessFunc + , mPreProc = preProcessFunc + , mMonitorProc = Just monitorFunc , mPostProc = Nothing } +monitorFunc :: VivadoHandle -> String -> FilePath -> [(HwTarget, c)] -> IO ExitCode +monitorFunc v _name ilaPath [(hwT, _preData)] = do + openHwT v hwT + execCmd_ v "set_property" ["PROBES.FILE", embrace ilaPath] + refresh_hw_device v [] -preProcessFunc :: VivadoHandle -> String -> HwTarget -> IO () -preProcessFunc _v _name _hwT = do gdbScript <- getDataFileName "data/gdb/test-gdb-prog" runGdbPicocomOpenOcd gdbScript $ \gdbOut (picocomIn, picocomOut) -> do @@ -191,4 +196,25 @@ preProcessFunc _v _name _hwT = do -- Test UART echo hPutStrLn picocomIn "Hello, UART!" waitForLine picocomOut "Hello, UART!" - pure () + + exitSuccess +monitorFunc _v _name _ilaPath _ = error "VexRiscv monitor func should only run with one hardware target" + +preProcessFunc :: VivadoHandle -> String -> HwTarget -> IO (TestStepResult ()) +preProcessFunc _v _name _hwT = do + pure $ TestStepSuccess () + + -- gdbScript <- getDataFileName "data/gdb/test-gdb-prog" + + -- runGdbPicocomOpenOcd gdbScript $ \gdbOut (picocomIn, picocomOut) -> do + -- -- This is the first thing that will print when the FPGA has been programmed + -- -- and starts executing the new program. + -- waitForLine picocomOut "Going in echo mode!" + + -- -- Wait for GDB to reach its last command - where it will wait indefinitely + -- waitForLine gdbOut "> continue" + + -- -- Test UART echo + -- hPutStrLn picocomIn "Hello, UART!" + -- waitForLine picocomOut "Hello, UART!" + -- pure () diff --git a/bittide-shake/exe/Main.hs b/bittide-shake/exe/Main.hs index 42c4405a5..6ef1c5f21 100644 --- a/bittide-shake/exe/Main.hs +++ b/bittide-shake/exe/Main.hs @@ -145,9 +145,6 @@ data Target = Target , targetTest :: Maybe HitlTestGroup -- ^ Whether target has a VIO probe that can be used to run hardware-in-the- -- loop tests. Note that this flag, 'targetTest', implies 'targetHasVio'. - , targetPostProcess :: Maybe (FilePath -> ExitCode -> IO ()) - -- ^ Function to run for post processing of ILA CSV data, or Nothing - -- if it has none. , targetExtraXdc :: [FilePath] -- ^ Extra constraints to be sourced. Will be sourced _after_ main XDC. , targetExternalHdl :: [TclGlobPattern] @@ -162,7 +159,6 @@ defTarget name = , targetHasXdc = False , targetHasVio = False , targetTest = Nothing - , targetPostProcess = Nothing , targetExtraXdc = [] , targetExternalHdl = [] } @@ -174,7 +170,6 @@ testTarget test@(HitlTestGroup{..}) = , targetHasXdc = True , targetHasVio = True , targetTest = Just test - , targetPostProcess = mPostProc , targetExtraXdc = extraXdcFiles , targetExternalHdl = externalHdl } @@ -573,16 +568,18 @@ main = do phony (entityName targetName <> ":test") $ do need [testExitCodePath] exitCode <- read <$> readFile' testExitCodePath - when (isJust targetPostProcess) $ do - liftIO $ (fromJust targetPostProcess) ilaDataDir exitCode + when (isJust (mPostProc =<< targetTest)) $ do + _ <- liftIO $ (fromJust $ mPostProc =<< targetTest) ilaDataDir exitCode + pure () unless (exitCode == ExitSuccess) $ do liftIO $ exitWith exitCode - when (isJust targetPostProcess) $ do + when (isJust (mPostProc =<< targetTest)) $ do phony (entityName targetName <> ":post-process") $ do need [testExitCodePath] exitCode <- read <$> readFile' testExitCodePath - liftIO $ (fromJust targetPostProcess) ilaDataDir exitCode + _ <- liftIO $ (fromJust (mPostProc =<< targetTest)) ilaDataDir exitCode + pure () if null shakeTargets then rules diff --git a/bittide-shake/src/Clash/Shake/Vivado.hs b/bittide-shake/src/Clash/Shake/Vivado.hs index a7204cb0f..8f79532ba 100644 --- a/bittide-shake/src/Clash/Shake/Vivado.hs +++ b/bittide-shake/src/Clash/Shake/Vivado.hs @@ -8,6 +8,7 @@ {-# LANGUAGE RecordWildCards #-} {-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE TypeApplications #-} +{-# LANGUAGE ViewPatterns #-} {- | Helper functions to do things like synthesis, place & route, bitstream generation, programming and running hardware tests for the Bittide project. @@ -45,8 +46,7 @@ import Control.Concurrent (threadDelay) import Control.Exception (try) import Control.Monad.Extra (andM, forM, forM_, orM, unless, when) import Data.Containers.ListUtils (nubOrd) -import Data.Either (lefts, rights) -import Data.Functor ((<&>)) +import Data.Either (lefts, rights, partitionEithers) import Data.List (elemIndex, isInfixOf, isSuffixOf, sort, sortOn, (\\)) import Data.List.Extra (anySame, split, (!?)) import Data.Map.Strict (fromList, keys, mapKeys, toAscList) @@ -56,10 +56,10 @@ import Data.String.Interpolate (__i) import Data.Text (unpack) import System.Clock (Clock (Monotonic), diffTimeSpec, getTime, toNanoSecs) import System.Directory (createDirectoryIfMissing) -import System.Exit (ExitCode (..)) +import System.Exit (ExitCode (..), exitFailure) import System.FilePath (dropFileName, ()) import Text.Read (readMaybe) -import Vivado (TclException (..), VivadoHandle, execPrint, execPrint_, with) +import Vivado (TclException (..), VivadoHandle, execPrint_, with) import Vivado.Tcl -- | Satisfied if all actions result in 'False' @@ -416,32 +416,6 @@ resolveHwTRefs v requestedHwTRefs = do <> "\n\tFound but unexpected: " <> show (foundFpgaIds \\ knownFpgaIds) -{- | Open the given hardware target and set the current hardware device to the -Xilinx FPGA on it. --} -openHwT :: VivadoHandle -> HwTarget -> IO () -openHwT v hwT = do - currentHwT <- current_hw_target v [] - currentIsOpened <- - execPrint v "get_property IS_OPENED [current_hw_target]" <&> \case - "1" -> True - "0" -> False - o -> error $ "Property IS_OPENED was " <> show o <> " where 0 or 1 was expected." - if currentHwT == hwT - then do - unless currentIsOpened $ - open_hw_target v [] - else do - when currentIsOpened $ - close_hw_target v ["-quiet"] - _ <- current_hw_target v [show hwT] - open_hw_target v [] - -- Assumes that the open target has the Xilinx device to program at index 0. - -- This is also what Xilinx does in its examples in UG908. - hwD <- current_hw_device v ["[lindex [get_hw_devices] 0]"] - when (null (fromHwDevice hwD)) $ - error "Setting the current hardware device failed." - programBitstream :: -- | Directory where the bitstream files are located FilePath -> @@ -725,7 +699,7 @@ verifyHwIlas v = do {- | Waits (with a timeout) until a HITL test case is finished by probing the probe_test_done probe. Returns whether the test case was successful. -} -waitTestCaseEnd :: VivadoHandle -> HitlTestCase HwTarget a b -> FilePath -> IO ExitCode +waitTestCaseEnd :: VivadoHandle -> HitlTestCase HwTarget a b c -> FilePath -> IO ExitCode waitTestCaseEnd v HitlTestCase{..} probesFilePath = do startTime <- getTime Monotonic let calcTimeSpentMs = (`div` 1000000) . toNanoSecs . diffTimeSpec startTime <$> getTime Monotonic @@ -786,7 +760,7 @@ runHitlTest :: -- | Filepath the the ILA data dump directory FilePath -> IO ExitCode -runHitlTest test@HitlTestGroup{topEntity, testCases, mPreProc} url probesFilePath ilaDataDir = do +runHitlTest test@HitlTestGroup{topEntity, testCases, mPreProc, mMonitorProc} url probesFilePath ilaDataDir = do putStrLn $ "Starting HITL test for FPGA design '" <> show topEntity @@ -827,7 +801,7 @@ runHitlTest test@HitlTestGroup{topEntity, testCases, mPreProc} url probesFilePat { parameters = mapKeys (fromJust . (`Map.lookup` refToHwTMap)) parameters , .. } - exitCode <- runHitlTestCase v resolvedTestCase mPreProc probesFilePath ilaDataDir + exitCode <- runHitlTestCase v resolvedTestCase mPreProc mMonitorProc probesFilePath ilaDataDir pure (name, exitCode) let failedTestCaseNames = fst <$> filter ((/= ExitSuccess) . snd) testResults @@ -851,19 +825,21 @@ runHitlTest test@HitlTestGroup{topEntity, testCases, mPreProc} url probesFilePat -- | Runs one test case of a HITL test group runHitlTestCase :: - forall a b. + forall a b c. -- | Handle to a Vivado object that is to execute the Tcl VivadoHandle -> -- | The HITL test case to run - HitlTestCase HwTarget a b -> + HitlTestCase HwTarget a b c -> -- | Pre-process function for the test group - Maybe (VivadoHandle -> String -> HwTarget -> IO ()) -> + (VivadoHandle -> String -> HwTarget -> IO (TestStepResult c)) -> + -- | Monitor function + Maybe (VivadoHandle -> String -> FilePath -> [(HwTarget, c)] -> IO ExitCode) -> -- | Path to the generated probes file FilePath -> -- | Filepath the the ILA data dump directory FilePath -> IO ExitCode -runHitlTestCase v testCase@HitlTestCase{..} preProcessFunc probesFilePath ilaDataDir = do +runHitlTestCase v testCase@HitlTestCase{..} preProcessFunc monitorFunc probesFilePath ilaDataDir = do if null parameters then do putStrLn @@ -874,7 +850,7 @@ runHitlTestCase v testCase@HitlTestCase{..} preProcessFunc probesFilePath ilaDat verifyHwIlas v -- XXX: We should not rely on start probe assertion order. -- See https://github.com/bittide/bittide-hardware/issues/638. - forM_ (sortOn (prettyShow . fst) (toAscList parameters)) $ \(hwT, param) -> do + testData <- forM (sortOn (prettyShow . fst) (toAscList parameters)) $ \(hwT, param) -> do openHwT v hwT execCmd_ v "set_property" ["PROBES.FILE", embrace probesFilePath, "[current_hw_device]"] refresh_hw_device v [] @@ -931,15 +907,12 @@ runHitlTestCase v testCase@HitlTestCase{..} preProcessFunc probesFilePath ilaDat commit_hw_vio v ["[get_hw_vios]"] -- run pre-processing - case preProc of - NoPreProcess -> pure () - InheritPreProcess -> case preProcessFunc of - Just f -> do - putStrLn $ - "Running test-group pre-process function for " - <> name <> " ('" <> prettyShow hwT <> "')" - f v name hwT - Nothing -> pure () + testRunData <- case preProc of + InheritPreProcess -> do + putStrLn $ + "Running test-group pre-process function for " + <> name <> " ('" <> prettyShow hwT <> "')" + preProcessFunc v name hwT CustomPreProcess f -> do putStrLn $ "Running case pre-process function for " @@ -947,13 +920,45 @@ runHitlTestCase v testCase@HitlTestCase{..} preProcessFunc probesFilePath ilaDat f v hwT - -- Assert HitlVio start probe - execCmd_ v "set_property" ["OUTPUT_VALUE", "1", getProbeTestStartTcl] - commit_hw_vio v ["[get_hw_vios]"] - putStrLn $ "Started test case for hardware target " <> prettyShow hwT <> "." + case testRunData of + TestStepFailure msg -> do + putStrLn $ + "pre-process step failed: " <> msg + pure $ Left (hwT, msg) - putStrLn $ "Waiting for test case '" <> name <> "' to end..." - testCaseExitCode <- waitTestCaseEnd v testCase probesFilePath + TestStepSuccess val -> do + pure $ Right (hwT, val) + + -- Assert HitlVio start probe + -- execCmd_ v "set_property" ["OUTPUT_VALUE", "1", getProbeTestStartTcl] + -- commit_hw_vio v ["[get_hw_vios]"] + -- putStrLn $ "Started test case for hardware target " <> prettyShow hwT <> "." + + -- pure testRunData + + let (failedTests, validTests) = partitionEithers testData + + testCaseExitCode0 <- case monitorFunc of + Just fn -> do + fn v name probesFilePath validTests + Nothing -> do + forM_ validTests $ \(hwT, _testData) -> do + -- Assert HitlVio start probe + openHwT v hwT + execCmd_ v "set_property" ["PROBES.FILE", embrace probesFilePath, "[current_hw_device]"] + refresh_hw_device v [] + + execCmd_ v "set_property" ["OUTPUT_VALUE", "1", getProbeTestStartTcl] + commit_hw_vio v ["[get_hw_vios]"] + putStrLn $ "Started test case for hardware target " <> prettyShow hwT <> "." + + putStrLn $ "Waiting for test case '" <> name <> "' to end..." + waitTestCaseEnd v testCase probesFilePath + + testCaseExitCode <- + if null failedTests + then pure testCaseExitCode0 + else exitFailure putStrLn "Saving captured ILA data (if relevant)..." forM_ (keys parameters) $ \hwT -> do diff --git a/bittide-tools/clockcontrol/plot/Main.hs b/bittide-tools/clockcontrol/plot/Main.hs index 10830bedc..75302d07d 100644 --- a/bittide-tools/clockcontrol/plot/Main.hs +++ b/bittide-tools/clockcontrol/plot/Main.hs @@ -487,8 +487,8 @@ knownTestsWithCcConf = Map.fromList (mapMaybe go hitlTests) justOrDie _ (Just x) = Just x justOrDie k Nothing = error $ "No CcConf for " <> show k - go HitlTestGroup{topEntity, testCases = iters :: [HitlTestCase HwTargetRef q r]} = - case cast @[HitlTestCase HwTargetRef q r] @[HitlTestCase HwTargetRef q CcConf] iters of + go HitlTestGroup{topEntity, testCases = iters :: [HitlTestCase HwTargetRef q r c]} = + case cast @[HitlTestCase HwTargetRef q r c] @[HitlTestCase HwTargetRef q CcConf c] iters of Just q -> Just ( show topEntity diff --git a/vivado-hs/src/Vivado/Tcl.hs b/vivado-hs/src/Vivado/Tcl.hs index 589571c64..88c251733 100644 --- a/vivado-hs/src/Vivado/Tcl.hs +++ b/vivado-hs/src/Vivado/Tcl.hs @@ -14,8 +14,35 @@ module Vivado.Tcl where import Control.Monad (unless, void, when) import Data.Maybe (listToMaybe) +import Data.Functor ((<&>)) import Vivado +{- | Open the given hardware target and set the current hardware device to the +Xilinx FPGA on it. +-} +openHwT :: VivadoHandle -> HwTarget -> IO () +openHwT v hwT = do + currentHwT <- current_hw_target v [] + currentIsOpened <- + execPrint v "get_property IS_OPENED [current_hw_target]" <&> \case + "1" -> True + "0" -> False + o -> error $ "Property IS_OPENED was " <> show o <> " where 0 or 1 was expected." + if currentHwT == hwT + then do + unless currentIsOpened $ + open_hw_target v [] + else do + when currentIsOpened $ + close_hw_target v ["-quiet"] + _ <- current_hw_target v [show hwT] + open_hw_target v [] + -- Assumes that the open target has the Xilinx device to program at index 0. + -- This is also what Xilinx does in its examples in UG908. + hwD <- current_hw_device v ["[lindex [get_hw_devices] 0]"] + when (null (fromHwDevice hwD)) $ + error "Setting the current hardware device failed." + -- | Executes a TCL command with an optional list of arguments. execCmd :: VivadoHandle -> String -> [String] -> IO String execCmd v cmd args = execPrint v $ unwords $ cmd : args