Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implemented scatterUnitWB and gatherUnitWB #31

Merged
merged 12 commits into from
Jul 11, 2022
1 change: 1 addition & 0 deletions bittide/bittide.cabal
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,7 @@ test-suite unittests
Tests.DoubleBufferedRam
Tests.ScatterGather
Tests.Switch
Tests.Shared
default-extensions:
ImplicitPrelude
build-depends:
Expand Down
205 changes: 197 additions & 8 deletions bittide/src/Bittide/ScatterGather.hs
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,27 @@
--
-- SPDX-License-Identifier: Apache-2.0

module Bittide.ScatterGather(scatterEngine, gatherEngine, scatterGatherEngine) where
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE NamedFieldPuns #-}
{-# LANGUAGE RecordWildCards #-}
module Bittide.ScatterGather
lmbollen marked this conversation as resolved.
Show resolved Hide resolved
( scatterEngine
, gatherEngine
, scatterGatherEngine
, scatterUnitWb
, gatherUnitWb) where
lmbollen marked this conversation as resolved.
Show resolved Hide resolved

import Clash.Prelude

import Contranomy.Wishbone
import Data.Proxy
import Data.Type.Equality ((:~:)(Refl))

import Bittide.Calendar
import Bittide.DoubleBufferedRam
import Bittide.SharedTypes


-- | Contains a calendar entry that can be used by a scatter or gather engine.
-- | Calendar entry that can be used by a scatter or gather engine.
type CalendarEntry memDepth = Index memDepth

-- | The Calendar for the scatter and gather engines contains entries of a single index
Expand Down Expand Up @@ -60,20 +71,31 @@ gatherEngine newMetaCycle frameIn readAddr =
writeAddr = register 0 $ satSucc SatWrap <$> writeAddr
writeFrame = combineFrameWithAddr frameIn writeAddr

combineFrameWithAddr :: Signal dom (Maybe dat) -> Signal dom addr -> Signal dom (Maybe (addr,dat))
combineFrameWithAddr ::
Signal dom (Maybe dat) ->
Signal dom addr ->
Signal dom (Maybe (addr,dat))
combineFrameWithAddr frameIn writeAddr = combine <$> frameIn <*> writeAddr
where
combine :: Maybe dat -> addr -> Maybe (addr,dat)
combine frame addr = fmap (addr,) frame

-- | scatterGatherEngine is a 4 port memory component that enables gathering and scattering for the processing element.
-- Scattering and gathering data is done using two seperate memory banks with each their own calendar,
-- Scattering and gathering data is done using two separate memory banks with each their own calendar,
-- the writeAddrSwitch dictate the read and write address on the switch side for the gather and scatter memory respectively.
-- If the read address for the scatter engine is 0, a null frame (Nothing) will be sent to the switch.
scatterGatherEngine ::
forall dom calDepthG calDepthS memDepthG memDepthS frameWidth .
(HiddenClockResetEnable dom, KnownNat calDepthG, KnownNat calDepthS, KnownNat memDepthG, KnownNat memDepthS, KnownNat frameWidth,
1 <= calDepthG , 1 <= calDepthS, 1 <= memDepthG, 1 <= memDepthS) =>
( HiddenClockResetEnable dom
, KnownNat calDepthG
, KnownNat calDepthS
, KnownNat memDepthG
, KnownNat memDepthS
, KnownNat frameWidth
, 1 <= calDepthG
, 1 <= calDepthS
, 1 <= memDepthG
, 1 <= memDepthS) =>
-- | Bootstrap calendar gather memory.
Calendar calDepthS memDepthS ->
-- | Bootstrap calendar scatter memory.
Expand Down Expand Up @@ -107,4 +129,171 @@ scatterGatherEngine bootCalS bootCalG scatterConfig gatherConfig
(readAddrSwitch, newMetaCycleG) = calendar bootCalG (pure False) gatherConfig
writeFramePE = (\f a -> fmap (a,) f) <$> frameInPE' <*> writeAddrPE
gatherOut = doubleBufferedRamU newMetaCycleG readAddrSwitch writeFramePE
toSwitch = mux (register True $ (==0) <$> readAddrSwitch) (pure Nothing) $ Just <$> gatherOut
toSwitch = mux (register True $ (==0) <$> readAddrSwitch) (pure Nothing)
lmbollen marked this conversation as resolved.
Show resolved Hide resolved
$ Just <$> gatherOut


-- | Double buffered memory component that can be written to by a Bittide link. The write
-- address of the incoming frame is determined by the scatterUnit's calendar. The buffers
-- are swapped at the beginning of each metacycle. Reading the buffer is done by supplying
-- a read address. Furthermore this component offers ports to control the incorporated calendar.
scatterUnit ::
( HiddenClockResetEnable dom
, KnownNat memDepth, 1 <= memDepth
, KnownNat frameWidth) =>
-- | Configuration for the calendar.
CalendarConfig bytes addressWidth (CalendarEntry memDepth) ->
-- | Wishbone master-slave port for the calendar.
lmbollen marked this conversation as resolved.
Show resolved Hide resolved
Signal dom (WishboneM2S bytes addressWidth) ->
-- | Swap active calendar and shadow calendar.
Signal dom Bool ->
-- | Incoming frame from Bittide link.
Signal dom (DataLink frameWidth) ->
-- | Read address.
Signal dom (Index memDepth) ->
-- | (Data at read address delayed 1 cycle, Wishbone slave-master from calendar)
(Signal dom (BitVector frameWidth), Signal dom (WishboneS2M bytes))
scatterUnit calConfig wbIn calSwitch linkIn readAddr = (readOut, wbOut)
where
(writeAddr, metaCycle, wbOut) = mkCalendar calConfig calSwitch wbIn
writeOp = (\a b -> (a,) <$> b) <$> writeAddr <*> linkIn
readOut = doubleBufferedRamU metaCycle readAddr writeOp

-- | Double buffered memory component that can be written to by a generic write operation. The
-- write address of the incoming frame is determined by the scatterUnit's calendar. The
-- buffers are swapped at the beginning of each metacycle. Reading the buffer is done by
-- supplying a read address. Furthermore this component offers ports to control the
-- incorporated calendar.
gatherUnit ::
( HiddenClockResetEnable dom
, KnownNat memDepth, 1 <= memDepth
, KnownNat frameWidth, 1 <= frameWidth
, KnownNat (DivRU frameWidth 8), 1 <= (DivRU frameWidth 8)) =>
-- | Configuration for the calendar.
CalendarConfig bytes addressWidth (CalendarEntry memDepth) ->
-- | Wishbone master-slave port for the calendar.
lmbollen marked this conversation as resolved.
Show resolved Hide resolved
Signal dom (WishboneM2S bytes addressWidth) ->
-- | Swap active calendar and shadow calendar.
Signal dom Bool ->
-- | Generic write operation writing a frame.
lmbollen marked this conversation as resolved.
Show resolved Hide resolved
Signal dom (Maybe (LocatedBits memDepth frameWidth)) ->
-- | Byte enable for write operation.
Signal dom (ByteEnable (DivRU frameWidth 8)) ->
-- | (Transmitted frame to Bittide Link, Wishbone slave-master from calendar)
(Signal dom (DataLink frameWidth), Signal dom (WishboneS2M bytes))
gatherUnit calConfig wbIn calSwitch writeOp byteEnables= (linkOut, wbOut)
where
(readAddr, metaCycle, wbOut) = mkCalendar calConfig calSwitch wbIn
linkOut = mux (register True $ (==0) <$> readAddr) (pure Nothing) $ Just <$> bramOut
lmbollen marked this conversation as resolved.
Show resolved Hide resolved
bramOut = doubleBufferedRamByteAddressableU metaCycle readAddr writeOp byteEnables

-- | Wishbone interface for the scatterUnit and gatherUnit. It makes the scatter and gather
lmbollen marked this conversation as resolved.
Show resolved Hide resolved
-- unit, which operate on 64 bit frames, addressable via a 32 bit wishbone bus.
wbInterface ::
forall bytes addressWidth addresses .
( KnownNat bytes
, KnownNat addresses, 1 <= addresses
, KnownNat addressWidth, 2 <= addressWidth) =>
-- | Maximum address of the respective memory element as seen from the wishbone side.
Index addresses ->
-- | Wishbone master - slave data.
WishboneM2S bytes addressWidth ->
-- | Read data to be send to over the slave-master port.
Bytes bytes ->
-- | (slave - master data, read address memory element, write data memory element)
(WishboneS2M bytes, Index addresses, Maybe (Bytes bytes))
lmbollen marked this conversation as resolved.
Show resolved Hide resolved
wbInterface addressRange WishboneM2S{..} readData =
(WishboneS2M{readData, acknowledge, err}, memAddr, writeOp)
where
masterActive = strobe && busCycle
(alignedAddress, alignment) = split @_ @(addressWidth - 2) @2 addr
wordAligned = alignment == (0 :: BitVector 2)
lmbollen marked this conversation as resolved.
Show resolved Hide resolved
err = masterActive && ((alignedAddress > resize (pack addressRange)) || not wordAligned)
acknowledge = masterActive && not err
wbAddr = unpack . resize $ pack alignedAddress
memAddr = wbAddr
writeOp | strobe && writeEnable && not err = Just writeData
| otherwise = Nothing
lmbollen marked this conversation as resolved.
Show resolved Hide resolved

-- | Wishbone addressable scatterUnit, the wishbone port can read the data from this
lmbollen marked this conversation as resolved.
Show resolved Hide resolved
-- memory element as if it has a 32 bit port by selecting the upper 32 or lower 32 bits
-- of the read data.
scatterUnitWb ::
forall dom memDepth awSU bsCal awCal .
( HiddenClockResetEnable dom
, KnownNat memDepth, 1 <= memDepth
, KnownNat awSU, 2 <= awSU) =>
-- | Configuration for the calendar.
CalendarConfig bsCal awCal (CalendarEntry memDepth) ->
-- | Wishbone master - slave data calendar.
Signal dom (WishboneM2S bsCal awCal) ->
-- | Swap active calendar and shadow calendar.
Signal dom Bool ->
-- | Incoming frame from Bittide link.
Signal dom (DataLink 64) ->
-- | Wishbone master-slave port scatterUnit.
Signal dom (WishboneM2S 4 awSU) ->
-- | (slave - master data scatterUnit , slave - master data calendar)
(Signal dom (WishboneS2M 4), Signal dom (WishboneS2M bsCal))
Copy link
Contributor

Choose a reason for hiding this comment

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

I don't understand why the scatter memory has a 32bit WB bus, but the calendar has a configurable-width WB bus. Why not?:

  • Both configurable, or
  • Both 32 bit

Copy link
Contributor Author

Choose a reason for hiding this comment

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

We know that:

  • The framewidth for now will be 64 bits since we have to send over the timing oracles(f.k.a. UGNs).
  • The wishbone bus interface that accesses the s/g units will most likely be 32 bits since they're hooked up to rv32imc.

By enforcing the s/g unit bus to be 32 bits we can have a nice way to interface with the 64 bit s/g units (either using the upper or lower 32 bits).
However, the calendar wishbone bus has no requirements to constrain the amount of bytes on the bus and may not be hooked up to the same processor that the s/g bus is hooked up to.

To summarize:

  • The s/g bus cannot be easily made configurable due to the 64 bit framewidth constraint
  • There is no reason other than readability(?) to constrain the calendar bus to 32 bits.

scatterUnitWb calConfig wbInCal calSwitch linkIn wbInSU =
(delayControls wbOutSU, wbOutCal)
where
(wbOutSU, memAddr, _) = unbundle $ wbInterface maxBound <$> wbInSU <*> scatteredData
(readAddr, upperSelected) = unbundle $ coerceIndexes <$> memAddr
(scatterUnitRead, wbOutCal) = scatterUnit calConfig wbInCal calSwitch linkIn readAddr
(upper, lower) = unbundle $ split <$> scatterUnitRead
selected = register (errorX "scatterUnitWb: Initial selection undefined") upperSelected
scatteredData = mux selected upper lower

-- | Wishbone addressable gatherUnit, the wishbone port can write data to this
-- memory element as if it has a 32 bit port by controlling the byte enables of the
-- gatherUnit based on the third bit.
lmbollen marked this conversation as resolved.
Show resolved Hide resolved
gatherUnitWb ::
forall dom memDepth awSU bsCal awCal .
( HiddenClockResetEnable dom
, KnownNat memDepth, 1 <= memDepth
, KnownNat awSU, 2 <= awSU) =>
-- | Configuration for the calendar.
CalendarConfig bsCal awCal (CalendarEntry memDepth) ->
-- | Wishbone master - slave data calendar.
Signal dom (WishboneM2S bsCal awCal) ->
-- | Swap active calendar and shadow calendar.
Signal dom Bool ->
-- | Wishbone master-slave port gatherUnit.
Signal dom (WishboneM2S 4 awSU) ->
-- | (slave - master data gatherUnit , slave - master data calendar)
(Signal dom (DataLink 64), Signal dom (WishboneS2M 4), Signal dom (WishboneS2M bsCal))
gatherUnitWb calConfig wbInCal calSwitch wbInSU =
(linkOut, delayControls wbOutSU, wbOutCal)
where
(wbOutSU, memAddr, writeOp) = unbundle $ wbInterface maxBound <$> wbInSU <*> pure 0b0
(writeAddr, upperSelected) = unbundle $ coerceIndexes <$> memAddr
(linkOut, wbOutCal) =
gatherUnit calConfig wbInCal calSwitch gatherWrite gatherByteEnables
gatherWrite = mkWrite <$> writeAddr <*> writeOp
gatherByteEnables = mkEnables <$> upperSelected <*> (busSelect <$> wbInSU)

mkWrite address (Just write) = Just (address, write ++# write)
lmbollen marked this conversation as resolved.
Show resolved Hide resolved
mkWrite _ _ = Nothing
mkEnables selected byteEnables
| selected = byteEnables ++# 0b0
| otherwise = 0b0 ++# byteEnables

-- | Coerces an index of size (n*2) to index n with the lower bit as separate boolean.
lmbollen marked this conversation as resolved.
Show resolved Hide resolved
coerceIndexes :: forall n . (KnownNat n, 1 <= n) => (Index (n*2) -> (Index n, Bool))
lmbollen marked this conversation as resolved.
Show resolved Hide resolved
coerceIndexes = case sameNat natA natB of
Just Refl -> bitCoerce
_ -> error "gatherUnitWb: Index coercion failed."
lmbollen marked this conversation as resolved.
Show resolved Hide resolved
where
natA = Proxy @(CLog 2 (n*2))
natB = Proxy @(1 + (CLog 2 n))

-- | Delays the output controls to align them with the actual read / write timing.
delayControls :: HiddenClockResetEnable dom =>
Signal dom (WishboneS2M bytes) -> Signal dom (WishboneS2M bytes)
delayControls wbIn = wbOut
where
delayedAck = register False (acknowledge <$> wbIn)
delayedErr = register False (err <$> wbIn)
wbOut = (\wb newAck newErr-> wb{acknowledge = newAck, err = newErr})
<$> wbIn <*> delayedAck <*> delayedErr
14 changes: 2 additions & 12 deletions bittide/tests/Tests/Calendar.hs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ import Clash.Hedgehog.Sized.Vector

import Bittide.Calendar
import Bittide.SharedTypes
import Tests.Shared

import Clash.Sized.Vector (unsafeFromList)
import Contranomy.Wishbone
import Data.Constraint
Expand Down Expand Up @@ -71,18 +73,6 @@ instance Show (BVCalendar addressWidth) where
-- TODO: Remove this show instance after issue (https://github.com/clash-lang/clash-compiler/issues/2190) has been fixed.
deriving instance Show (SNatLE a b)

data IsInBounds a b c where
InBounds :: (a <= b, b <= c) => IsInBounds a b c
NotInBounds :: IsInBounds a b c

deriving instance Show (IsInBounds a b c)

-- | Returns 'InBounds' if a <= b <= c, otherwise returns 'NotInBounds'.
isInBounds :: SNat a -> SNat b -> SNat c -> IsInBounds a b c
isInBounds a b c = case (compareSNat a b, compareSNat b c) of
(SNatLE, SNatLE) -> InBounds
_ -> NotInBounds

-- | Generates a configuration for 'Bittide.Calendar.calendarWb', with as first argument
-- the maximum depth of the stored calendar and as second argument a generator for the
-- calendar entries.
Expand Down
Loading