Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -96,3 +96,4 @@ docs/mdbook-drawio

# Scala
.metals
.scala-build
3 changes: 3 additions & 0 deletions bittide-instances/bittide-instances.cabal
Original file line number Diff line number Diff line change
Expand Up @@ -203,6 +203,7 @@ library
Bittide.Instances.Hitl.Utils.Program
Bittide.Instances.Hitl.Utils.Serial
Bittide.Instances.Hitl.Utils.Ugn
Bittide.Instances.Hitl.Utils.UgnGrooming
Bittide.Instances.Hitl.Utils.Usb
Bittide.Instances.Hitl.Utils.Utils
Bittide.Instances.Hitl.Utils.Vivado
Expand Down Expand Up @@ -325,6 +326,7 @@ test-suite unittests
Tests.Bittide.Instances.Hitl.Utils.MemoryMap
Tests.Bittide.Instances.Hitl.Utils.OpenOcd
Tests.Bittide.Instances.Hitl.Utils.Serial
Tests.Bittide.Instances.Hitl.Utils.UgnGrooming
Tests.ClockControlWb
Wishbone.AddressableBytesWb
Wishbone.Axi
Expand All @@ -349,6 +351,7 @@ test-suite unittests
clash-prelude-hedgehog,
clash-protocols-memmap,
clash-vexriscv,
containers,
directory,
extra,
filepath,
Expand Down
29 changes: 23 additions & 6 deletions bittide-instances/src/Bittide/Instances/Hitl/GenericDemo/Core.hs
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ import Protocols
import Bittide.CaptureUgn (captureUgns, sendUgn)
import Bittide.ClockControl (SpeedChange)
import Bittide.ClockControl.CallistoSw (SwcccInternalBusses, callistoSwClockControlC)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you have a look at the commit message? The general take is that it doesn't really convey information that's important.

import Bittide.ClockControl.Ugn.Corrections (correctionsWb)
import Bittide.DoubleBufferedRam (wbStorage)
import Bittide.ElasticBuffer (fromData, xilinxElasticBufferWb)
import Bittide.Extra.Maybe (toMaybe)
Expand All @@ -76,6 +77,7 @@ import Bittide.ProcessingElement (
import Bittide.RingBuffer (receiveRingBuffer, transmitRingBuffer)
import Bittide.SharedTypes (Bitbone, BitboneMm)
import Bittide.Sync (Sync)
import Bittide.TimedReset (timedResetWb)
import Bittide.Wishbone (readDnaPortE2WbWorker, timeWb, uartBytes, uartInterfaceWb)
import Clash.Class.BitPackC (ByteOrder)
import Clash.Cores.Xilinx (withXilinx)
Expand All @@ -97,16 +99,18 @@ import qualified Protocols.Vec as Vec
-- | The number of CPUs in 'core'
type InternalCpuCount = 2

type FifoSize = 5 -- = 2^5 = 32
type FifoSize = 6 -- = 2^6 = 64

{- Internal busses:
- Instruction memory
- Data memory
- `timeWb`
- DNA
- UART
- UGN grooming corrections
- application reset (TimedReset)
-}
type NmuInternalBusses = 3 + PeInternalBusses
type NmuInternalBusses = 5 + PeInternalBusses

{- Busses per link:
- Elastic buffer
Expand Down Expand Up @@ -144,6 +148,10 @@ type UserCoreCircuit userCoreBusses muRemBusWidth =
Enable Bittide ->
Signal Bittide (Unsigned 64) ->
Signal Bittide (Maybe (BitVector 96)) ->
-- Application reset, released by the management unit's 'timedResetWb' at a

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Update documentation after switching to programmable mux.

-- chosen local-counter cycle (the UGN-grooming relabel). Demos that have an
-- application counter gate it on this reset; others may ignore it.
Reset Bittide ->
Circuit
( Vec userCoreBusses (ToConstBwd Mm, Bitbone Bittide muRemBusWidth)
, "RXS2_RAW" ::: CSignal Bittide (Vec LinkCount (BitVector 64))
Expand Down Expand Up @@ -201,6 +209,7 @@ managementUnit ::
Circuit
(ToConstBwd Mm.Mm, Jtag dom)
( Df dom (BitVector 8)
, "APP_RESET" ::: Reset dom
, Vec
(NmuExternalBusses userCoreBusses)
( ToConstBwd Mm.Mm
Expand All @@ -211,16 +220,24 @@ managementUnit externalCounter maybeDna =
circuit $ \(mm, jtag) -> do
-- Core and interconnect
allBusses <- processingElement NoDumpVcd muConfig -< (mm, jtag)
([timeBus, uartBus, dnaBus], restBusses) <- Vec.split -< allBusses
([timeBus, uartBus, dnaBus, correctionsBus, timedResetBus], restBusses) <-
Vec.split -< allBusses

-- Peripherals
_localCounter <- timeWb (Just externalCounter) -< timeBus
(uartOut, _uartStatus) <-
uartInterfaceWb d16 d16 uartBytes -< (uartBus, Fwd (pure Nothing))
readDnaPortE2WbWorker maybeDna -< dnaBus
-- Host-written UGN grooming corrections, polled and applied by the MU CPU.
correctionsWb (SNat @LinkCount) -< correctionsBus
-- Application reset for the demo's user core: the management unit chooses the
-- local-counter cycle at which the application leaves reset (the UGN-grooming
-- relabel). The reset lives here, in the layer around the user core; the
-- application counter it gates lives in the user core itself.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the UGN-grooming relabel

The what now?

appReset <- timedResetWb externalCounter -< timedResetBus

-- Output
idC -< (uartOut, restBusses)
idC -< (uartOut, appReset, restBusses)

core ::
forall userCoreBusses ringBufferDepth.
Expand Down Expand Up @@ -262,7 +279,7 @@ core bufferDepth mkUserCore (refClk, refRst) (bitClk, bitRst, bitEna) rxClocks r
localCounter = register bitClk bitRst bitEna 0 (localCounter + 1)

-- Start management unit
(muUartBytesBittide, muWbAll) <-
(muUartBytesBittide, Fwd appReset, muWbAll) <-
withBittideClockResetEnable managementUnit localCounter maybeDna -< (muMm, muJtag)
(ebWbs, muWbs1) <- Vec.split -< muWbAll
(rxBufferBusses, muWbs2) <- Vec.split -< muWbs1
Expand Down Expand Up @@ -329,7 +346,7 @@ core bufferDepth mkUserCore (refClk, refRst) (bitClk, bitRst, bitEna) rxClocks r

-- Start user core: post-handshake stage drives the GTH-TX wire.
Fwd txsOut <-
mkUserCore bitClk bitRst bitEna localCounter maybeDna
mkUserCore bitClk bitRst bitEna localCounter maybeDna appReset
-< ( extraMuBusses
, Fwd (bundle rxs2Raw)
, Fwd (bundle handshakesOut.toNeighbors)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ ringBufferDepth :: SNat RingBufferDepth
ringBufferDepth = SNat

mkUserCore :: UserCoreCircuit UserCoreBusses (NmuRemBusWidth UserCoreBusses)
mkUserCore _bitClk _bitRst _bitEna _localCounter _maybeDna =
mkUserCore _bitClk _bitRst _bitEna _localCounter _maybeDna _appReset =
circuit $ \(emptyUserCoreBusses, _rxs2Raw, handshakeOut) -> do
[] <- idC -< emptyUserCoreBusses
idC -< handshakeOut
81 changes: 81 additions & 0 deletions bittide-instances/src/Bittide/Instances/Hitl/Utils/Gdb.hs
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,20 @@ module Bittide.Instances.Hitl.Utils.Gdb where

import Prelude

import Clash.Prelude (Signed, Unsigned, Vec, checkedFromIntegral, toList)

import Bittide.Hitl (DeviceInfo (..))
import Bittide.Instances.Hitl.Setup (LinkCount)
Comment on lines +9 to +12

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See other comment in this module: this module shouldn't contain device-specific functions.

import Bittide.Instances.Hitl.Utils.Driver (getTargetIndex)
import Bittide.Instances.Hitl.Utils.MemoryMap (getPathAddress)
import Bittide.Instances.Hitl.Utils.OpenOcd (TapInfo (..))
import Bittide.Wishbone (TimeCmd (Capture))
import Control.Monad (forM_)
import GHC.Stack (HasCallStack)
import Gdb (Gdb)
import Project.FilePath
import Project.Handle (expectRight)
import Protocols.MemoryMap (MemoryMap)
import System.FilePath ((</>))
import Vivado.Tcl (HwTarget)

Expand All @@ -26,3 +35,75 @@ initGdb hitlDir binName gdb tapInfo (hwT, _d) = do
Gdb.setTimeout gdb Nothing
Gdb.runCommand gdb "echo connected to target device"
pure ()

-- * GDB-based component HAL accessors

--
-- Read/write a component's registers over GDB, addressed through a 'MemoryMap'. Generic
-- across demos (the memory map is a parameter). All require the CPU to be halted.

-- | Read the current local-counter value from a device's @Timer@ peripheral.
readCurrentTime :: (HasCallStack) => MemoryMap -> (HwTarget, DeviceInfo) -> Gdb -> IO (Unsigned 64)
readCurrentTime mm (_, d) gdb = do
putStrLn $ "Getting current time from device " <> d.deviceId
commandAddress <- expectRight $ getPathAddress mm ["0", "Timer", "command"]
scratchAddress <- expectRight $ getPathAddress mm ["0", "Timer", "scratchpad"]
Gdb.writeLe gdb commandAddress Capture
Gdb.readLe gdb scratchAddress
Comment on lines +45 to +52

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This function already exists in WireDemo. Could you move them to their own module per memory mapped device? Also see issue #1336.


{- | Write a node's reset-release cycle to its @TimedReset@ peripheral. Once the local
counter exceeds this value the node's application (and its application counter) leave
reset.
-}
writeReleaseCycle :: (HasCallStack) => MemoryMap -> Gdb -> Unsigned 64 -> IO ()
writeReleaseCycle mm gdb releaseCycle = do
addr <- expectRight $ getPathAddress mm ["0", "TimedReset", "release_cycle"]
Gdb.writeLe gdb addr releaseCycle

{- | Write the per-link frame corrections and set the @valid@ flag on the
@UgnCorrections@ peripheral. Resuming the CPU makes the management unit apply the
corrections to its elastic buffers.
-}
writeCorrections :: (HasCallStack) => MemoryMap -> Gdb -> Vec LinkCount (Signed 64) -> IO ()
writeCorrections mm gdb corrections = do
correctionsAddr <- expectRight $ getPathAddress mm ["0", "UgnCorrections", "corrections"]
validAddr <- expectRight $ getPathAddress mm ["0", "UgnCorrections", "valid"]
Gdb.writeLe gdb correctionsAddr corrections
Gdb.writeLe gdb validAddr True

{- | Read the per-link hardware UGN counter captures from the @CaptureUgns@ peripheral:
the @(localCounter, remoteCounter)@ pair per link, with @localCounter@ already adjusted
for the elastic-buffer frames added/removed since capture (@elastic_buffer_delta@).
-}
readHardwareUgns ::
(HasCallStack) => MemoryMap -> (HwTarget, DeviceInfo) -> Gdb -> IO [(Unsigned 64, Unsigned 64)]
readHardwareUgns mm (_, d) gdb = do
let
getUgnRegister reg = expectRight $ getPathAddress mm ["0", "CaptureUgns", reg]

-- Adjust the local counter for the frames added/removed from the elastic
-- buffer after capturing the UGN. Leaves the remote counter untouched.
adjustLocalCounter :: (Unsigned 64, Unsigned 64, Signed 32) -> (Unsigned 64, Unsigned 64)
adjustLocalCounter (localCounter, remoteCounter, delta) =
(addSigned localCounter delta, remoteCounter)
where
addSigned :: Unsigned 64 -> Signed 32 -> Unsigned 64
addSigned u s = checkedFromIntegral (toInteger u + toInteger s)

putStrLn $ "Getting UGNs for device " <> d.deviceId
localCounters <- Gdb.readLe @(Vec LinkCount (Unsigned 64)) gdb =<< getUgnRegister "local_counter"
remoteCounters <- Gdb.readLe @(Vec LinkCount (Unsigned 64)) gdb =<< getUgnRegister "remote_counter"
deltas <- Gdb.readLe @(Vec LinkCount (Signed 32)) gdb =<< getUgnRegister "elastic_buffer_delta"
let ugnTriples = zip3 (toList localCounters) (toList remoteCounters) (toList deltas)
forM_ ugnTriples $ \triple -> putStrLn $ "Raw UGN triple: " <> show triple
pure $ adjustLocalCounter <$> ugnTriples

{- | Read the relabeled application counter exposed by the user core's
@AppCounterReadback@ peripheral. With the application running, @localCounter -
app_counter@ equals the node's reset-release cycle, letting the host verify the relabel
landed on hardware.
-}
readAppCounter :: (HasCallStack) => MemoryMap -> (HwTarget, DeviceInfo) -> Gdb -> IO (Unsigned 64)
readAppCounter mm (_, _) gdb = do
addr <- expectRight $ getPathAddress mm ["0", "AppCounterReadback", "app_counter"]
Gdb.readLe gdb addr
25 changes: 25 additions & 0 deletions bittide-instances/src/Bittide/Instances/Hitl/Utils/Ugn.hs
Original file line number Diff line number Diff line change
Expand Up @@ -211,6 +211,31 @@ timingOracleToUgnEdge oracle =
, ugn = bitCoerce oracle.dstCounter - bitCoerce oracle.srcCounter
}

{- | Turn a per-node, per-link table of UGN values (@λ@) into 'UgnEdge's keyed by DNA
node id. The outer vector is FPGA-indexed (the capturing / destination node); each
inner vector is link-indexed (matching 'knownLinkConfigs').
-}
ugnEdges :: CP.Vec FpgaCount (CP.Vec LinkCount (Signed 64)) -> [UgnEdge]
ugnEdges lams =
Comment on lines +214 to +219

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should probably take the map as an argument and move to bittide.

L.concat (CP.toList (CP.imap nodeEdges lams))
where
nodeEdges dstIndex links = CP.toList (CP.imap (edge dstIndex) links)
edge dstIndex port lam =
UgnEdge
{ srcNode = indexToNodeId ((knownLinkConfigs CP.!! dstIndex) CP.!! port)
, srcPort = port
, dstNode = indexToNodeId dstIndex
, dstPort = port
, ugn = lam
}

{- | As 'ugnEdges', but from raw @(localCounter, remoteCounter)@ captures: the UGN is
their signed difference @local - remote@.
-}
hardwareUgnEdges ::
CP.Vec FpgaCount (CP.Vec LinkCount (Unsigned 64, Unsigned 64)) -> [UgnEdge]
hardwareUgnEdges = ugnEdges . CP.map (CP.map (\(l, r) -> bitCoerce l - bitCoerce r))

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you use numConvert?


{- | Compare hardware and software UGN edges with detailed difference reporting.

Performs edge-by-edge comparison and reports:
Expand Down
Loading
Loading