Skip to content

Commit

Permalink
Simply/optimize downConverter
Browse files Browse the repository at this point in the history
  • Loading branch information
t-wallet committed Jul 29, 2024
1 parent 074b977 commit 1ae3a4f
Showing 1 changed file with 57 additions and 101 deletions.
158 changes: 57 additions & 101 deletions src/Protocols/PacketStream/Converters.hs
Original file line number Diff line number Diff line change
Expand Up @@ -128,109 +128,65 @@ upConverterC ::
Circuit (PacketStream dom 1 ()) (PacketStream dom dataWidth ())
upConverterC = forceResetSanity |> fromSignals upConverter

data DownConverterState (dataWidth :: Nat) = DownConverterState
{ _dcBuf :: Vec dataWidth (BitVector 8)
-- ^ Buffer
, _dcSize :: Index (dataWidth + 1)
-- ^ Number of valid bytes in _dcBuf
, _dcLastVec :: Bool
-- ^ True if last byte of _dcBuf was marked as last byte by incoming stream
, _dcAborted :: Bool
-- ^ If True, outgoing bytes should be marked as aborted until _dcBuf is replaced
}
deriving (Generic, NFDataX)

-- | Computes new state from incoming data
fromPacketStreamM2S ::
forall (dataWidth :: Nat).
(KnownNat dataWidth) =>
PacketStreamM2S dataWidth () ->
DownConverterState dataWidth
fromPacketStreamM2S (PacketStreamM2S vs lastIdx _ aborted) =
data DownConverterState (dataWidth :: Nat) =
DownConverterState
{ _dcBuf = vs
, _dcSize = maybe (natToNum @dataWidth) (succ . resize) lastIdx -- lastIdx points to the last valid byte, so the buffer size is one more
, _dcLastVec = isJust lastIdx
, _dcAborted = aborted
{ _dcBuf :: Vec dataWidth (BitVector 8)
-- ^ Buffer
, _dcSize :: Index (dataWidth + 1)
-- ^ Number of valid bytes in _dcBuf
}

-- | Computes output of down converter
toMaybePacketStreamM2S ::
forall (dataWidth :: Nat).
(1 <= dataWidth) =>
(KnownNat dataWidth) =>
DownConverterState dataWidth ->
Maybe (PacketStreamM2S 1 ())
toMaybePacketStreamM2S DownConverterState{..} = toMaybe (_dcSize > 0) out
where
out =
PacketStreamM2S
{ _data = leToPlusKN @1 @dataWidth head _dcBuf :> Nil
, _last = toMaybe (_dcSize == 1 && _dcLastVec) 0
, _meta = ()
, _abort = _dcAborted
}

downConverter ::
forall (dataWidth :: Nat) (dom :: Domain).
(HiddenClockResetEnable dom) =>
(1 <= dataWidth) =>
(KnownNat dataWidth) =>
-- | Input packet stream from the source and backpressure from the sink
( Signal dom (Maybe (PacketStreamM2S dataWidth ()))
, Signal dom PacketStreamS2M
) ->
-- | Output backpressure to the source
-- Output packet stream to the sink
( Signal dom PacketStreamS2M
, Signal dom (Maybe (PacketStreamM2S 1 ()))
)
downConverter = mealyB go s0
where
s0 =
DownConverterState
deriving (Generic, NFDataX)

downConverterT
:: forall (dataWidth :: Nat).
1 <= dataWidth
=> KnownNat dataWidth
=> DownConverterState dataWidth
-> (Maybe (PacketStreamM2S dataWidth ()), PacketStreamS2M)
-> (DownConverterState dataWidth, (PacketStreamS2M, Maybe (PacketStreamM2S 1 ())))
downConverterT st (Nothing, _) = (st, (PacketStreamS2M True, Nothing))
downConverterT st@DownConverterState{..} (Just inPkt, bwdIn) = (nextSt, (PacketStreamS2M outReady, Just outPkt))
where
-- If _dcSize == 0, then we have received a new transfer and should use
-- its corresponding _data. Else, we should use our stored buffer.
(nextSize, buf) = case (_dcSize == 0, _last inPkt) of
(True, Nothing) -> (natToNum @(dataWidth - 1), _data inPkt)
(True, Just i) -> (resize i, _data inPkt)
(False, _) -> (pred _dcSize, _dcBuf)

outPkt =
PacketStreamM2S
{ _data = singleton (leToPlus @1 @dataWidth head buf)
, _last = outLast
, _meta = ()
, _abort = _abort inPkt
}

(outReady, outLast)
| nextSize == 0 = (_ready bwdIn, 0 <$ _last inPkt)
| otherwise = (False, Nothing)

-- Keep the buffer in the state and rotate it once the byte is acknowledged to avoid
-- dynamic indexing.
nextSt
| _ready bwdIn = DownConverterState (rotateLeftS buf d1) nextSize
| otherwise = st

-- | Converts packet streams of arbitrary data widths to packet streams of single bytes.
-- If @_abort@ is asserted on an input transfer, it will be asserted on all
-- corresponding output transfers as well.
--
-- Has one clock cycle of latency, but optimal throughput, i.e. a packet of n bytes is
-- sent out in n clock cycles, even if `_last` is set.
downConverterC
:: forall (dataWidth :: Nat) (dom :: Domain).
HiddenClockResetEnable dom
=> 1 <= dataWidth
=> KnownNat dataWidth
=> Circuit (PacketStream dom dataWidth ()) (PacketStream dom 1 ())
downConverterC = forceResetSanity |> fromSignals (mealyB downConverterT s0)
where
s0 = DownConverterState
{ _dcBuf = errorX "downConverter: undefined initial value"
, _dcSize = 0
, _dcLastVec = False
, _dcAborted = False
}
go ::
DownConverterState dataWidth ->
(Maybe (PacketStreamM2S dataWidth ()), PacketStreamS2M) ->
(DownConverterState dataWidth, (PacketStreamS2M, Maybe (PacketStreamM2S 1 ())))
go st@(DownConverterState{..}) (fwdIn, PacketStreamS2M inReady) = (st', (bwdOut, fwdOut))
where
(_dcSize', _dcBuf') =
if _dcSize > 0 && inReady
then (_dcSize - 1, _dcBuf <<+ 0)
else (_dcSize, _dcBuf)

-- If the next buffer contains no valid bytes,
-- and the final byte was acknowledged, we can
-- acknowledge the newly received data.
-- The || is lazy, and we need this: if the output
-- of the downconverter is Nothing, we are not allowed to
-- evaluate inReady.
outReady = _dcSize == 0 || (_dcSize == 1 && inReady)
st' = case fwdIn of
Just inp | outReady -> fromPacketStreamM2S inp
_ ->
st
{ _dcBuf = _dcBuf'
, _dcSize = _dcSize'
}

bwdOut = PacketStreamS2M outReady
fwdOut = toMaybePacketStreamM2S st

{- | Converts packet streams of arbitrary data widths to packet streams of single bytes.
Has one clock cycle of latency, but optimal throughput, i.e. a packet of n bytes is
sent out in n clock cycles, even if `_last` is set.
-}
downConverterC ::
forall (dataWidth :: Nat) (dom :: Domain).
(HiddenClockResetEnable dom) =>
(1 <= dataWidth) =>
(KnownNat dataWidth) =>
Circuit (PacketStream dom dataWidth ()) (PacketStream dom 1 ())
downConverterC = forceResetSanity |> fromSignals downConverter

0 comments on commit 1ae3a4f

Please sign in to comment.