diff --git a/bittide-instances/bittide-instances.cabal b/bittide-instances/bittide-instances.cabal index 3ac6497152..1f43c49313 100644 --- a/bittide-instances/bittide-instances.cabal +++ b/bittide-instances/bittide-instances.cabal @@ -174,6 +174,7 @@ library Bittide.Instances.Hitl.Driver.ClockControl.Plot.Report Bittide.Instances.Hitl.Driver.ClockControl.Samples Bittide.Instances.Hitl.Driver.DnaOverSerial + Bittide.Instances.Hitl.Driver.FincFdec Bittide.Instances.Hitl.Driver.Si539xConfiguration Bittide.Instances.Hitl.Driver.VexRiscv Bittide.Instances.Hitl.Driver.VexRiscvTcp diff --git a/bittide-instances/data/constraints/fincFdecTests.xdc b/bittide-instances/data/constraints/fincFdecTests.xdc deleted file mode 100644 index e6a8d86f96..0000000000 --- a/bittide-instances/data/constraints/fincFdecTests.xdc +++ /dev/null @@ -1,23 +0,0 @@ -# SPDX-FileCopyrightText: 2022 Google LLC -# -# SPDX-License-Identifier: Apache-2.0 - -# SYSCLK_125 -set_property BOARD_PART_PIN sysclk_125_p [get_ports {CLK_125MHZ_p}] -set_property BOARD_PART_PIN sysclk_125_n [get_ports {CLK_125MHZ_n}] -# SMA_MGT_REFCLK -set_property BOARD_PART_PIN sma_mgt_refclk_p [get_ports {SMA_MGT_REFCLK_C_p}] -set_property BOARD_PART_PIN sma_mgt_refclk_n [get_ports {SMA_MGT_REFCLK_C_n}] - -# Vivado marks all clocks as related by default. Our external clocks are not -# though, which means that we need to explicitly mark them as unrelated (or -# "asynchronous"). -set_clock_groups \ - -asynchronous \ - -group [get_clocks -include_generated_clocks {CLK_125MHZ_p}] \ - -group [get_clocks -include_generated_clocks {SMA_MGT_REFCLK_C_p}] - -# GPIO_LED_0_LS -set_property BOARD_PART_PIN GPIO_LED_0_LS [get_ports {done}] -# GPIO_LED_1_LS -set_property BOARD_PART_PIN GPIO_LED_1_LS [get_ports {success}] diff --git a/bittide-instances/src/Bittide/Instances/Hitl/Driver/FincFdec.hs b/bittide-instances/src/Bittide/Instances/Hitl/Driver/FincFdec.hs new file mode 100644 index 0000000000..75eb95a157 --- /dev/null +++ b/bittide-instances/src/Bittide/Instances/Hitl/Driver/FincFdec.hs @@ -0,0 +1,94 @@ +-- SPDX-FileCopyrightText: 2025 Google LLC +-- +-- SPDX-License-Identifier: Apache-2.0 +{-# LANGUAGE OverloadedStrings #-} +{-# LANGUAGE PackageImports #-} + +module Bittide.Instances.Hitl.Driver.FincFdec where + +import Clash.Prelude + +import Control.Monad (forM_) +import Project.Chan +import Project.FilePath +import Project.Handle (assertEither) + +import Vivado.Tcl (HwTarget) +import Vivado.VivadoM + +import Bittide.Hitl +import Bittide.Instances.Hitl.Utils.Driver (assertProbe) +import Bittide.Instances.Hitl.Utils.Gdb (initGdb) +import Bittide.Instances.Hitl.Utils.OpenOcd (parseTapInfo) +import Bittide.Instances.Hitl.Utils.Serial (initSerial) +import Bittide.Instances.Hitl.Utils.Usb (resetUsbDeviceByLocation) +import "bittide-extra" Control.Exception.Extra (brackets) + +import Control.Concurrent.Async (forConcurrently_, mapConcurrently_) +import Control.Concurrent.Async.Extra (zipWithConcurrently3_) +import Control.Monad.IO.Class +import System.Exit +import System.FilePath + +import qualified Bittide.Instances.Hitl.Utils.OpenOcd as Ocd +import qualified Data.List as L +import qualified Gdb +import qualified System.Timeout.Extra as T + +driver :: + String -> + [(HwTarget, DeviceInfo)] -> + VivadoM ExitCode +driver _name targets = do + liftIO + $ putStrLn + $ "Running driver function for targets " + <> show ((\(_, info) -> info.deviceId) <$> targets) + + projectDir <- liftIO $ findParentContaining "cabal.project" + let hitlDir = projectDir "_build" "hitl" + + forM_ targets (assertProbe "probe_test_start") + + -- Reset USB adapter, see documentation of "Bittide.Instances.Hitl.Utils.Usb" + liftIO $ mapM_ (\(_, d) -> resetUsbDeviceByLocation d.usbAdapterLocation) targets + + let + expectedJtagIds = [0x0514C001] + toInitArgs (_, deviceInfo) targetIndex = + Ocd.InitOpenOcdArgs{deviceInfo, expectedJtagIds, hitlDir, targetIndex} + initArgs = L.zipWith toInitArgs targets [0 ..] + optionalInitArgs = L.repeat def + openOcdStarts = liftIO <$> L.zipWith Ocd.initOpenOcd initArgs optionalInitArgs + + brackets openOcdStarts (liftIO . (.cleanup)) $ \initOcdsData -> do + let + allTapInfos = parseTapInfo expectedJtagIds <$> initOcdsData + + peTapInfos :: [Ocd.TapInfo] + peTapInfos + | all (== L.length expectedJtagIds) (L.length <$> allTapInfos) + , [pes] <- L.transpose allTapInfos = + pes + | otherwise = + error + $ "Unexpected number of OpenOCD taps initialized. Expected: " + <> show (L.length expectedJtagIds) + <> ", but got: " + <> show (L.length <$> allTapInfos) + + Gdb.withGdbs (L.length targets) $ \gdbs -> do + liftIO $ zipWithConcurrently3_ (initGdb hitlDir "finc-fdec") gdbs peTapInfos targets + liftIO $ mapConcurrently_ ((assertEither =<<) . Gdb.loadBinary) gdbs + + let serialStarts = liftIO <$> L.zipWith (initSerial hitlDir) targets [0 ..] + brackets serialStarts (liftIO . snd) $ \(L.map fst -> serials) -> do + liftIO $ mapConcurrently_ Gdb.continue gdbs + + liftIO + $ T.tryWithTimeout T.PrintActionTime "Waiting for test success" 60_000_000 + $ forConcurrently_ serials + $ \serial -> + waitForLine serial "All tests passed" + + pure ExitSuccess diff --git a/bittide-instances/src/Bittide/Instances/Hitl/FincFdec.hs b/bittide-instances/src/Bittide/Instances/Hitl/FincFdec.hs index 9b440937d4..94e2f6ced8 100644 --- a/bittide-instances/src/Bittide/Instances/Hitl/FincFdec.hs +++ b/bittide-instances/src/Bittide/Instances/Hitl/FincFdec.hs @@ -8,205 +8,189 @@ FINC and FDEC pins. -} module Bittide.Instances.Hitl.FincFdec where +import Clash.Explicit.Prelude +import Clash.Prelude (HiddenClock, HiddenReset, withClock, withClockResetEnable, withReset) +import Protocols + import Clash.Annotations.TH (makeTopEntity) +import Clash.Class.BitPackC (ByteOrder) +import Clash.Cores.Uart (ValidBaud) import Clash.Cores.Xilinx (withXilinx) -import Clash.Explicit.Prelude -import Clash.Prelude (withClockResetEnable) import Clash.Xilinx.ClockGen (clockWizardDifferential) - -import Bittide.ClockControl ( - SpeedChange (NoChange, SlowDown, SpeedUp), - speedChangeToFincFdec, +import Protocols.MemoryMap (Access (WriteOnly), MemoryMap, Mm, getMMAny) +import Protocols.MemoryMap.Registers.WishboneStandard ( + RegisterConfig (..), + deviceConfig, + deviceWbI, + registerConfig, + registerWbI, ) -import Bittide.ClockControl.Si539xSpi (ConfigState (Finished), si539xSpi) -import Bittide.Counter (domainDiffCounter) +import Protocols.Spi +import VexRiscv + +import Bittide.ClockControl (FDEC, FINC, SpeedChange (..), speedChangeToFincFdec) +import Bittide.ClockControl.Si539xSpi (si539xSpiWb) +import Bittide.Counter (domainDiffCountersWbC) import Bittide.Hitl ( + HitlTestCase (..), HitlTestGroup (..), - hitlVio, - testCasesFromEnum, + hitlVioBool, + paramForHwTargets, ) -import Bittide.Instances.Common (commonSpiConfig) import Bittide.Instances.Domains import Bittide.Instances.Hitl.Setup (allHwTargets) +import Bittide.ProcessingElement (PeConfig (..), processingElement) +import Bittide.SharedTypes (BitboneMm, withLittleEndian) +import Bittide.Wishbone (timeWb, uartDf, uartInterfaceWb) -import Data.Maybe (isJust) +import GHC.Stack (HasCallStack) import System.FilePath (()) +import qualified Bittide.Cpus.Riscv32imc as Riscv32imc +import qualified Bittide.Instances.Hitl.Driver.FincFdec as Driver +import qualified Clash.Class.Cdc as Cdc import qualified Clash.Cores.Xilinx.Gth as Gth import qualified Protocols.Spi as Spi -data TestState = Busy | Fail | Success -data Test - = -- | Keep pressing FDEC, see if counter falls below certain threshold - FDec - | -- | Keep pressing FINC, see if counter exceeds certain threshold - FInc - | -- | 'FDec' test followed by an 'FInc' one - FDecInc - | -- | 'FInc' test followed by an 'FDec' one - FIncDec - deriving (Enum, Generic, NFDataX, Bounded, BitPack, ShowX, Show) - -{- | Counter threshold after which a test is considered passed/failed. In theory -clocks can diverge at +-20 kHz (at 200 MHz), which gives the tests 500 ms to -adjust their clocks - which should be plenty. +type Baud = 921_600 + +baud :: SNat Baud +baud = SNat + +{- | Memory mapped component to control the FINC and FDEC pins. Note that you still need to +convert from 'SpeedChange' to the FINC/FDEC pins using 'speedChangeToFincFdec'. -} -threshold :: Signed 32 -threshold = 20_000 - -testStateToDoneSuccess :: TestState -> (Bool, Bool) -testStateToDoneSuccess = \case - Busy -> (False, False) - Fail -> (True, False) - Success -> (True, True) - -goFincFdecTests :: - Clock Basic200 -> - Reset Basic200 -> - Clock Ext200 -> - Signal Basic200 Test -> - Signal Basic200 Spi.S2M -> - "" - ::: ( Signal Basic200 TestState - , -- Freq increase / freq decrease request to clock board - "" - ::: ( "FINC" ::: Signal Basic200 Bool - , "FDEC" ::: Signal Basic200 Bool - ) - , -- SPI to clock board: - "" ::: Signal Basic200 Spi.M2S - , -- Debug signals: - "" - ::: ( "SPI_BUSY" ::: Signal Basic200 Bool - , "SPI_STATE" ::: Signal Basic200 (BitVector 40) - , "SI_LOCKED" ::: Signal Basic200 Bool - , "COUNTER_ACTIVE" ::: Signal Basic200 Bool - , "COUNTER" ::: Signal Basic200 (Signed 32) - ) - ) -goFincFdecTests clk rst clkControlled testSelect spiS2M = - (testResult, fIncDec, spiM2S, debugSignals) +hardwareSpeedChange :: + forall aw dom. + ( HasCallStack + , HiddenClock dom + , HiddenReset dom + , KnownNat aw + , 1 <= aw + , ?byteOrder :: ByteOrder + ) => + Circuit (BitboneMm dom aw) (CSignal dom SpeedChange) +hardwareSpeedChange = circuit $ \(mm, wb) -> do + [speedChangeBus] <- deviceWbI (deviceConfig "HardwareSpeedChange") -< (mm, wb) + + (speedChange, _speedChangeActivity) <- + registerWbI speedChangeConfig NoChange -< (speedChangeBus, Fwd (pure Nothing)) + + idC -< speedChange + where + speedChangeConfig = (registerConfig "speed_change" ""){access = WriteOnly} + +fincFdecPe :: + forall free sky vendor. + ( HasCallStack + , KnownDomain free + , KnownDomain sky + , HasSynchronousReset free + , HasSynchronousReset sky + , 1 <= DomainPeriod free + , ValidBaud free Baud + , ?byteOrder :: ByteOrder + , Cdc.HiddenVendor vendor + , Cdc.ValidGray vendor 8 sky free + , Cdc.GrayConstraints vendor 8 sky free + ) => + Clock free -> + Reset free -> + Clock sky -> + Circuit + ( ToConstBwd Mm + , Jtag free + ) + ( "UART_TX" ::: CSignal free Bit + , Spi free + , CSignal free SpeedChange + ) +fincFdecPe freeClk freeRst skyClk = circuit $ \(mm, jtag) -> do + [timeBus, uartBus, siBus, dcBus, speedChangeBus] <- + withClockResetEnable freeClk freeRst enableGen + $ processingElement NoDumpVcd peConfig + -< (mm, jtag) + + Fwd _localCounter <- withClockResetEnable freeClk freeRst enableGen $ timeWb Nothing -< timeBus + (uartTx, _uartStatus) <- + withClockResetEnable freeClk freeRst enableGen + $ uartInterfaceWb d16 d16 + $ uartDf baud + -< (uartBus, Fwd (pure low)) + (Fwd spiDone, spiOut) <- + withClockResetEnable freeClk freeRst enableGen + $ si539xSpiWb (SNat @(Microseconds 10)) + -< siBus + + let skyRst = convertReset freeClk skyClk (unsafeFromActiveLow spiDone) + Fwd _domainDiff <- domainDiffCountersWbC (skyClk :> Nil) (skyRst :> Nil) freeClk freeRst -< dcBus + + speedChange <- withClock freeClk $ withReset freeRst $ hardwareSpeedChange -< speedChangeBus + + idC -< (uartTx, spiOut, speedChange) where - debugSignals = (spiBusy, pack <$> spiState, siClkLocked, counterActive, counter) - - (_, spiBusy, spiState@(fmap (== Finished) -> siClkLocked), spiM2S) = - withClockResetEnable clk rst enableGen - $ si539xSpi - commonSpiConfig - (SNat @(Microseconds 1)) - (pure Nothing) - spiS2M - - rstTest = unsafeFromActiveLow siClkLocked - rstControlled = convertReset clk clkControlled rst - - (counter, counterActive) = - unbundle - $ - -- Note that in a "real" Bittide system the clocks would be wired up the - -- other way around: the controlled domain would be the target domain. We - -- don't do that here because we know 'rstControlled' will come out of - -- reset much earlier than 'rstTest'. Doing it the "proper" way would - -- therefore introduce extra complexity, without adding to the test's - -- coverage. - (withXilinx domainDiffCounter) clkControlled rstControlled clk rstTest - - fIncDec = unbundle $ speedChangeToFincFdec clk rstTest fIncDecRequest - - (fIncDecRequest, testResult) = - unbundle - $ (!!) - <$> bundle (fDecResult :> fIncResult :> fDecIncResult :> fIncDecResult :> Nil) - <*> fmap fromEnum testSelect - - fDecResult = goFdec <$> counter - fIncResult = goFinc <$> counter - fDecIncResult = mealy clk rstTest enableGen goFdecFinc FDec counter - fIncDecResult = mealy clk rstTest enableGen goFincFdec FInc counter - - -- Keep pressing FDEC, expect counter to go below -@threshold@ - goFdec :: Signed 32 -> (SpeedChange, TestState) - goFdec n - | n > threshold = (NoChange, Fail) - | n < -threshold = (NoChange, Success) - | otherwise = (SlowDown, Busy) - - -- Keep pressing FINC, expect counter to go above @threshold@ - goFinc :: Signed 32 -> (SpeedChange, TestState) - goFinc n - | n > threshold = (NoChange, Success) - | n < -threshold = (NoChange, Fail) - | otherwise = (SpeedUp, Busy) - - -- Keep pressing FDEC, expect counter to go below -@threshold@, then keep pressing - -- FINC, expect counter to go above 0. - goFdecFinc :: Test -> Signed 32 -> (Test, (SpeedChange, TestState)) - goFdecFinc FDec n - | n > threshold = (FDec, (NoChange, Fail)) - | n < -threshold = (FInc, (NoChange, Busy)) - | otherwise = (FDec, (SlowDown, Busy)) - goFdecFinc FInc n - | n > 0 = (FInc, (NoChange, Success)) - | n < -(3 * threshold) = (FInc, (NoChange, Fail)) - | otherwise = (FInc, (SpeedUp, Busy)) - goFdecFinc s _ = (s, (NoChange, Fail)) -- Illegal state - - -- Keep pressing FINC, expect counter to go above @threshold@, then keep pressing - -- FDEC, expect counter to go below 0. - goFincFdec :: Test -> Signed 32 -> (Test, (SpeedChange, TestState)) - goFincFdec FInc n - | n > threshold = (FDec, (NoChange, Busy)) - | n < -threshold = (FInc, (NoChange, Fail)) - | otherwise = (FInc, (SpeedUp, Busy)) - goFincFdec FDec n - | n > (3 * threshold) = (FDec, (NoChange, Fail)) - | n < 0 = (FDec, (NoChange, Success)) - | otherwise = (FDec, (SlowDown, Busy)) - goFincFdec s _ = (s, (NoChange, Fail)) -- Illegal state + peConfig = + PeConfig + { cpu = Riscv32imc.vexRiscv0 + , depthI = SNat @(Div (8 * 1024) 4) + , depthD = SNat @(Div (8 * 1024) 4) + , initI = Nothing + , initD = Nothing + , iBusTimeout = d0 + , dBusTimeout = d0 + , includeIlaWb = False + } fincFdecTests :: -- Pins from internal oscillator: "CLK_125MHZ" ::: DiffClock Ext125 -> -- Pins from clock board: - "SMA_MGT_REFCLK_C" ::: DiffClock Ext200 -> - Signal Basic200 Spi.S2M -> + "SMA_MGT_REFCLK_C" ::: DiffClock Ext125 -> + "JTAG" ::: Signal Basic125 JtagIn -> + "USB_UART_TXD" ::: Signal Basic125 Bit -> + Signal Basic125 Spi.S2M -> "" - ::: ( "" - ::: ( "done" ::: Signal Basic200 Bool - , "success" ::: Signal Basic200 Bool - ) + ::: ( "JTAG" ::: Signal Basic125 JtagOut + , "USB_UART_RXD" ::: Signal Basic125 Bit + , -- SPI to clock board: + "" ::: Signal Basic125 Spi.M2S , -- Freq increase / freq decrease request to clock board "" - ::: ( "FINC" ::: Signal Basic200 Bool - , "FDEC" ::: Signal Basic200 Bool + ::: ( "FINC" ::: Signal Basic125 FINC + , "FDEC" ::: Signal Basic125 FDEC ) - , -- SPI to clock board: - "" ::: Signal Basic200 Spi.M2S ) -fincFdecTests diffClk controlledDiffClock spiS2M = - ((testDone, testSuccess), fIncDec, spiM2S) +fincFdecTests freeClkDiff boardClkDiff jtagIn _uartRx spiS2M = + (jtagOut, uartTx, spiM2S, fincFdec) where - (_, odivClk) = Gth.ibufds_gte3 controlledDiffClock - clkControlled = Gth.bufgGt d0 odivClk noReset + freeClk :: Clock Basic125 + freeRst :: Reset Basic125 + (freeClk, freeRst) = clockWizardDifferential freeClkDiff noReset + + (_, odivClk) = Gth.ibufds_gte3 boardClkDiff - (clk, clkStableRst) = clockWizardDifferential diffClk noReset + boardClk :: Clock Basic125A + boardClk = Gth.bufgGt d0 odivClk noReset - started = isJust <$> testInput - testRst = orReset clkStableRst (unsafeFromActiveLow started) + testStart :: Signal Basic125 Bool + testStart = hitlVioBool freeClk testStart (pure True) - (testResult, fIncDec, spiM2S, _debugSignals) = - goFincFdecTests clk testRst clkControlled (fromJustX <$> testInput) spiS2M + testRst = unsafeFromActiveLow testStart `orReset` freeRst - (testDone, testSuccess) = unbundle $ testStateToDoneSuccess <$> testResult + ((_memoryMap, jtagOut), (uartTx, spiM2S, speedChange)) = + toSignals + (withXilinx $ withLittleEndian $ fincFdecPe freeClk testRst boardClk) + (((), jtagIn), ((), spiS2M, ())) - -- For debugging, add to separate VIO? - -- clkStable1 = unsafeToActiveLow clkStableRst - -- testRstBool = unsafeToActiveHigh testRst - -- (fInc, fDec) = fIncDec - -- (spiBusy, spiState, siClkLocked, counterActive, counter) = debugSignals + fincFdec = unbundle $ speedChangeToFincFdec freeClk testRst speedChange + +memoryMap :: MemoryMap +memoryMap = + getMMAny + $ withXilinx + $ withLittleEndian + $ fincFdecPe @Basic125 @Basic125A clockGen resetGen clockGen - testInput :: Signal Basic200 (Maybe Test) - testInput = hitlVio FDec clk testDone testSuccess {-# OPAQUE fincFdecTests #-} makeTopEntity 'fincFdecTests @@ -215,12 +199,21 @@ tests = HitlTestGroup { topEntity = 'fincFdecTests , targetXdcs = - [ "fincFdecTests.xdc" + [ "si539xConfigTest.xdc" + , "jtag" "config.xdc" + , "jtag" "pmod1.xdc" + , "uart" "pmod1.xdc" , "si539x" "fincfdec.xdc" , "si539x" "spi.xdc" ] , externalHdl = [] - , testCases = testCasesFromEnum @Test allHwTargets () - , mDriverProc = Nothing + , testCases = + [ HitlTestCase + { name = "FincFdecTests" + , parameters = paramForHwTargets allHwTargets () + , postProcData = () + } + ] + , mDriverProc = Just Driver.driver , mPostProc = Nothing } diff --git a/bittide-instances/src/Bittide/Instances/Hitl/GenericDemo/BringUp.hs b/bittide-instances/src/Bittide/Instances/Hitl/GenericDemo/BringUp.hs index a01d091e32..1cf03925d2 100644 --- a/bittide-instances/src/Bittide/Instances/Hitl/GenericDemo/BringUp.hs +++ b/bittide-instances/src/Bittide/Instances/Hitl/GenericDemo/BringUp.hs @@ -186,11 +186,9 @@ bringUp bufferDepth mkUserCore refClk refRst = frequencyAdjustments :: Signal Bittide (FINC, FDEC) frequencyAdjustments = delay bittideClk enableGen minBound - $ speedChangeToStickyPins + $ speedChangeToFincFdec bittideClk bittideRst - enableGen - (SNat @Si539xHoldTime) speedChanges idC -< (spi, sync, uartTx, Fwd frequencyAdjustments) diff --git a/bittide-instances/src/Bittide/Instances/Hitl/GenericDemo/Core.hs b/bittide-instances/src/Bittide/Instances/Hitl/GenericDemo/Core.hs index d1378e3fda..96dfffa618 100644 --- a/bittide-instances/src/Bittide/Instances/Hitl/GenericDemo/Core.hs +++ b/bittide-instances/src/Bittide/Instances/Hitl/GenericDemo/Core.hs @@ -57,7 +57,7 @@ import Clash.Prelude ( import Protocols import Bittide.CaptureUgn (captureUgns, sendUgn) -import Bittide.ClockControl (SpeedChange) +import Bittide.ClockControl (SpeedChange (NoChange)) import Bittide.ClockControl.CallistoSw (SwcccInternalBusses, callistoSwClockControlC) import Bittide.DoubleBufferedRam (wbStorage) import Bittide.ElasticBuffer (fromData, xilinxElasticBufferWb) @@ -244,7 +244,7 @@ core :: , "CC_SUITABLE" ::: CSignal Bittide (BitVector LinkCount) , "RXS" ::: Vec LinkCount (CSignal GthRx (BitVector 64)) ) - ( CSignal Bittide (Maybe SpeedChange) + ( CSignal Bittide SpeedChange , "TXS" ::: Vec LinkCount (CSignal Bittide (BitVector 64)) , Sync Bittide Basic125 , "UARTS" ::: Vec InternalCpuCount (Df Bittide (BitVector 8)) @@ -374,7 +374,7 @@ core bufferDepth mkUserCore (refClk, refRst) (bitClk, bitRst, bitEna) rxClocks r in if simulateCc then swCcOut0 - else pure Nothing + else pure NoChange else swCcOut0 -- Stop clock control diff --git a/bittide-instances/src/Bittide/Instances/MemoryMaps.hs b/bittide-instances/src/Bittide/Instances/MemoryMaps.hs index 4889f9e996..4fb49c1bff 100644 --- a/bittide-instances/src/Bittide/Instances/MemoryMaps.hs +++ b/bittide-instances/src/Bittide/Instances/MemoryMaps.hs @@ -21,6 +21,7 @@ import Language.Haskell.TH (reportError, runIO) import System.Directory (createDirectoryIfMissing, removePathForcibly) import System.FilePath +import qualified Bittide.Instances.Hitl.FincFdec as FincFdec import qualified Bittide.Instances.Hitl.Si539xConfiguration as Si539xConfiguration import qualified Bittide.Instances.Hitl.SoftUgnDemo.MemoryMaps as SoftUgnDemo import qualified Bittide.Instances.Hitl.WireDemo.MemoryMaps as WireDemo @@ -53,6 +54,7 @@ $( do , ("DnaPortE2Test", DnaPortE2.dutMm) , ("Ethernet", vexRiscvEthernetMM) , ("ElasticBufferWbTest", ElasticBufferWb.dutMM) + , ("FincFdecTests", FincFdec.memoryMap) , ("Freeze", freezeMM) , ("NestedInterconnect", NestedInterconnect.nestedInterconnectMm) , ("ProcessingElement", vexRiscvUartHelloMM) diff --git a/bittide/data/clock_configs/Si5395J-125MHz-10ppb-Registers.csv b/bittide/data/clock_configs/Si5395J-125MHz-10ppb-Registers.csv new file mode 100644 index 0000000000..35a5e5aeff --- /dev/null +++ b/bittide/data/clock_configs/Si5395J-125MHz-10ppb-Registers.csv @@ -0,0 +1,622 @@ +# Si534x/7x/8x/9x Registers Script +# +# Part: Si5395 +# Project File: \\VBOXSVR\win_vm_shared\Si5395J-125MHz-10ppb.slabtimeproj +# Design ID: 125_10b +# Includes Pre/Post Download Control Register Writes: Yes +# Device Revision: A +# Creator: ClockBuilder Pro v4.11.0.1 [2023-09-14] +# Created On: 2026-06-09 16:17:14 GMT+02:00 +Address,Data +# +# Start configuration preamble +0x0B24,0xC0 +0x0B25,0x00 +0x0540,0x01 +# End configuration preamble +# +# Delay 300 msec +# Delay is worst case time for device to complete any calibration +# that is running due to device state change previous to this script +# being processed. +# +# Start configuration registers +0x0006,0x00 +0x0007,0x00 +0x0008,0x00 +0x000B,0x68 +0x0016,0x02 +0x0017,0xDC +0x0018,0xFF +0x0019,0xFF +0x001A,0xFF +0x0023,0xFF +0x0024,0x0F +0x0025,0x00 +0x0026,0x00 +0x0027,0x00 +0x0028,0x00 +0x002B,0x02 +0x002C,0x00 +0x002D,0x00 +0x002E,0x00 +0x002F,0x00 +0x0030,0x00 +0x0031,0x00 +0x0032,0x00 +0x0033,0x00 +0x0034,0x00 +0x0035,0x00 +0x0036,0x00 +0x0037,0x00 +0x0038,0x00 +0x0039,0x00 +0x003A,0x00 +0x003B,0x00 +0x003C,0x00 +0x003D,0x00 +0x003E,0x00 +0x003F,0x00 +0x0040,0x04 +0x0041,0x00 +0x0042,0x00 +0x0043,0x00 +0x0044,0x00 +0x0045,0x0C +0x0046,0x00 +0x0047,0x00 +0x0048,0x00 +0x0049,0x00 +0x004A,0x00 +0x004B,0x00 +0x004C,0x00 +0x004D,0x00 +0x004E,0x00 +0x004F,0x00 +0x0050,0x0F +0x0051,0x00 +0x0052,0x00 +0x0053,0x00 +0x0054,0x00 +0x0055,0x00 +0x0056,0x00 +0x0057,0x00 +0x0058,0x00 +0x0059,0x00 +0x005A,0x00 +0x005B,0x00 +0x005C,0x00 +0x005D,0x00 +0x005E,0x00 +0x005F,0x00 +0x0060,0x00 +0x0061,0x00 +0x0062,0x00 +0x0063,0x00 +0x0064,0x00 +0x0065,0x00 +0x0066,0x00 +0x0067,0x00 +0x0068,0x00 +0x0069,0x00 +0x0092,0x00 +0x0093,0x00 +0x0095,0x00 +0x0096,0x00 +0x0098,0x00 +0x009A,0x00 +0x009B,0x00 +0x009D,0x00 +0x009E,0x00 +0x00A0,0x00 +0x00A2,0x00 +0x00A9,0x00 +0x00AA,0x00 +0x00AB,0x00 +0x00AC,0x00 +0x00E5,0x01 +0x00EA,0x00 +0x00EB,0x00 +0x00EC,0x00 +0x00ED,0x00 +0x0102,0x01 +0x0103,0x02 +0x0104,0x09 +0x0105,0x3E +0x0106,0x18 +0x0108,0x01 +0x0109,0x09 +0x010A,0x3B +0x010B,0x28 +0x010D,0x01 +0x010E,0x09 +0x010F,0x3B +0x0110,0x28 +0x0112,0x01 +0x0113,0x09 +0x0114,0x3B +0x0115,0x28 +0x0117,0x01 +0x0118,0x09 +0x0119,0x3B +0x011A,0x28 +0x011C,0x01 +0x011D,0x09 +0x011E,0x3B +0x011F,0x28 +0x0121,0x01 +0x0122,0x09 +0x0123,0x3B +0x0124,0x28 +0x0126,0x01 +0x0127,0x09 +0x0128,0x3B +0x0129,0x28 +0x012B,0x01 +0x012C,0x09 +0x012D,0x3B +0x012E,0x28 +0x0130,0x01 +0x0131,0x09 +0x0132,0x3B +0x0133,0x28 +0x0135,0x02 +0x0136,0x09 +0x0137,0x3E +0x0138,0x19 +0x013A,0x02 +0x013B,0x09 +0x013C,0x3E +0x013D,0x19 +0x013F,0x00 +0x0140,0x00 +0x0141,0x40 +0x0142,0xFF +0x0206,0x00 +0x0208,0x00 +0x0209,0x00 +0x020A,0x00 +0x020B,0x00 +0x020C,0x00 +0x020D,0x00 +0x020E,0x00 +0x020F,0x00 +0x0210,0x00 +0x0211,0x00 +0x0212,0x00 +0x0213,0x00 +0x0214,0x00 +0x0215,0x00 +0x0216,0x00 +0x0217,0x00 +0x0218,0x00 +0x0219,0x00 +0x021A,0x00 +0x021B,0x00 +0x021C,0x00 +0x021D,0x00 +0x021E,0x00 +0x021F,0x00 +0x0220,0x00 +0x0221,0x00 +0x0222,0x00 +0x0223,0x00 +0x0224,0x00 +0x0225,0x00 +0x0226,0x00 +0x0227,0x00 +0x0228,0x00 +0x0229,0x00 +0x022A,0x00 +0x022B,0x00 +0x022C,0x00 +0x022D,0x00 +0x022E,0x00 +0x022F,0x00 +0x0231,0x0B +0x0232,0x0B +0x0233,0x0B +0x0234,0x0B +0x0235,0x00 +0x0236,0x00 +0x0237,0x00 +0x0238,0x80 +0x0239,0x89 +0x023A,0x00 +0x023B,0x00 +0x023C,0x00 +0x023D,0x00 +0x023E,0x80 +0x0247,0x04 +0x0248,0x00 +0x0249,0x00 +0x024A,0x00 +0x024B,0x00 +0x024C,0x00 +0x024D,0x00 +0x024E,0x00 +0x024F,0x00 +0x0250,0x00 +0x0251,0x00 +0x0252,0x00 +0x0253,0x00 +0x0254,0x00 +0x0255,0x00 +0x0256,0x00 +0x0257,0x00 +0x0258,0x00 +0x0259,0x00 +0x025A,0x00 +0x025B,0x00 +0x025C,0x00 +0x025D,0x00 +0x025E,0x00 +0x025F,0x00 +0x0260,0x00 +0x0261,0x00 +0x0262,0x00 +0x0263,0x00 +0x0264,0x00 +0x0265,0x4F +0x0266,0x00 +0x0267,0x00 +0x0268,0x04 +0x0269,0x00 +0x026A,0x00 +0x026B,0x31 +0x026C,0x32 +0x026D,0x35 +0x026E,0x5F +0x026F,0x31 +0x0270,0x30 +0x0271,0x62 +0x0272,0x00 +0x028A,0x00 +0x028B,0x00 +0x028C,0x00 +0x028D,0x00 +0x028E,0x00 +0x028F,0x00 +0x0290,0x00 +0x0291,0x00 +0x0292,0x3F +0x0293,0x2F +0x0294,0x80 +0x0296,0x00 +0x0297,0x00 +0x0299,0x00 +0x029D,0x00 +0x029E,0x00 +0x029F,0x00 +0x02A9,0x00 +0x02AA,0x00 +0x02AB,0x00 +0x02B7,0xFF +0x02BC,0x00 +0x0302,0x00 +0x0303,0x00 +0x0304,0x00 +0x0305,0x40 +0x0306,0x08 +0x0307,0x00 +0x0308,0x00 +0x0309,0x00 +0x030A,0x00 +0x030B,0xC8 +0x030C,0x00 +0x030D,0x00 +0x030E,0x00 +0x030F,0x00 +0x0310,0x40 +0x0311,0x08 +0x0312,0x00 +0x0313,0x00 +0x0314,0x00 +0x0315,0x00 +0x0316,0xC8 +0x0317,0x00 +0x0318,0x00 +0x0319,0x00 +0x031A,0x00 +0x031B,0x00 +0x031C,0x00 +0x031D,0x00 +0x031E,0x00 +0x031F,0x00 +0x0320,0x00 +0x0321,0x00 +0x0322,0x00 +0x0323,0x00 +0x0324,0x00 +0x0325,0x00 +0x0326,0x00 +0x0327,0x00 +0x0328,0x00 +0x0329,0x00 +0x032A,0x00 +0x032B,0x00 +0x032C,0x00 +0x032D,0x00 +0x032E,0x00 +0x032F,0x00 +0x0330,0x00 +0x0331,0x00 +0x0332,0x00 +0x0333,0x00 +0x0334,0x00 +0x0335,0x00 +0x0336,0x00 +0x0337,0x00 +0x0338,0x00 +0x0339,0x1C +0x033B,0x62 +0x033C,0x01 +0x033D,0x00 +0x033E,0x00 +0x033F,0x00 +0x0340,0x00 +0x0341,0x62 +0x0342,0x01 +0x0343,0x00 +0x0344,0x00 +0x0345,0x00 +0x0346,0x00 +0x0347,0x00 +0x0348,0x00 +0x0349,0x00 +0x034A,0x00 +0x034B,0x00 +0x034C,0x00 +0x034D,0x00 +0x034E,0x00 +0x034F,0x00 +0x0350,0x00 +0x0351,0x00 +0x0352,0x00 +0x0353,0x00 +0x0354,0x00 +0x0355,0x00 +0x0356,0x00 +0x0357,0x00 +0x0358,0x00 +0x0359,0x00 +0x035A,0x00 +0x035B,0x00 +0x035C,0x00 +0x035D,0x00 +0x035E,0x00 +0x035F,0x00 +0x0360,0x00 +0x0361,0x00 +0x0362,0x00 +0x0487,0x00 +0x0508,0x00 +0x0509,0x00 +0x050A,0x00 +0x050B,0x00 +0x050C,0x00 +0x050D,0x00 +0x050E,0x00 +0x050F,0x00 +0x0510,0x00 +0x0511,0x00 +0x0512,0x00 +0x0513,0x00 +0x0515,0x00 +0x0516,0x00 +0x0517,0x00 +0x0518,0x00 +0x0519,0x00 +0x051A,0x00 +0x051B,0x00 +0x051C,0x00 +0x051D,0x00 +0x051E,0x00 +0x051F,0x00 +0x0521,0x2B +0x052A,0x01 +0x052B,0x01 +0x052C,0x0F +0x052D,0x03 +0x052E,0x00 +0x052F,0x00 +0x0531,0x00 +0x0532,0x00 +0x0533,0x04 +0x0534,0x00 +0x0535,0x01 +0x0536,0x04 +0x0537,0x00 +0x0538,0x00 +0x0539,0x00 +0x053D,0x0A +0x053E,0x06 +0x0588,0x00 +0x0589,0x0C +0x058A,0x00 +0x058B,0x00 +0x058C,0x00 +0x058D,0x00 +0x059B,0x18 +0x059C,0x0C +0x059D,0x00 +0x059E,0x00 +0x059F,0x00 +0x05A0,0x00 +0x05A1,0x00 +0x05A2,0x00 +0x05A4,0x20 +0x05A5,0x00 +0x05A6,0x00 +0x05AC,0x00 +0x05AD,0x00 +0x05AE,0x00 +0x05B1,0x00 +0x05B2,0x00 +0x0802,0x35 +0x0803,0x05 +0x0804,0x01 +0x0805,0x00 +0x0806,0x00 +0x0807,0x00 +0x0808,0x00 +0x0809,0x00 +0x080A,0x00 +0x080B,0x00 +0x080C,0x00 +0x080D,0x00 +0x080E,0x00 +0x080F,0x00 +0x0810,0x00 +0x0811,0x00 +0x0812,0x00 +0x0813,0x00 +0x0814,0x00 +0x0815,0x00 +0x0816,0x00 +0x0817,0x00 +0x0818,0x00 +0x0819,0x00 +0x081A,0x00 +0x081B,0x00 +0x081C,0x00 +0x081D,0x00 +0x081E,0x00 +0x081F,0x00 +0x0820,0x00 +0x0821,0x00 +0x0822,0x00 +0x0823,0x00 +0x0824,0x00 +0x0825,0x00 +0x0826,0x00 +0x0827,0x00 +0x0828,0x00 +0x0829,0x00 +0x082A,0x00 +0x082B,0x00 +0x082C,0x00 +0x082D,0x00 +0x082E,0x00 +0x082F,0x00 +0x0830,0x00 +0x0831,0x00 +0x0832,0x00 +0x0833,0x00 +0x0834,0x00 +0x0835,0x00 +0x0836,0x00 +0x0837,0x00 +0x0838,0x00 +0x0839,0x00 +0x083A,0x00 +0x083B,0x00 +0x083C,0x00 +0x083D,0x00 +0x083E,0x00 +0x083F,0x00 +0x0840,0x00 +0x0841,0x00 +0x0842,0x00 +0x0843,0x00 +0x0844,0x00 +0x0845,0x00 +0x0846,0x00 +0x0847,0x00 +0x0848,0x00 +0x0849,0x00 +0x084A,0x00 +0x084B,0x00 +0x084C,0x00 +0x084D,0x00 +0x084E,0x00 +0x084F,0x00 +0x0850,0x00 +0x0851,0x00 +0x0852,0x00 +0x0853,0x00 +0x0854,0x00 +0x0855,0x00 +0x0856,0x00 +0x0857,0x00 +0x0858,0x00 +0x0859,0x00 +0x085A,0x00 +0x085B,0x00 +0x085C,0x00 +0x085D,0x00 +0x085E,0x00 +0x085F,0x00 +0x0860,0x00 +0x0861,0x00 +0x090E,0x02 +0x0943,0x01 +0x0949,0x00 +0x094A,0x00 +0x094E,0x49 +0x094F,0xF2 +0x095E,0x00 +0x0A02,0x00 +0x0A03,0x03 +0x0A04,0x00 +0x0A05,0x03 +0x0A14,0x00 +0x0A1A,0x00 +0x0A20,0x00 +0x0A26,0x00 +0x0A2C,0x00 +0x0A38,0x00 +0x0A39,0x00 +0x0A3A,0x00 +0x0A3C,0x00 +0x0A3D,0x00 +0x0A3E,0x00 +0x0A40,0x00 +0x0A41,0x00 +0x0A42,0x00 +0x0A44,0x00 +0x0A45,0x00 +0x0A46,0x00 +0x0A48,0x00 +0x0A49,0x00 +0x0A4A,0x00 +0x0A4C,0x00 +0x0A4D,0x00 +0x0A4E,0x00 +0x0A4F,0x00 +0x0A50,0x00 +0x0A51,0x00 +0x0A52,0x00 +0x0A53,0x00 +0x0A54,0x00 +0x0A55,0x00 +0x0A56,0x00 +0x0A57,0x00 +0x0A58,0x00 +0x0A59,0x00 +0x0A5A,0x00 +0x0A5B,0x00 +0x0A5C,0x00 +0x0A5D,0x00 +0x0A5E,0x00 +0x0A5F,0x00 +0x0B44,0x0F +0x0B46,0x00 +0x0B47,0x0F +0x0B48,0x0F +0x0B4A,0x1C +0x0B57,0x0E +0x0B58,0x01 +0x0C02,0x03 +0x0C03,0x00 +0x0C07,0x00 +0x0C08,0x00 +# End configuration registers +# +# Start configuration postamble +0x0514,0x01 +0x001C,0x01 +0x0540,0x00 +0x0B24,0xC3 +0x0B25,0x02 +# End configuration postamble diff --git a/bittide/data/clock_configs/Si5395J-125MHz-10ppb.slabtimeproj b/bittide/data/clock_configs/Si5395J-125MHz-10ppb.slabtimeproj new file mode 100644 index 0000000000..9dd563e1ec Binary files /dev/null and b/bittide/data/clock_configs/Si5395J-125MHz-10ppb.slabtimeproj differ diff --git a/bittide/src/Bittide/ClockControl.hs b/bittide/src/Bittide/ClockControl.hs index ef8b9b0ada..133c16051c 100644 --- a/bittide/src/Bittide/ClockControl.hs +++ b/bittide/src/Bittide/ClockControl.hs @@ -8,13 +8,9 @@ module Bittide.ClockControl ( FDEC, RelDataCount, SpeedChange (..), - Si539xHoldTime, - Si539xMinUpdatePeriod, sign, speedChangeToFincFdec, speedChangeToPins, - speedChangeToStickyPins, - stickyBits, targetDataCount, ) where @@ -23,7 +19,6 @@ import Clash.Explicit.Prelude hiding (PeriodToCycles) import Bittide.Arithmetic.Time (PeriodToCycles) import Clash.Class.BitPackC (BitPackC) -import Data.Maybe (fromMaybe) import Protocols.MemoryMap.TypeDescription {- | The (virtual) type of the FIFO's data counter. Setting this to @@ -69,6 +64,9 @@ data ToFincFdecState dom | Idle deriving (Generic, NFDataX) +type FINC = Bool +type FDEC = Bool + {- | Convert 'SpeedChange' to a pair of (FINC, FDEC). This is currently hardcoded to work on the Si5395 constraints: @@ -83,9 +81,9 @@ speedChangeToFincFdec :: Clock dom -> Reset dom -> Signal dom SpeedChange -> - Signal dom (Bool, Bool) + Signal dom (FINC, FDEC) speedChangeToFincFdec clk rst = - dflipflop clk . fmap conv . mealy clk rst enableGen go (Wait maxBound) + dflipflop clk . fmap speedChangeToPins . mealy clk rst enableGen go (Wait maxBound) where go :: ToFincFdecState dom -> SpeedChange -> (ToFincFdecState dom, SpeedChange) go (Wait n) _s @@ -97,74 +95,8 @@ speedChangeToFincFdec clk rst = go Idle NoChange = (Idle, NoChange) go Idle s = (Pulse maxBound s, NoChange) - -- FINC FDEC - conv NoChange = (False, False) - conv SpeedUp = (True, False) - conv SlowDown = (False, True) - -type FINC = Bool -type FDEC = Bool - speedChangeToPins :: SpeedChange -> (FINC, FDEC) speedChangeToPins = \case SpeedUp -> (True, False) SlowDown -> (False, True) NoChange -> (False, False) - -{- | Holds any @a@ which has any bits set for @stickyCycles@ clock cycles. -On receiving a new @a@ with non-zero bits, it sets the new incoming value as it output -and holds it for @stickyCycles@ clock cycles. --} -stickyBits :: - forall dom stickyCycles a. - ( KnownDomain dom - , NFDataX a - , BitPack a - , 1 <= stickyCycles - ) => - Clock dom -> - Reset dom -> - Enable dom -> - SNat stickyCycles -> - Signal dom a -> - Signal dom a -stickyBits clk rst ena SNat = mealy clk rst ena go (0, unpack 0) - where - go :: (Index stickyCycles, a) -> a -> ((Index stickyCycles, a), a) - go (count, storedBits) incomingBits = ((nextCount, nextStored), storedBits) - where - newIncoming = pack incomingBits /= 0 - predCount = satPred SatZero count - holdingBits = count /= 0 - (nextStored, nextCount) - | newIncoming = (incomingBits, maxBound) - | holdingBits = (storedBits, predCount) - | otherwise = (unpack 0, predCount) - -{- | The minimum hold time for FINC/FDEC pulses for the Si539* boards Bittide works -with is specified as 100ns. An additional 50ns is included for margin of error. --} -type Si539xHoldTime = Nanoseconds 150 - --- | The minimum update period for FINC/FDEC pulses as specified in the Si539* manual is 1μs. -type Si539xMinUpdatePeriod = Microseconds 1 - -{- | Takes the clock modification from a Callisto clock control implementation and -converts it into clock control pin signals stickied for a specified hold time. --} -speedChangeToStickyPins :: - forall dom prd. - ( KnownDomain dom - , KnownNat prd - , 1 <= PeriodToCycles dom prd - ) => - Clock dom -> - Reset dom -> - Enable dom -> - SNat prd -> - Signal dom (Maybe SpeedChange) -> - Signal dom (Bool, Bool) -speedChangeToStickyPins clk rst ena SNat msc = speedChangeToPins <$> stickySC - where - sc = fromMaybe NoChange <$> msc - stickySC = stickyBits clk rst ena (SNat @(PeriodToCycles dom prd)) sc diff --git a/bittide/src/Bittide/ClockControl/CallistoSw.hs b/bittide/src/Bittide/ClockControl/CallistoSw.hs index 53da93eeb6..22381e9d6a 100644 --- a/bittide/src/Bittide/ClockControl/CallistoSw.hs +++ b/bittide/src/Bittide/ClockControl/CallistoSw.hs @@ -9,12 +9,14 @@ module Bittide.ClockControl.CallistoSw ( import Clash.Prelude hiding (PeriodToCycles) +import Data.Maybe (fromMaybe) + import Clash.Class.BitPackC (ByteOrder) import Clash.Functor.Extra ((<<$>>)) import Protocols import VexRiscv -import Bittide.ClockControl (SpeedChange) +import Bittide.ClockControl (SpeedChange (NoChange)) import Bittide.ClockControl.Freeze (freeze) import Bittide.ClockControl.Registers (clockControlWb) import Bittide.Counter (domainDiffCountersWbC) @@ -84,7 +86,7 @@ callistoSwClockControlC :: ) ) ( Sync dom free - , CSignal dom (Maybe SpeedChange) + , CSignal dom SpeedChange , Vec otherWb (BitboneMm dom (SwcccRemBusWidth otherWb)) @@ -102,7 +104,10 @@ callistoSwClockControlC freeClk freeRst rxClocks rxResets dumpVcd peConfig = ) <- Vec.split -< allWishbone - speedChanges <- clockControlWb linkMask linksOk (unbundle diffCounters) -< clockControlBus + speedChange <- + applyC (fmap (fromMaybe NoChange)) id + <| clockControlWb linkMask linksOk (unbundle diffCounters) + -< clockControlBus clockControlBus <- arbiterMm -< [ccClockControlBusWide, muClockControlBusWide] -- We need to extend the width of both wishbone busses since we don't know which @@ -136,4 +141,4 @@ callistoSwClockControlC freeClk freeRst rxClocks rxResets dumpVcd peConfig = let diffCounters = fst <<$>> domainDiffs - idC -< (sync, speedChanges, wbRest) + idC -< (sync, speedChange, wbRest) diff --git a/firmware-binaries/Cargo.lock b/firmware-binaries/Cargo.lock index bda178e940..5104daebf9 100644 --- a/firmware-binaries/Cargo.lock +++ b/firmware-binaries/Cargo.lock @@ -324,6 +324,18 @@ version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "784a4df722dc6267a04af36895398f59d21d07dce47232adf31ec0ff2fa45e67" +[[package]] +name = "finc-fdec" +version = "0.1.0" +dependencies = [ + "bittide-build-utils", + "bittide-hal", + "bittide-macros", + "bittide-sys", + "riscv-rt 0.11.0", + "ufmt", +] + [[package]] name = "find-msvc-tools" version = "0.1.9" diff --git a/firmware-binaries/Cargo.toml b/firmware-binaries/Cargo.toml index 92613766e4..7b02be28b1 100644 --- a/firmware-binaries/Cargo.toml +++ b/firmware-binaries/Cargo.toml @@ -40,6 +40,7 @@ members = [ "hitl-tests/vexriscv-hello", "hitl-tests/clock-board", + "hitl-tests/finc-fdec", "demos/soft-ugn-demo-clock-control", "demos/soft-ugn-demo-boot", diff --git a/firmware-binaries/hitl-tests/clock-board/src/main.rs b/firmware-binaries/hitl-tests/clock-board/src/main.rs index 621ff99567..06b29bc424 100644 --- a/firmware-binaries/hitl-tests/clock-board/src/main.rs +++ b/firmware-binaries/hitl-tests/clock-board/src/main.rs @@ -145,7 +145,7 @@ fn main() -> ! { { uwriteln!(uart, "Test 2: Write clock configuration").unwrap(); if let Err(WriteError::NotConfirmed { entry, read_data }) = - si539x_spi.write_configuration(&timer, &CONFIG_200) + si539x_spi.write_configuration_with_retry(&timer, &CONFIG_200, 10) { all_passed = false; uwriteln!( diff --git a/firmware-binaries/hitl-tests/finc-fdec/Cargo.lock.license b/firmware-binaries/hitl-tests/finc-fdec/Cargo.lock.license new file mode 100644 index 0000000000..848612f0ea --- /dev/null +++ b/firmware-binaries/hitl-tests/finc-fdec/Cargo.lock.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2022 Google LLC + +SPDX-License-Identifier: CC0-1.0 diff --git a/firmware-binaries/hitl-tests/finc-fdec/Cargo.toml b/firmware-binaries/hitl-tests/finc-fdec/Cargo.toml new file mode 100644 index 0000000000..35ffa6cc61 --- /dev/null +++ b/firmware-binaries/hitl-tests/finc-fdec/Cargo.toml @@ -0,0 +1,25 @@ +# SPDX-FileCopyrightText: 2022 Google LLC +# +# SPDX-License-Identifier: CC0-1.0 + +[package] +name = "finc-fdec" +version = "0.1.0" +edition = "2021" +license = "Apache-2.0" +authors = ["Google LLC"] + +[lints] +workspace = true + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +bittide-hal = { path = "../../../firmware-support/bittide-hal" } +bittide-macros = { path = "../../../firmware-support/bittide-macros" } +bittide-sys = { path = "../../../firmware-support/bittide-sys" } +riscv-rt = "0.11.0" +ufmt = "0.2.0" + +[build-dependencies] +bittide-build-utils = { path = "../../../firmware-support/bittide-build-utils" } diff --git a/firmware-binaries/hitl-tests/finc-fdec/build.rs b/firmware-binaries/hitl-tests/finc-fdec/build.rs new file mode 100644 index 0000000000..8a47c69f8f --- /dev/null +++ b/firmware-binaries/hitl-tests/finc-fdec/build.rs @@ -0,0 +1,10 @@ +// SPDX-FileCopyrightText: 2022 Google LLC +// +// SPDX-License-Identifier: Apache-2.0 + +use bittide_build_utils::standard_memmap_build; + +fn main() { + standard_memmap_build("FincFdecTests.json", "DataMemory", "InstructionMemory"); + println!("cargo:rerun-if-changed=build.rs"); +} diff --git a/firmware-binaries/hitl-tests/finc-fdec/src/main.rs b/firmware-binaries/hitl-tests/finc-fdec/src/main.rs new file mode 100644 index 0000000000..8cf7cfef57 --- /dev/null +++ b/firmware-binaries/hitl-tests/finc-fdec/src/main.rs @@ -0,0 +1,332 @@ +#![no_std] +#![cfg_attr(not(test), no_main)] + +// SPDX-FileCopyrightText: 2025 Google LLC +// +// SPDX-License-Identifier: Apache-2.0 + +use core::panic::PanicInfo; +use ufmt::uDisplay; +use ufmt::uWrite; +use ufmt::uwrite; +use ufmt::uwriteln; + +use bittide_hal::hals::finc_fdec_tests::devices::DomainDiffCounters; +use bittide_hal::hals::finc_fdec_tests::DeviceInstances; +use bittide_hal::manual_additions::si539x_spi::{Config, WriteError}; +use bittide_hal::shared_devices::{HardwareSpeedChange, Si539xSpi, Timer, Uart}; +use bittide_hal::types::SpeedChange; + +use bittide_macros::load_clock_config_csv; + +#[cfg(not(test))] +use riscv_rt::entry; + +const INSTANCES: DeviceInstances = unsafe { DeviceInstances::new() }; +const CONFIG_125: Config<3, 590, 5> = load_clock_config_csv!( + "../../../bittide/data/clock_configs/Si5395J-125MHz-10ppb-Registers.csv" +); + +const MAX_CONFIG_RETRIES: usize = 10; + +const THRESHOLD: i32 = 20_000; + +#[derive(Copy, Clone)] +enum Direction { + Finc, + Fdec, +} + +impl uDisplay for Direction { + fn fmt(&self, f: &mut ufmt::Formatter<'_, W>) -> Result<(), W::Error> + where + W: uWrite + ?Sized, + { + match self { + Direction::Fdec => uwrite!(f, "Fdec"), + Direction::Finc => uwrite!(f, "Finc"), + } + } +} + +type TestResult = Result<(), Direction>; + +fn read_counter(dc: &DomainDiffCounters) -> i32 { + dc.counters(0).unwrap().into_inner() +} + +fn counter_test( + dc: &DomainDiffCounters, + mut apply_speed_change: SpeedChangeFn, + (mut pass_test, pass): (Pass, i32), + (mut fail_test, fail): (Fail, i32), + direction: Direction, +) -> TestResult +where + Pass: FnMut(&i32, &i32) -> bool, + Fail: FnMut(&i32, &i32) -> bool, + SpeedChangeFn: FnMut(), +{ + loop { + apply_speed_change(); + let count = read_counter(dc); + if pass_test(&count, &pass) { + return Ok(()); + } + if fail_test(&count, &fail) { + return Err(direction); + } + } +} + +fn noop() {} + +// FINC/FDEC test patterns, split into hardware and software variants. +// Hardware: set the SpeedChange register once, the hardware will continuously pulse +// the FINC/FDEC pins. +// Software: send one SPI request per loop iteration. + +fn do_hw_fdec(sc: &HardwareSpeedChange, dc: &DomainDiffCounters) -> TestResult { + sc.set_speed_change(SpeedChange::SlowDown); + let result = counter_test( + dc, + noop, + (i32::lt, -THRESHOLD), + (i32::gt, THRESHOLD), + Direction::Fdec, + ); + sc.set_speed_change(SpeedChange::NoChange); + result +} + +fn do_hw_finc(sc: &HardwareSpeedChange, dc: &DomainDiffCounters) -> TestResult { + sc.set_speed_change(SpeedChange::SpeedUp); + let result = counter_test( + dc, + noop, + (i32::gt, THRESHOLD), + (i32::lt, -THRESHOLD), + Direction::Finc, + ); + sc.set_speed_change(SpeedChange::NoChange); + result +} + +fn do_hw_fdec_inc(sc: &HardwareSpeedChange, dc: &DomainDiffCounters) -> TestResult { + sc.set_speed_change(SpeedChange::SlowDown); + counter_test( + dc, + noop, + (i32::lt, -THRESHOLD), + (i32::gt, THRESHOLD), + Direction::Fdec, + )?; + sc.set_speed_change(SpeedChange::SpeedUp); + let result = counter_test( + dc, + noop, + (i32::gt, 0), + (i32::lt, -(3 * THRESHOLD)), + Direction::Finc, + ); + sc.set_speed_change(SpeedChange::NoChange); + result +} + +fn do_hw_finc_dec(sc: &HardwareSpeedChange, dc: &DomainDiffCounters) -> TestResult { + sc.set_speed_change(SpeedChange::SpeedUp); + counter_test( + dc, + noop, + (i32::gt, THRESHOLD), + (i32::lt, -THRESHOLD), + Direction::Finc, + )?; + sc.set_speed_change(SpeedChange::SlowDown); + let result = counter_test( + dc, + noop, + (i32::lt, 0), + (i32::gt, (3 * THRESHOLD)), + Direction::Fdec, + ); + sc.set_speed_change(SpeedChange::NoChange); + result +} + +fn do_sw_fdec(si539x_spi: &Si539xSpi, timer: &Timer, dc: &DomainDiffCounters) -> TestResult { + counter_test( + dc, + || si539x_spi.fdec(timer, 1), + (i32::lt, -THRESHOLD), + (i32::gt, THRESHOLD), + Direction::Fdec, + ) +} + +fn do_sw_finc(si539x_spi: &Si539xSpi, timer: &Timer, dc: &DomainDiffCounters) -> TestResult { + counter_test( + dc, + || si539x_spi.finc(timer, 1), + (i32::gt, THRESHOLD), + (i32::lt, -THRESHOLD), + Direction::Finc, + ) +} + +fn do_sw_fdec_inc(si539x_spi: &Si539xSpi, timer: &Timer, dc: &DomainDiffCounters) -> TestResult { + counter_test( + dc, + || si539x_spi.fdec(timer, 1), + (i32::lt, -THRESHOLD), + (i32::gt, THRESHOLD), + Direction::Fdec, + )?; + + counter_test( + dc, + || si539x_spi.finc(timer, 1), + (i32::gt, 0), + (i32::lt, -(3 * THRESHOLD)), + Direction::Finc, + ) +} + +fn do_sw_finc_dec(si539x_spi: &Si539xSpi, timer: &Timer, dc: &DomainDiffCounters) -> TestResult { + counter_test( + dc, + || si539x_spi.finc(timer, 1), + (i32::gt, THRESHOLD), + (i32::lt, -THRESHOLD), + Direction::Finc, + )?; + + counter_test( + dc, + || si539x_spi.fdec(timer, 1), + (i32::lt, 0), + (i32::gt, (3 * THRESHOLD)), + Direction::Fdec, + ) +} + +/// Run a single test with the full per-test sequence: +/// +/// 1. Program clock board +/// 2. Enable domain diff counter +/// 3. Execute test body (FINC/FDEC until threshold) +/// 5. Disable domain diff counter +/// 4. Print result +/// +/// Returns whether the test passed. +fn run_test( + name: &str, + si539x_spi: &Si539xSpi, + timer: &Timer, + uart: &mut Uart, + dc: &DomainDiffCounters, + test_body: Test, +) -> bool +where + Test: FnOnce(&DomainDiffCounters) -> TestResult, +{ + if let Err(WriteError::NotConfirmed { entry, read_data }) = + si539x_spi.write_configuration_with_retry(timer, &CONFIG_125, MAX_CONFIG_RETRIES) + { + uwriteln!( + uart, + "FAIL: {} (clock board at 0x{:02X}{:02X}: wrote 0x{:02X}, read 0x{:02X})", + name, + entry.page, + entry.address, + entry.data, + read_data, + ) + .unwrap(); + return false; + } + + dc.set_enable(0, true); + loop { + if dc.counters_active(0) == Some(true) { + break; + } + } + + let test_result = test_body(dc); + dc.set_enable(0, false); + + match test_result { + Ok(()) => uwriteln!(uart, "PASS: {}", name).unwrap(), + Err(dir) => uwriteln!(uart, "FAIL: {} on part {}", name, dir).unwrap(), + } + + test_result.is_ok() +} + +macro_rules! run_tests { + ( + spi: $spi:ident, + timer: $timer:ident, + uart: $uart:ident, + dc: $dc:ident, + tests: [ + $([name: $name:literal, test: $test:expr]),+ + $(,)? + ], + ) => { + $( + run_test( + $name, + &$spi, + &$timer, + &mut $uart, + &$dc, + $test, + ) + )&&+ + }; +} + +#[cfg_attr(not(test), entry)] +fn main() -> ! { + let si539x_spi = INSTANCES.si539x_spi; + let timer = INSTANCES.timer; + let mut uart = INSTANCES.uart; + let dc = INSTANCES.domain_diff_counters; + let sc = INSTANCES.hardware_speed_change; + + let all_passed = run_tests!( + spi: si539x_spi, + timer: timer, + uart: uart, + dc: dc, + tests: [ + [name: "Hardware FDec", test: |dc| do_hw_fdec(&sc, dc)], + [name: "Hardware FInc", test: |dc| do_hw_finc(&sc, dc)], + [name: "Hardware FDecInc", test: |dc| do_hw_fdec_inc(&sc, dc)], + [name: "Hardware FIncDec", test: |dc| do_hw_finc_dec(&sc, dc)], + [name: "Software FDec", test: |dc| do_sw_fdec(&si539x_spi, &timer, dc)], + [name: "Software FInc", test: |dc| do_sw_finc(&si539x_spi, &timer, dc)], + [name: "Software FDecInc", test: |dc| do_sw_fdec_inc(&si539x_spi, &timer, dc)], + [name: "Software FIncDec", test: |dc| do_sw_finc_dec(&si539x_spi, &timer, dc)], + ], + ); + + if all_passed { + uwriteln!(uart, "All tests passed").unwrap(); + } else { + uwriteln!(uart, "Some tests failed").unwrap(); + } + + loop { + continue; + } +} + +#[panic_handler] +fn panic_handler(_info: &PanicInfo) -> ! { + loop { + continue; + } +} diff --git a/firmware-support/bittide-hal/src/manual_additions/si539x_spi.rs b/firmware-support/bittide-hal/src/manual_additions/si539x_spi.rs index 46e0404d70..0b02dee92e 100644 --- a/firmware-support/bittide-hal/src/manual_additions/si539x_spi.rs +++ b/firmware-support/bittide-hal/src/manual_additions/si539x_spi.rs @@ -84,6 +84,34 @@ impl Si539xSpi { Ok(()) } + /// Write a clock board configuration using SPI, will retry upon failure. + /// + /// Writing a configuration can sometimes fail, usually because a written register was not + /// the same when not read back. This happens roughly once in 200 runs. + pub fn write_configuration_with_retry< + const PRE_LEN: usize, + const CFG_LEN: usize, + const PST_LEN: usize, + >( + &self, + timer: &Timer, + config: &Config, + max_retries: usize, + ) -> Result<(), WriteError> { + let mut retries = 0; + loop { + match self.write_configuration(timer, config) { + Ok(()) => return Ok(()), + Err(e) => { + retries += 1; + if retries >= max_retries { + return Err(e); + } + } + } + } + } + /// Verfiy that the config part of the configuration is as expected. pub fn verify_configuration< const PRE_LEN: usize, @@ -214,4 +242,34 @@ impl Si539xSpi { self.write(write_op); } } + + /// Do a frequency increment 'n' times. + /// + /// Waits for 1 us between SPI transactions to adhere to the maximum update rate of + /// 1 us of the Si5395. + pub fn finc(&self, timer: &Timer, n: u8) { + for _ in 0..n { + self.write(ConfigEntry { + page: 0x00, + address: 0x1D, + data: 0b1 << 0, + }); + timer.wait(Duration::from_micros(1)); + } + } + + /// Do a frequency decrement 'n' times. + /// + /// Waits for 1 us between SPI transactions to adhere to the maximum update rate of + /// 1 us of the Si5395. + pub fn fdec(&self, timer: &Timer, n: u8) { + for _ in 0..n { + self.write(ConfigEntry { + page: 0x00, + address: 0x1D, + data: 0b1 << 1, + }); + timer.wait(Duration::from_micros(1)); + } + } }