uart: rewrite bridge to support backpressure on output
This commit is contained in:
parent
de42bf98f4
commit
018f7cc223
|
|
@ -15,183 +15,138 @@ class EthernetBridge(wiring.Component):
|
||||||
def elaborate(self, platform):
|
def elaborate(self, platform):
|
||||||
m = Module()
|
m = Module()
|
||||||
|
|
||||||
|
msg_type = Signal(MessageTypes)
|
||||||
seq_num_expected = Signal(13)
|
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"):
|
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):
|
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
|
# Send NACK if message type or sequence number is incorrect
|
||||||
with m.If(
|
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)
|
| (self.sink.data[3:16] != seq_num_expected)
|
||||||
):
|
):
|
||||||
# Wait to NACK if this isn't the last beat in message
|
m.d.sync += seen_last.eq(self.sink.last)
|
||||||
with m.If(~self.sink.last):
|
m.next = "NACK"
|
||||||
m.next = "NACK_WAIT_FOR_LAST"
|
|
||||||
|
|
||||||
# Otherwise, NACK immediately
|
with m.Else():
|
||||||
with m.Else():
|
m.d.sync += msg_type.eq(self.sink.data[:3])
|
||||||
m.d.sync += self.source.data.eq(
|
m.d.sync += count.eq(self.sink.data[16:23])
|
||||||
EthernetMessageHeader.concat_signals(
|
m.next = "WAIT_FOR_ADDR"
|
||||||
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.Elif(self.sink.data[:3] == MessageTypes.READ_REQUEST):
|
with m.State("WAIT_FOR_ADDR"):
|
||||||
m.d.sync += seq_num_expected.eq(seq_num_expected + 1)
|
with m.If(self.sink.valid & self.sink.ready):
|
||||||
m.d.sync += read_len.eq(self.sink.data[16:23] - 1)
|
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(
|
m.d.sync += self.source.data.eq(
|
||||||
EthernetMessageHeader.concat_signals(
|
EthernetMessageHeader.concat_signals(
|
||||||
MessageTypes.READ_RESPONSE,
|
MessageTypes.READ_RESPONSE,
|
||||||
seq_num_expected,
|
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 = "READ"
|
||||||
m.next = "WRITE_WAIT_FOR_ADDR"
|
|
||||||
|
|
||||||
with m.State("READ_WAIT_FOR_ADDR"):
|
with m.Elif(msg_type == MessageTypes.WRITE_REQUEST):
|
||||||
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():
|
|
||||||
m.next = "WRITE"
|
m.next = "WRITE"
|
||||||
|
|
||||||
with m.State("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.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.data.eq(self.sink.data)
|
||||||
m.d.sync += self.bus_source.p.rw.eq(1)
|
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.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():
|
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"):
|
with m.If(seen_last & (count == 0)):
|
||||||
m.d.sync += self.bus_source.p.eq(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):
|
with m.Else():
|
||||||
m.d.sync += seq_num_expected.eq(seq_num_expected + 1)
|
m.d.sync += self.source.valid.eq(1)
|
||||||
m.d.sync += self.source.data.eq(
|
m.d.sync += self.source.last.eq(1)
|
||||||
EthernetMessageHeader.concat_signals(
|
m.d.sync += self.source.data.eq(
|
||||||
MessageTypes.WRITE_RESPONSE,
|
EthernetMessageHeader.concat_signals(
|
||||||
seq_num_expected,
|
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.valid.eq(1)
|
||||||
m.d.sync += self.source.last.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(
|
m.d.sync += self.source.data.eq(
|
||||||
EthernetMessageHeader.concat_signals(
|
EthernetMessageHeader.concat_signals(
|
||||||
MessageTypes.NACK,
|
MessageTypes.NACK,
|
||||||
seq_num_expected,
|
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.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
|
return m
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue