soc/core/uart: add fixed burst support to UARTBone.
Allows speeding-up consecutives accesses on the same address. This is currently used by LiteDRAM bench to speed-up the logging of the BIOS over the crossover UART, but could be useful for other purposes.
This commit is contained in:
parent
1fb48d308e
commit
b44ca6d61a
|
@ -249,17 +249,22 @@ class UART(Module, AutoCSR, UARTInterface):
|
|||
|
||||
# UART Bone ----------------------------------------------------------------------------------------
|
||||
|
||||
CMD_WRITE = 0x01
|
||||
CMD_READ = 0x02
|
||||
CMD_WRITE_BURST_INCR = 0x01
|
||||
CMD_READ_BURST_INCR = 0x02
|
||||
CMD_WRITE_BURST_FIXED = 0x03
|
||||
CMD_READ_BURST_FIXED = 0x04
|
||||
|
||||
class Stream2Wishbone(Module):
|
||||
def __init__(self, phy, clk_freq, data_width=32, address_width=32):
|
||||
def __init__(self, phy=None, clk_freq=None, data_width=32, address_width=32):
|
||||
self.sink = sink = stream.Endpoint([("data", 8)]) if phy is None else phy.source
|
||||
self.source = source = stream.Endpoint([("data", 8)]) if phy is None else phy.sink
|
||||
self.wishbone = wishbone.Interface()
|
||||
self.comb += phy.source.ready.eq(1) # Always accept incoming stream.
|
||||
self.comb += sink.ready.eq(1) # Always accept incoming stream.
|
||||
|
||||
# # #
|
||||
|
||||
cmd = Signal(8, reset_less=True)
|
||||
incr = Signal()
|
||||
length = Signal(8, reset_less=True)
|
||||
address = Signal(address_width, reset_less=True)
|
||||
data = Signal(data_width, reset_less=True)
|
||||
|
@ -277,25 +282,27 @@ class Stream2Wishbone(Module):
|
|||
fsm.act("RECEIVE-CMD",
|
||||
NextValue(bytes_count, 0),
|
||||
NextValue(words_count, 0),
|
||||
If(phy.source.valid,
|
||||
NextValue(cmd, phy.source.data),
|
||||
If(sink.valid,
|
||||
NextValue(cmd, sink.data),
|
||||
NextState("RECEIVE-LENGTH")
|
||||
)
|
||||
)
|
||||
fsm.act("RECEIVE-LENGTH",
|
||||
If(phy.source.valid,
|
||||
NextValue(length, phy.source.data),
|
||||
If(sink.valid,
|
||||
NextValue(length, sink.data),
|
||||
NextState("RECEIVE-ADDRESS")
|
||||
)
|
||||
)
|
||||
fsm.act("RECEIVE-ADDRESS",
|
||||
If(phy.source.valid,
|
||||
NextValue(address, Cat(phy.source.data, address)),
|
||||
If(sink.valid,
|
||||
NextValue(address, Cat(sink.data, address)),
|
||||
NextValue(bytes_count, bytes_count + 1),
|
||||
If(bytes_count_done,
|
||||
If(cmd == CMD_WRITE,
|
||||
If((cmd == CMD_WRITE_BURST_INCR) | (cmd == CMD_WRITE_BURST_FIXED),
|
||||
NextValue(incr, cmd == CMD_WRITE_BURST_INCR),
|
||||
NextState("RECEIVE-DATA")
|
||||
).Elif(cmd == CMD_READ,
|
||||
).Elif((cmd == CMD_READ_BURST_INCR) | (cmd == CMD_READ_BURST_FIXED),
|
||||
NextValue(incr, cmd == CMD_READ_BURST_INCR),
|
||||
NextState("READ-DATA")
|
||||
).Else(
|
||||
NextState("RECEIVE-CMD")
|
||||
|
@ -304,8 +311,8 @@ class Stream2Wishbone(Module):
|
|||
)
|
||||
)
|
||||
fsm.act("RECEIVE-DATA",
|
||||
If(phy.source.valid,
|
||||
NextValue(data, Cat(phy.source.data, data)),
|
||||
If(sink.valid,
|
||||
NextValue(data, Cat(sink.data, data)),
|
||||
NextValue(bytes_count, bytes_count + 1),
|
||||
If(bytes_count_done,
|
||||
NextState("WRITE-DATA")
|
||||
|
@ -323,7 +330,7 @@ class Stream2Wishbone(Module):
|
|||
self.wishbone.cyc.eq(1),
|
||||
If(self.wishbone.ack,
|
||||
NextValue(words_count, words_count + 1),
|
||||
NextValue(address, address + 1),
|
||||
NextValue(address, address + incr),
|
||||
If(words_count_done,
|
||||
NextState("RECEIVE-CMD")
|
||||
).Else(
|
||||
|
@ -342,15 +349,15 @@ class Stream2Wishbone(Module):
|
|||
)
|
||||
cases = {}
|
||||
for i, n in enumerate(reversed(range(data_width//8))):
|
||||
cases[i] = phy.sink.data.eq(data[8*n:])
|
||||
cases[i] = source.data.eq(data[8*n:])
|
||||
self.comb += Case(bytes_count, cases)
|
||||
fsm.act("SEND-DATA",
|
||||
phy.sink.valid.eq(1),
|
||||
If(phy.sink.ready,
|
||||
source.valid.eq(1),
|
||||
If(source.ready,
|
||||
NextValue(bytes_count, bytes_count + 1),
|
||||
If(bytes_count_done,
|
||||
NextValue(words_count, words_count + 1),
|
||||
NextValue(address, address + 1),
|
||||
NextValue(address, address + incr),
|
||||
If(words_count_done,
|
||||
NextState("RECEIVE-CMD")
|
||||
).Else(
|
||||
|
@ -359,9 +366,9 @@ class Stream2Wishbone(Module):
|
|||
)
|
||||
)
|
||||
)
|
||||
self.comb += phy.sink.last.eq(bytes_count_done & words_count_done)
|
||||
if hasattr(phy.sink, "length"):
|
||||
self.comb += phy.sink.length.eq((data_width//8)*length)
|
||||
self.comb += source.last.eq(bytes_count_done & words_count_done)
|
||||
if hasattr(source, "length"):
|
||||
self.comb += source.length.eq((data_width//8)*length)
|
||||
|
||||
|
||||
class UARTBone(Stream2Wishbone):
|
||||
|
|
|
@ -36,11 +36,12 @@ class RemoteClient(EtherboneIPC, CSRBuilder):
|
|||
self.socket.close()
|
||||
del self.socket
|
||||
|
||||
def read(self, addr, length=None):
|
||||
def read(self, addr, length=None, burst="incr"):
|
||||
length_int = 1 if length is None else length
|
||||
# prepare packet
|
||||
record = EtherboneRecord()
|
||||
record.reads = EtherboneReads(addrs=[self.base_address + addr + 4*j for j in range(length_int)])
|
||||
incr = (burst == "incr")
|
||||
record.reads = EtherboneReads(addrs=[self.base_address + addr + 4*incr*j for j in range(length_int)])
|
||||
record.rcount = len(record.reads)
|
||||
|
||||
# send packet
|
||||
|
|
|
@ -18,27 +18,51 @@ import threading
|
|||
from litex.tools.remote.etherbone import EtherbonePacket, EtherboneRecord, EtherboneWrites
|
||||
from litex.tools.remote.etherbone import EtherboneIPC
|
||||
|
||||
def _read_merger(addrs, max_length=256):
|
||||
def _read_merger(addrs, max_length=256, bursts=["incr", "fixed"]):
|
||||
"""Sequential reads merger
|
||||
|
||||
Take a list of read addresses as input and merge the sequential reads in (base, length) tuples:
|
||||
Example: [0x0, 0x4, 0x10, 0x14] input will return [(0x0,2), (0x10,2)].
|
||||
Take a list of read addresses as input and merge the sequential/fixed reads in (base, length, burst) tuples:
|
||||
Example: [0x0, 0x4, 0x10, 0x14, 0x20, 0x20] input will return [(0x0,2, "incr"), (0x10,2, "incr"), (0x20,2, "fixed")].
|
||||
|
||||
This is useful for UARTBone/Etherbone where command/response roundtrip delay is responsible for
|
||||
most of the access delay and allows minimizing number of commands by grouping them in UARTBone
|
||||
packets.
|
||||
"""
|
||||
base = None
|
||||
length = 0
|
||||
for addr in addrs:
|
||||
if (addr - (4*length) != base) or (length == max_length):
|
||||
if base is not None:
|
||||
yield (base, length)
|
||||
base = addr
|
||||
length = 0
|
||||
length += 1
|
||||
yield (base, length)
|
||||
assert "incr" in bursts
|
||||
burst_base = addrs[0]
|
||||
burst_length = 1
|
||||
burst_type = "incr"
|
||||
for addr in addrs[1:]:
|
||||
merged = False
|
||||
# Try to merge to a "fixed" burst if supported
|
||||
if ("fixed" in bursts):
|
||||
# If current burst matches
|
||||
if (burst_type in [None, "fixed"]) or (burst_length == 1):
|
||||
# If addr matches
|
||||
if (addr == burst_base):
|
||||
if (burst_length != max_length):
|
||||
burst_type = "fixed"
|
||||
burst_length += 1
|
||||
merged = True
|
||||
|
||||
# Try to merge to an "incr" burst if supported
|
||||
if ("incr" in bursts):
|
||||
# If current burst matches
|
||||
if (burst_type in [None, "incr"]) or (burst_length == 1):
|
||||
# If addr matches
|
||||
if (addr == burst_base + (4 * burst_length)):
|
||||
if (burst_length != max_length):
|
||||
burst_type = "incr"
|
||||
burst_length += 1
|
||||
merged = True
|
||||
|
||||
# Generate current burst if addr has not able to merge
|
||||
if not merged:
|
||||
yield (burst_base, burst_length, burst_type)
|
||||
burst_base = addr
|
||||
burst_length = 1
|
||||
burst_type = "incr"
|
||||
yield (burst_base, burst_length, burst_type)
|
||||
|
||||
class RemoteServer(EtherboneIPC):
|
||||
def __init__(self, comm, bind_ip, bind_port=1234):
|
||||
|
@ -101,9 +125,14 @@ class RemoteServer(EtherboneIPC):
|
|||
"CommUART": 256,
|
||||
"CommUDP": 4,
|
||||
}.get(self.comm.__class__.__name__, 1)
|
||||
bursts = {
|
||||
"CommUART": ["incr", "fixed"]
|
||||
}.get(self.comm.__class__.__name__, ["incr"])
|
||||
reads = []
|
||||
for addr, length in _read_merger(record.reads.get_addrs(), max_length=max_length):
|
||||
reads += self.comm.read(addr, length)
|
||||
for addr, length, burst in _read_merger(record.reads.get_addrs(),
|
||||
max_length = max_length,
|
||||
bursts = bursts):
|
||||
reads += self.comm.read(addr, length, burst)
|
||||
|
||||
record = EtherboneRecord()
|
||||
record.writes = EtherboneWrites(datas=reads)
|
||||
|
|
|
@ -26,7 +26,8 @@ class CommPCIe:
|
|||
del self.file
|
||||
self.mmap.close()
|
||||
|
||||
def read(self, addr, length=None):
|
||||
def read(self, addr, length=None, burst="incr"):
|
||||
assert burst == "incr"
|
||||
data = []
|
||||
length_int = 1 if length is None else length
|
||||
for i in range(length_int):
|
||||
|
|
|
@ -7,12 +7,12 @@
|
|||
import serial
|
||||
import struct
|
||||
|
||||
CMD_WRITE_BURST_INCR = 0x01
|
||||
CMD_READ_BURST_INCR = 0x02
|
||||
CMD_WRITE_BURST_FIXED = 0x03
|
||||
CMD_READ_BURST_FIXED = 0x04
|
||||
|
||||
class CommUART:
|
||||
msg_type = {
|
||||
"write": 0x01,
|
||||
"read": 0x02
|
||||
}
|
||||
def __init__(self, port, baudrate=115200, debug=False):
|
||||
self.port = port
|
||||
self.baudrate = str(baudrate)
|
||||
|
@ -48,11 +48,15 @@ class CommUART:
|
|||
if self.port.inWaiting() > 0:
|
||||
self.port.read(self.port.inWaiting())
|
||||
|
||||
def read(self, addr, length=None):
|
||||
def read(self, addr, length=None, burst="incr"):
|
||||
self._flush()
|
||||
data = []
|
||||
length_int = 1 if length is None else length
|
||||
self._write([self.msg_type["read"], length_int])
|
||||
cmd = {
|
||||
"incr" : CMD_READ_BURST_INCR,
|
||||
"fixed": CMD_READ_BURST_FIXED,
|
||||
}[burst]
|
||||
self._write([cmd, length_int])
|
||||
self._write(list((addr//4).to_bytes(4, byteorder="big")))
|
||||
for i in range(length_int):
|
||||
value = int.from_bytes(self._read(4), "big")
|
||||
|
@ -63,14 +67,18 @@ class CommUART:
|
|||
data.append(value)
|
||||
return data
|
||||
|
||||
def write(self, addr, data):
|
||||
def write(self, addr, data, burst="incr"):
|
||||
self._flush()
|
||||
data = data if isinstance(data, list) else [data]
|
||||
length = len(data)
|
||||
offset = 0
|
||||
while length:
|
||||
size = min(length, 8)
|
||||
self._write([self.msg_type["write"], size])
|
||||
cmd = {
|
||||
"incr" : CMD_WRITE_BURST_INCR,
|
||||
"fixed": CMD_WRITE_BURST_FIXED,
|
||||
}[burst]
|
||||
self._write([cmd, size])
|
||||
self._write(list(((addr+offset)//4).to_bytes(4, byteorder="big")))
|
||||
for i, value in enumerate(data[offset:offset+size]):
|
||||
self._write(list(value.to_bytes(4, byteorder="big")))
|
||||
|
|
|
@ -29,7 +29,8 @@ class CommUDP:
|
|||
self.socket.close()
|
||||
del self.socket
|
||||
|
||||
def read(self, addr, length=None):
|
||||
def read(self, addr, length=None, burst="incr"):
|
||||
assert burst == "incr"
|
||||
length_int = 1 if length is None else length
|
||||
record = EtherboneRecord()
|
||||
record.reads = EtherboneReads(addrs=[addr+4*j for j in range(length_int)])
|
||||
|
|
|
@ -90,7 +90,8 @@ class CommUSB:
|
|||
return
|
||||
del self.dev
|
||||
|
||||
def read(self, addr, length=None):
|
||||
def read(self, addr, length=None, burst="incr"):
|
||||
assert burst == "incr"
|
||||
data = []
|
||||
length_int = 1 if length is None else length
|
||||
for i in range(length_int):
|
||||
|
|
Loading…
Reference in New Issue