Stream utilities
Splitting / merging streams
- class tiliqua.dsp.Split(*args, src_loc_at=0, **kwargs)
Consumes payloads from a single stream and splits it into multiple independent streams. This component may be instantiated in 2 modes depending on the value of
replicate
:- Channel splitter (
replicate == False
): The incoming stream has an
data.ArrayLayout
signature. Each payload in thedata.ArrayLayout
becomes an independent outgoing stream.n_channels
must match the number of payloads in thedata.ArrayLayout
.
- Channel splitter (
- Channel replicater (
replicate == True
): The incoming stream has a single payload. Each payload in the incoming stream is replicated and at the output appears as
n_channels
independent streams, which produce the same values, however may be synchronized/consumed independently.
- Channel replicater (
This class is inspired by previous work in the lambdalib and LiteX projects.
- __init__(n_channels, replicate=False, source=None)
- n_channelsint
The number of independent output streams. See usage above.
- replicatebool, optional
See usage above.
- sourcestream, optional
Optional incoming stream to pass through to
wiring.connect
on elaboration. This argument means you do not have to hook upself.i
and can make some pipelines a little easier to read.
- wire_ready(m, channels)
Set out channels as permanently READY so they don’t block progress.
- class tiliqua.dsp.Merge(*args, src_loc_at=0, **kwargs)
Consumes payloads from multiple independent streams and merges them into a single stream.
This class is inspired by previous work in the lambdalib and LiteX projects.
- __init__(n_channels, sink=None)
- n_channelsint
The number of independent incoming streams.
- sinkstream, optional
Optional outgoing stream to pass through to
wiring.connect
on elaboration. This argument means you do not have to hook upself.o
and can make some pipelines a little easier to read.
- wire_valid(m, channels)
Set in channels as permanently VALID so they don’t block progress.
Connecting and remapping streams
- tiliqua.dsp.connect_remap(m, stream_o, stream_i, mapping)
Connect 2 streams, bypassing normal wiring.connect() checks that the signatures match. This allows easily remapping fields when you are trying to connect streams with different signatures.
For example, say I have a stream with an ArrayLayout payload and want to map it to a different stream with a StructLayout payload, and the underlying bit-representation of both layouts do not match, I can remap using:
dsp.connect_remap(m, vca_merge2a.o, vca0.i, lambda o, i : [ i.payload.x .eq(o.payload[0]), i.payload.gain.eq(o.payload[1] << 2) ])
This is a bit of a hack. TODO perhaps implement this as a StreamConverter such that we can still use wiring.connect?.
- tiliqua.dsp.channel_remap(m, stream_o, stream_i, mapping_o_to_i)
Connect 2 streams of type
data.ArrayLayout
, with different channel counts or channel indices. For example, to connect a source with 4 channels to a sink with 2 channels, mapping 0 to 0, 1 to 1, leaving 2 and 3 unconnected:s1 = stream.Signature(data.ArrayLayout(ASQ, 4)).create() s2 = stream.Signature(data.ArrayLayout(ASQ, 2)).create() dsp.channel_remap(m, s1, s2, {0: 0, 1: 1})
This also works the other way around, to connect e.g. a source with 2 channels to a sink with 4 channels. The stream will make progress however the value of the payloads in any unmapped output channels is undefined.
Connecting streams in feedback loops
- class tiliqua.dsp.KickFeedback(*args, src_loc_at=0, **kwargs)
Inject a single dummy (garbage) sample after reset between two streams. This is necessary to break infinite blocking after reset if streams are set up in a feedback loop.
- tiliqua.dsp.connect_feedback_kick(m, o, i)