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