uart: rewrite bridge to support backpressure on output
This commit is contained in:
parent
776d465ef2
commit
dd6a3ac467
|
|
@ -15,183 +15,138 @@ class EthernetBridge(wiring.Component):
|
|||
def elaborate(self, platform):
|
||||
m = Module()
|
||||
|
||||
msg_type = Signal(MessageTypes)
|
||||
seq_num_expected = Signal(13)
|
||||
read_len = Signal(7)
|
||||
|
||||
with m.FSM(init="IDLE"):
|
||||
seen_last = Signal()
|
||||
|
||||
count = Signal(7)
|
||||
|
||||
with m.FSM() as fsm:
|
||||
with m.State("IDLE"):
|
||||
m.d.sync += self.sink.ready.eq(1)
|
||||
m.d.sync += self.source.valid.eq(0)
|
||||
|
||||
# TODO: not necessary, but makes debugging way easier
|
||||
m.d.sync += self.source.last.eq(0)
|
||||
m.d.sync += self.source.data.eq(0)
|
||||
|
||||
with m.If(self.sink.valid & self.sink.ready):
|
||||
# First 32 bits was presented, which contains message type (first 3 bits)
|
||||
# as well as sequence number (next 13 bits). The remaining 16 bits are unused.
|
||||
|
||||
# Send NACK if message type or sequence number is incorrect
|
||||
with m.If(
|
||||
(self.sink.data[:3] > max(MessageTypes))
|
||||
(
|
||||
(self.sink.data[:3] != MessageTypes.READ_REQUEST)
|
||||
& (self.sink.data[:3] != MessageTypes.WRITE_REQUEST)
|
||||
)
|
||||
| (self.sink.data[3:16] != seq_num_expected)
|
||||
):
|
||||
# Wait to NACK if this isn't the last beat in message
|
||||
with m.If(~self.sink.last):
|
||||
m.next = "NACK_WAIT_FOR_LAST"
|
||||
m.d.sync += seen_last.eq(self.sink.last)
|
||||
m.next = "NACK"
|
||||
|
||||
# Otherwise, NACK immediately
|
||||
with m.Else():
|
||||
m.d.sync += self.source.data.eq(
|
||||
EthernetMessageHeader.concat_signals(
|
||||
MessageTypes.NACK,
|
||||
seq_num_expected,
|
||||
)
|
||||
)
|
||||
m.d.sync += self.source.valid.eq(1)
|
||||
m.d.sync += self.source.last.eq(1)
|
||||
m.d.sync += self.sink.ready.eq(0)
|
||||
m.next = "NACK_WAIT_FOR_READY"
|
||||
with m.Else():
|
||||
m.d.sync += msg_type.eq(self.sink.data[:3])
|
||||
m.d.sync += count.eq(self.sink.data[16:23])
|
||||
m.next = "WAIT_FOR_ADDR"
|
||||
|
||||
with m.Elif(self.sink.data[:3] == MessageTypes.READ_REQUEST):
|
||||
m.d.sync += seq_num_expected.eq(seq_num_expected + 1)
|
||||
m.d.sync += read_len.eq(self.sink.data[16:23] - 1)
|
||||
with m.State("WAIT_FOR_ADDR"):
|
||||
with m.If(self.sink.valid & self.sink.ready):
|
||||
m.d.sync += self.bus_source.p.addr.eq(self.sink.data)
|
||||
m.d.sync += seq_num_expected.eq(seq_num_expected + 1)
|
||||
|
||||
with m.If(msg_type == MessageTypes.READ_REQUEST):
|
||||
# Send read response header
|
||||
m.d.sync += self.source.valid.eq(1)
|
||||
m.d.sync += self.source.last.eq(0)
|
||||
m.d.sync += self.source.data.eq(
|
||||
EthernetMessageHeader.concat_signals(
|
||||
MessageTypes.READ_RESPONSE,
|
||||
seq_num_expected,
|
||||
)
|
||||
)
|
||||
m.d.sync += self.source.valid.eq(1)
|
||||
m.next = "READ_WAIT_FOR_ADDR"
|
||||
|
||||
with m.Elif(self.sink.data[:3] == MessageTypes.WRITE_REQUEST):
|
||||
m.next = "WRITE_WAIT_FOR_ADDR"
|
||||
m.next = "READ"
|
||||
|
||||
with m.State("READ_WAIT_FOR_ADDR"):
|
||||
m.d.sync += self.source.valid.eq(0)
|
||||
m.d.sync += self.source.data.eq(0)
|
||||
|
||||
with m.If(self.sink.valid):
|
||||
# we have the length and the address to read from, let's go!
|
||||
m.d.sync += self.bus_source.p.addr.eq(self.sink.data)
|
||||
m.d.sync += self.bus_source.p.data.eq(0)
|
||||
m.d.sync += self.bus_source.p.rw.eq(0)
|
||||
m.d.sync += self.bus_source.p.valid.eq(1)
|
||||
|
||||
with m.If(read_len == 0):
|
||||
# we've sent the last read request in this batch to the bus
|
||||
m.d.sync += self.bus_source.p.last.eq(1)
|
||||
m.d.sync += read_len.eq(0)
|
||||
|
||||
m.next = "READ"
|
||||
|
||||
with m.State("READ"):
|
||||
m.d.sync += self.sink.ready.eq(0)
|
||||
|
||||
# Clock out read requests to the bus
|
||||
with m.If(read_len > 0):
|
||||
m.d.sync += self.bus_source.p.addr.eq(self.bus_source.p.addr + 1)
|
||||
m.d.sync += read_len.eq(read_len - 1)
|
||||
|
||||
with m.If(read_len == 1):
|
||||
m.d.sync += self.bus_source.p.last.eq(1)
|
||||
|
||||
with m.Else():
|
||||
m.d.sync += self.bus_source.p.eq(
|
||||
0
|
||||
) # TODO: it's probably overzealous to set the whole bus to zero, but it makes debugging easy so we're doing it xD
|
||||
|
||||
# Clock out any read data from the bus
|
||||
with m.If(self.bus_sink.p.valid):
|
||||
m.d.sync += self.source.data.eq(self.bus_sink.p.data)
|
||||
m.d.sync += self.source.valid.eq(1)
|
||||
m.d.sync += self.source.last.eq(self.bus_sink.p.last)
|
||||
|
||||
with m.If(self.source.last):
|
||||
m.d.sync += self.source.data.eq(0)
|
||||
m.d.sync += self.source.valid.eq(0)
|
||||
m.d.sync += self.source.last.eq(0)
|
||||
m.next = "IDLE" # TODO: could save a cycle by checking valid_i to see if there's more work to do
|
||||
|
||||
with m.State("WRITE_WAIT_FOR_ADDR"):
|
||||
with m.If(self.sink.valid):
|
||||
m.d.sync += self.bus_source.p.addr.eq(self.sink.data)
|
||||
m.next = "WRITE_FIRST"
|
||||
|
||||
# Don't want to increment address on the first write,
|
||||
# and I'm lazy so I'm making a new state to keep track of that
|
||||
with m.State("WRITE_FIRST"):
|
||||
with m.If(self.sink.valid):
|
||||
m.d.sync += self.bus_source.p.data.eq(self.sink.data)
|
||||
m.d.sync += self.bus_source.p.rw.eq(1)
|
||||
m.d.sync += self.bus_source.p.valid.eq(1)
|
||||
m.d.sync += self.bus_source.p.last.eq(self.sink.last)
|
||||
|
||||
with m.If(self.sink.last):
|
||||
m.d.sync += self.sink.ready.eq(0)
|
||||
m.next = "WRITE_WAIT_FOR_LAST"
|
||||
|
||||
with m.Else():
|
||||
with m.Elif(msg_type == MessageTypes.WRITE_REQUEST):
|
||||
m.next = "WRITE"
|
||||
|
||||
with m.State("WRITE"):
|
||||
with m.If(self.sink.valid):
|
||||
# Keep a running count of the number of requests inflight on the bus
|
||||
# Once that hits zero and seen_last is high, we're done!
|
||||
# Send write response and wait for it to clock out
|
||||
|
||||
m.d.sync += seen_last.eq(
|
||||
seen_last | (self.sink.last & self.sink.valid & self.sink.ready)
|
||||
)
|
||||
m.d.sync += count.eq(
|
||||
count + (self.sink.valid & self.sink.ready) - self.bus_sink.p.valid
|
||||
)
|
||||
|
||||
with m.If(self.sink.valid & self.sink.ready):
|
||||
m.d.sync += self.bus_source.p.addr.eq(self.bus_source.p.addr + 1)
|
||||
m.d.sync += self.bus_source.p.data.eq(self.sink.data)
|
||||
m.d.sync += self.bus_source.p.rw.eq(1)
|
||||
m.d.sync += self.bus_source.p.valid.eq(1)
|
||||
m.d.sync += self.bus_source.p.last.eq(self.sink.last)
|
||||
|
||||
with m.If(self.sink.last):
|
||||
m.d.sync += self.sink.ready.eq(0)
|
||||
m.next = "WRITE_WAIT_FOR_LAST"
|
||||
|
||||
with m.Else():
|
||||
m.next = "WRITE"
|
||||
|
||||
with m.Else():
|
||||
m.next = "WRITE"
|
||||
m.d.sync += self.bus_source.p.data.eq(0) # just for clarity of debugging
|
||||
m.d.sync += self.bus_source.p.rw.eq(0) # just for clarity of debugging
|
||||
m.d.sync += self.bus_source.p.valid.eq(0)
|
||||
|
||||
with m.State("WRITE_WAIT_FOR_LAST"):
|
||||
m.d.sync += self.bus_source.p.eq(0)
|
||||
with m.If(seen_last & (count == 0)):
|
||||
with m.If(self.source.valid & self.source.ready):
|
||||
m.d.sync += self.source.valid.eq(0)
|
||||
m.d.sync += self.source.last.eq(0) # just for clarity of debugging
|
||||
m.d.sync += self.source.data.eq(0) # just for clarity of debugging
|
||||
m.next = "IDLE"
|
||||
|
||||
with m.If(self.bus_sink.p.last):
|
||||
m.d.sync += seq_num_expected.eq(seq_num_expected + 1)
|
||||
m.d.sync += self.source.data.eq(
|
||||
EthernetMessageHeader.concat_signals(
|
||||
MessageTypes.WRITE_RESPONSE,
|
||||
seq_num_expected,
|
||||
with m.Else():
|
||||
m.d.sync += self.source.valid.eq(1)
|
||||
m.d.sync += self.source.last.eq(1)
|
||||
m.d.sync += self.source.data.eq(
|
||||
EthernetMessageHeader.concat_signals(
|
||||
MessageTypes.WRITE_RESPONSE,
|
||||
seq_num_expected,
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
with m.State("READ"):
|
||||
# Wait for downstream to accept data. Put next read request on the bus after it's accepted, if more is needed
|
||||
m.d.sync += self.bus_source.p.valid.eq(0)
|
||||
with m.If(self.source.valid):
|
||||
with m.If(self.source.ready):
|
||||
m.d.sync += self.source.valid.eq(0)
|
||||
m.d.sync += self.source.data.eq(0) # for debugging
|
||||
m.d.sync += self.source.last.eq(0) # for debugging
|
||||
|
||||
with m.If(self.source.last):
|
||||
m.next = "IDLE"
|
||||
|
||||
with m.Else():
|
||||
m.d.sync += self.bus_source.p.rw.eq(0)
|
||||
m.d.sync += self.bus_source.p.valid.eq(1)
|
||||
m.d.sync += count.eq(count - 1)
|
||||
|
||||
# Not waiting on downstream to accept data. No need to issue bus request.
|
||||
# Instead check if data's available on bus_sink, and put it on source if so
|
||||
with m.Else():
|
||||
with m.If(self.bus_sink.p.valid):
|
||||
m.d.sync += self.source.data.eq(self.bus_sink.p.data)
|
||||
m.d.sync += self.source.valid.eq(1)
|
||||
m.d.sync += self.source.last.eq(count == 0)
|
||||
m.d.sync += self.bus_source.p.addr.eq(self.bus_source.p.addr + 1)
|
||||
|
||||
with m.State("NACK"):
|
||||
# Only send NACK after full packet has been received
|
||||
with m.If(seen_last | (self.sink.valid & self.sink.ready & self.sink.last)):
|
||||
m.d.sync += self.source.valid.eq(1)
|
||||
m.d.sync += self.source.last.eq(1)
|
||||
m.next = "IDLE" # TODO: could save a cycle by checking valid_i to see if there's more work to do
|
||||
|
||||
with m.State("NACK_WAIT_FOR_LAST"):
|
||||
with m.If(self.sink.last):
|
||||
m.d.sync += self.source.data.eq(
|
||||
EthernetMessageHeader.concat_signals(
|
||||
MessageTypes.NACK,
|
||||
seq_num_expected,
|
||||
)
|
||||
)
|
||||
m.d.sync += self.source.valid.eq(1)
|
||||
m.d.sync += self.source.last.eq(1)
|
||||
m.d.sync += self.sink.ready.eq(0)
|
||||
m.next = "NACK_WAIT_FOR_READY"
|
||||
|
||||
with m.State("NACK_WAIT_FOR_READY"):
|
||||
with m.If(self.source.ready):
|
||||
m.d.sync += self.source.valid.eq(0)
|
||||
|
||||
# TODO: remove these next two lines, they're not necessary
|
||||
# although they are nice for debug...
|
||||
m.d.sync += self.source.data.eq(0)
|
||||
m.d.sync += self.source.last.eq(0)
|
||||
m.d.sync += self.sink.ready.eq(1)
|
||||
|
||||
with m.If(self.source.valid & self.source.ready):
|
||||
m.next = "IDLE"
|
||||
|
||||
m.d.sync += self.source.valid.eq(0)
|
||||
m.d.sync += self.source.last.eq(0)
|
||||
m.d.sync += self.source.data.eq(0)
|
||||
|
||||
m.d.comb += self.sink.ready.eq(~fsm.ongoing("READ"))
|
||||
|
||||
return m
|
||||
|
|
|
|||
Loading…
Reference in New Issue