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

Add PacketStream protocol. #85

Open
wants to merge 58 commits into
base: main
Choose a base branch
from
Open

Add PacketStream protocol. #85

wants to merge 58 commits into from

Conversation

rowanG077
Copy link
Member

@rowanG077 rowanG077 commented Jul 3, 2024

Todos:

  • Test haddock and make nicer
  • Copy over tests
  • Make top-level PacketStream module that exports all sub-modules
  • Top-level module should hide inner modules
  • Add forceResetSanity to async fifo
  • Remove code duplication in the tests

@rowanG077 rowanG077 force-pushed the packetstream branch 3 times, most recently from 0669c0c to ee9e485 Compare July 3, 2024 14:35
@bgamari
Copy link
Collaborator

bgamari commented Aug 2, 2024

The motivation here is a bit unclear to me. The documentation says

Provides the PacketStream protocol, a simple streaming protocol for transferring packets of data between components

However the "data" in question appears to be only bytes. Presumably there is a specific use-case in mind here but it is not clear to me what it is; network packets, perhaps?

It seems to me like ideally protocols in clash-protocols should be polymorphic in the type of the data that they transfer. Afterall, I wouldn't expect packets of bytes to be terribly prevalent in the "interior" of most designs. Unless you are building actual networking hardware, I would imagine you would typically want to parse the packet into a more structured form somewhere near the "edge" of the design.

@t-wallet
Copy link
Collaborator

t-wallet commented Aug 2, 2024

The motivation here is a bit unclear to me. The documentation says

Provides the PacketStream protocol, a simple streaming protocol for transferring packets of data between components

However the "data" in question appears to be only bytes. Presumably there is a specific use-case in mind here but it is not clear to me what it is; network packets, perhaps?

It seems to me like ideally protocols in clash-protocols should be polymorphic in the type of the data that they transfer. Afterall, I wouldn't expect packets of bytes to be terribly prevalent in the "interior" of most designs. Unless you are building actual networking hardware, I would imagine you would typically want to parse the packet into a more structured form somewhere near the "edge" of the design.

This protocol was indeed specifically created for a hardware-based MAC/IP/UDP stack (which will soon be coming to clash-cores). However, it is just a simpler version of AXI Stream with stricter metadata invariants. You can use it for any other purpose that needs high-speed data streaming, like video streaming. This PR also provides several circuits which are able to read these unstructured bytes into arbitrary types.

@rowanG077
Copy link
Member Author

rowanG077 commented Aug 2, 2024

@bgamari It was discussed with @lmbollen whether to make the payload polymorphic. In theory there isn't anything that currently really looks at the payload so it would not be super hard to do add a type variable for the payload.

But in my experience working with packetized streams there usually is not a sane way to have an interior type. You are almost certainly only dealing with fragments, these will generally not have a clear structure at the scope of a single cycle. The structure comes from the fragments together.

Do you have some concrete idea how you see a packet stream packet with a non-byte oriented payload? I'm not against the idea but just don't see the advantage.

@martijnbastiaan
Copy link
Member

But in my experience working with packetized streams there usually is not a sane way to have an interior type.

This is my experience too. In particular, any protocol that has byte enables (including the interface of block RAMs) are a pain to deal with if they're not having bits/bytes/bitvector as an interior type.

Still, ADTs are the killer feature of Clash IMO, so perhaps we should also offer API compatible versions without byte enables.

@t-wallet t-wallet force-pushed the packetstream branch 2 times, most recently from 4d8758d to a38eadc Compare August 30, 2024 16:21
@t-wallet t-wallet force-pushed the packetstream branch 2 times, most recently from 97b5f56 to a90007a Compare October 18, 2024 14:11
* Support zero-byte transfers
* Add utility that merges trailing zero-byte transfers
* Make PacketStream test framework more flexible
Now allows users to:
- Set the distribution of abort generation themselves;
- Choose whether to generate zero-byte packets;
- Choose whether to generate trailing zero-byte transfers.
* Converters: remove expensive operations
`mod` and `*` are removed.
Reverted back to the old version where all M2S outputs are registered, but now also handles arbitrary metadata and output data widths bigger than 1 that divide the input data width. Zero-byte transfers are always preserved. This new version reduces the LUT resource usage by half compared to the old registered version, because of a mux with 1 selector bit (optimal) being inferred by synthesizers for the next state computation, instead of a mux with 2 selector bits.
Now correctly drops packets that are too big to fit in the content FIFO in 'Backpressure' mode.
Forcing bytes in PacketStream _data to be 0x00 turned out to be too restrictive. Allowing them to be undefined makes a lot of components more efficient: depacketizers, packetizers, dropTail, and upConverter. It also strengthens the test framework as we can now force un-enabled bytes to throw an error when evaluated.
Adds a generic component which enforces packets to be a certain length,
which is extracted from the metadata. Packets that are too small are
aborted. This is useful for higher-level network protocols such as IP
and UDP.
Copy link
Member

@lmbollen lmbollen left a comment

Choose a reason for hiding this comment

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

lgtm modulo some minor remarks.

Comment on lines +148 to +160
-- Bitmask used for data equality. If the index of a data byte is larger
-- than or equal to the size of `_data`, it is a null byte and must be
-- disregarded in the equality check.
mask = case _last t1 of
Nothing -> repeat False
Just size -> imap (\i _ -> resize i >= size) (_data t1)

dataEq = case compareSNat (SNat @dataWidth) d0 of
SNatLE -> True
SNatGT ->
leToPlus @1 @dataWidth
$ fold (&&)
$ zipWith3 (\b1 b2 isNull -> isNull || b1 == b2) (_data t1) (_data t2) mask
Copy link
Member

Choose a reason for hiding this comment

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

Why is the mask inverted?

Comment on lines +472 to +474
__NB__: assumes that packets are longer than @ceil(n / dataWidth)@ transfers.
If this is not the case, the behaviour of this component is undefined.
-}
Copy link
Member

Choose a reason for hiding this comment

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

Behavior when this assumption is false should not affect other packets and at the least abort the packet.

clash-protocols/src/Data/Constraint/Nat/Extra.hs Outdated Show resolved Hide resolved
clash-protocols/src/Protocols/PacketStream/Base.hs Outdated Show resolved Hide resolved
clash-protocols/src/Protocols/PacketStream/Base.hs Outdated Show resolved Hide resolved
clash-protocols/src/Protocols/PacketStream/Converters.hs Outdated Show resolved Hide resolved
forall (dwIn :: Nat) (dwOut :: Nat) (meta :: Type) (dom :: Domain) (n :: Nat).
(HiddenClockResetEnable dom) =>
(1 <= dwIn) =>
(1 <= dwOut) =>
Copy link
Member

Choose a reason for hiding this comment

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

Replace with dwIn * n

clash-protocols/src/Protocols/PacketStream/Delay.hs Outdated Show resolved Hide resolved
Comment on lines +26 to +51
{- | Test the depacketizer with varying datawidth and number of bytes in the header,
with metaIn = () and toMetaOut = const.
-}
depacketizerPropertyGenerator ::
forall
(dataWidth :: Nat)
(headerBytes :: Nat).
(1 <= dataWidth) =>
(1 <= headerBytes) =>
SNat dataWidth ->
SNat headerBytes ->
Property
depacketizerPropertyGenerator SNat SNat =
idWithModelSingleDomain
@System
defExpectOptions{eoSampleMax = 1000, eoStopAfterEmpty = 1000}
(genPackets 1 4 (genValidPacket defPacketOptions (pure ()) (Range.linear 0 30)))
(exposeClockResetEnable (depacketizerModel const))
(exposeClockResetEnable ckt)
where
ckt ::
(HiddenClockResetEnable System) =>
Circuit
(PacketStream System dataWidth ())
(PacketStream System dataWidth (Vec headerBytes (BitVector 8)))
ckt = depacketizerC const
Copy link
Member

Choose a reason for hiding this comment

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

Can you also test the behavior of metadata fields?

Comment on lines +26 to +32
-- | Collect mode for `packetArbiterC`
data ArbiterMode
= -- | Collect in a round-robin fashion. Fair and cheaper than `Parallel`.
RoundRobin
| -- | Check components in parallel. This mode has a higher throughput, but is
-- biased towards the last source and also slightly more expensive.
Parallel
Copy link
Member

Choose a reason for hiding this comment

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

Reuse existing type from Df

Copy link
Collaborator

@t-wallet t-wallet Dec 2, 2024

Choose a reason for hiding this comment

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

Just checked it, it's not quite the same. roundrobinCollect also has a NoSkip mode:

Collect in a roundrobin fashion. If a component does not produce data, wait until it does.

We could add this mode to the arbiter as well.

Copy link
Member

Choose a reason for hiding this comment

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

That would be nice, let's try to make it uniform

- Removed dangerous leZeroIsZero (could cause type checker to break)
- Added source argument to nullByte
- Improved documentation of unsafeAbortOnBackpressureC
- Remove use of bitCoerce if only pack or unpack is required
- Removed M2SNoMeta in favour of PacketStreamM2S n ()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

8 participants