mirror of
https://github.com/enjoy-digital/litex.git
synced 2025-01-04 09:52:26 -05:00
Merge pull request #1822 from trabucayre/rework_tools
tools: litex_server, litex_client and remote: don't hardcode bus address
This commit is contained in:
commit
4b9601bdab
5 changed files with 130 additions and 58 deletions
|
@ -21,16 +21,21 @@ from litex.tools.remote.csr_builder import CSRBuilder
|
|||
# Remote Client ------------------------------------------------------------------------------------
|
||||
|
||||
class RemoteClient(EtherboneIPC, CSRBuilder):
|
||||
def __init__(self, host="localhost", port=1234, base_address=0, csr_csv=None, csr_data_width=None, debug=False):
|
||||
def __init__(self, host="localhost", port=1234, base_address=0, csr_csv=None, csr_data_width=None,
|
||||
csr_bus_address_width=None, debug=False):
|
||||
# If csr_csv set to None and local csr.csv file exists, use it.
|
||||
if csr_csv is None and os.path.exists("csr.csv"):
|
||||
csr_csv = "csr.csv"
|
||||
# If valid csr_csv file found, build the CSRs.
|
||||
if csr_csv is not None:
|
||||
CSRBuilder.__init__(self, self, csr_csv, csr_data_width)
|
||||
# Else if csr_data_width set to None, force to csr_data_width 32-bit.
|
||||
elif csr_data_width is None:
|
||||
csr_data_width = 32
|
||||
else:
|
||||
# Else if csr_data_width set to None, force to csr_data_width 32-bit.
|
||||
if csr_data_width is None:
|
||||
csr_data_width = 32
|
||||
# Else if csr_bus_address_width set to None, force to csr_bus_address_width 32-bit.
|
||||
if self.csr_bus_address_width is None:
|
||||
self.csr_bus_address_width = 32
|
||||
self.host = host
|
||||
self.port = port
|
||||
self.debug = debug
|
||||
|
@ -61,20 +66,27 @@ class RemoteClient(EtherboneIPC, CSRBuilder):
|
|||
|
||||
def read(self, addr, length=None, burst="incr"):
|
||||
length_int = 1 if length is None else length
|
||||
addr_size = self.csr_bus_address_width // 8
|
||||
# Prepare packet
|
||||
record = EtherboneRecord()
|
||||
record = EtherboneRecord(addr_size)
|
||||
incr = (burst == "incr")
|
||||
record.reads = EtherboneReads(addrs=[self.base_address + addr + 4*incr*j for j in range(length_int)])
|
||||
record.reads = EtherboneReads(
|
||||
addr_size = addr_size,
|
||||
addrs = [self.base_address + addr + 4*incr*j for j in range(length_int)]
|
||||
)
|
||||
record.rcount = len(record.reads)
|
||||
|
||||
# Send packet
|
||||
packet = EtherbonePacket()
|
||||
packet = EtherbonePacket(self.csr_bus_address_width)
|
||||
packet.records = [record]
|
||||
packet.encode()
|
||||
self.send_packet(self.socket, packet)
|
||||
|
||||
# Receive response
|
||||
packet = EtherbonePacket(self.receive_packet(self.socket))
|
||||
packet = EtherbonePacket(
|
||||
addr_width = self.csr_bus_address_width,
|
||||
init = self.receive_packet(self.socket, addr_size)
|
||||
)
|
||||
packet.decode()
|
||||
datas = packet.records.pop().writes.get_datas()
|
||||
if self.debug:
|
||||
|
@ -84,11 +96,16 @@ class RemoteClient(EtherboneIPC, CSRBuilder):
|
|||
|
||||
def write(self, addr, datas):
|
||||
datas = datas if isinstance(datas, list) else [datas]
|
||||
record = EtherboneRecord()
|
||||
record.writes = EtherboneWrites(base_addr=self.base_address + addr, datas=[d for d in datas])
|
||||
addr_size = self.csr_bus_address_width // 8
|
||||
record = EtherboneRecord(addr_size)
|
||||
record.writes = EtherboneWrites(
|
||||
base_addr = self.base_address + addr,
|
||||
addr_size = addr_size,
|
||||
datas = [d for d in datas]
|
||||
)
|
||||
record.wcount = len(record.writes)
|
||||
|
||||
packet = EtherbonePacket()
|
||||
packet = EtherbonePacket(self.csr_bus_address_width)
|
||||
packet.records = [record]
|
||||
packet.encode()
|
||||
self.send_packet(self.socket, packet)
|
||||
|
|
|
@ -70,11 +70,12 @@ def _read_merger(addrs, max_length=256, bursts=["incr", "fixed"]):
|
|||
# Remote Server ------------------------------------------------------------------------------------
|
||||
|
||||
class RemoteServer(EtherboneIPC):
|
||||
def __init__(self, comm, bind_ip, bind_port=1234):
|
||||
self.comm = comm
|
||||
self.bind_ip = bind_ip
|
||||
self.bind_port = bind_port
|
||||
self.lock = False
|
||||
def __init__(self, comm, bind_ip, bind_port=1234, addr_width=32):
|
||||
self.comm = comm
|
||||
self.bind_ip = bind_ip
|
||||
self.bind_port = bind_port
|
||||
self.lock = False
|
||||
self.addr_width = addr_width
|
||||
|
||||
def open(self):
|
||||
if hasattr(self, "socket"):
|
||||
|
@ -115,14 +116,13 @@ class RemoteServer(EtherboneIPC):
|
|||
while True:
|
||||
# Receive packet.
|
||||
try:
|
||||
packet = self.receive_packet(client_socket)
|
||||
packet = self.receive_packet(client_socket, self.addr_width // 8)
|
||||
if packet == 0:
|
||||
break
|
||||
except:
|
||||
break
|
||||
|
||||
# Decode Packet.
|
||||
packet = EtherbonePacket(packet)
|
||||
packet = EtherbonePacket(self.addr_width, packet)
|
||||
packet.decode()
|
||||
|
||||
# Get Packet's Record.
|
||||
|
@ -152,11 +152,12 @@ class RemoteServer(EtherboneIPC):
|
|||
bursts = bursts):
|
||||
reads += self.comm.read(addr, length, burst)
|
||||
|
||||
record = EtherboneRecord()
|
||||
record.writes = EtherboneWrites(datas=reads)
|
||||
addr_size = self.addr_width // 8
|
||||
record = EtherboneRecord(addr_size)
|
||||
record.writes = EtherboneWrites(addr_size=addr_size, datas=reads)
|
||||
record.wcount = len(record.writes)
|
||||
|
||||
packet = EtherbonePacket()
|
||||
packet = EtherbonePacket(self.addr_width)
|
||||
packet.records = [record]
|
||||
packet.encode()
|
||||
self.send_packet(client_socket, packet)
|
||||
|
@ -181,6 +182,7 @@ def main():
|
|||
# Common arguments
|
||||
parser.add_argument("--bind-ip", default="localhost", help="Host bind address.")
|
||||
parser.add_argument("--bind-port", default=1234, help="Host bind port.")
|
||||
parser.add_argument("--addr-width", default=32, help="bus address width.")
|
||||
parser.add_argument("--debug", action="store_true", help="Enable debug.")
|
||||
|
||||
# UART arguments
|
||||
|
@ -220,7 +222,7 @@ def main():
|
|||
uart_port = args.uart_port
|
||||
uart_baudrate = int(float(args.uart_baudrate))
|
||||
print("[CommUART] port: {} / baudrate: {} / ".format(uart_port, uart_baudrate), end="")
|
||||
comm = CommUART(uart_port, uart_baudrate, debug=args.debug)
|
||||
comm = CommUART(uart_port, uart_baudrate, debug=args.debug, addr_width=int(args.addr_width))
|
||||
|
||||
# JTAG mode
|
||||
elif args.jtag:
|
||||
|
@ -279,7 +281,7 @@ def main():
|
|||
parser.print_help()
|
||||
exit()
|
||||
|
||||
server = RemoteServer(comm, args.bind_ip, int(args.bind_port))
|
||||
server = RemoteServer(comm, args.bind_ip, int(args.bind_port), addr_width=int(args.addr_width))
|
||||
server.open()
|
||||
server.start(4)
|
||||
try:
|
||||
|
|
|
@ -19,11 +19,12 @@ CMD_READ_BURST_FIXED = 0x04
|
|||
# CommUART -----------------------------------------------------------------------------------------
|
||||
|
||||
class CommUART(CSRBuilder):
|
||||
def __init__(self, port, baudrate=115200, csr_csv=None, debug=False):
|
||||
def __init__(self, port, baudrate=115200, csr_csv=None, debug=False, addr_width=32):
|
||||
CSRBuilder.__init__(self, comm=self, csr_csv=csr_csv)
|
||||
self.port = serial.serial_for_url(port, baudrate)
|
||||
self.baudrate = str(baudrate)
|
||||
self.debug = debug
|
||||
self.port = serial.serial_for_url(port, baudrate)
|
||||
self.baudrate = str(baudrate)
|
||||
self.debug = debug
|
||||
self.addr_bytes = addr_width // 8
|
||||
|
||||
def open(self):
|
||||
if hasattr(self, "port"):
|
||||
|
@ -63,7 +64,7 @@ class CommUART(CSRBuilder):
|
|||
"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(self.addr_bytes, byteorder="big")))
|
||||
for i in range(length_int):
|
||||
value = int.from_bytes(self._read(4), "big")
|
||||
if self.debug:
|
||||
|
@ -85,7 +86,7 @@ class CommUART(CSRBuilder):
|
|||
"fixed": CMD_WRITE_BURST_FIXED,
|
||||
}[burst]
|
||||
self._write([cmd, size])
|
||||
self._write(list(((addr//4 + offset).to_bytes(4, byteorder="big"))))
|
||||
self._write(list(((addr//4 + offset).to_bytes(self.addr_bytes, byteorder="big"))))
|
||||
for i, value in enumerate(data[offset:offset+size]):
|
||||
self._write(list(value.to_bytes(4, byteorder="big")))
|
||||
if self.debug:
|
||||
|
|
|
@ -64,7 +64,7 @@ class CSRMemoryRegion:
|
|||
# CSR Builder --------------------------------------------------------------------------------------
|
||||
|
||||
class CSRBuilder:
|
||||
def __init__(self, comm, csr_csv, csr_data_width=None):
|
||||
def __init__(self, comm, csr_csv, csr_data_width=None, csr_bus_address_width=None):
|
||||
if csr_csv is not None:
|
||||
self.items = self.get_csr_items(csr_csv)
|
||||
self.constants = self.build_constants()
|
||||
|
@ -79,7 +79,18 @@ class CSRBuilder:
|
|||
raise KeyError("csr_data_width of {} provided but {} found in constants".format(
|
||||
csr_data_width, constant_csr_data_width))
|
||||
|
||||
self.csr_data_width = csr_data_width
|
||||
# Load csr_data_width from the constants, otherwise it must be provided
|
||||
constant_csr_bus_address_width = self.constants.d.get("config_bus_address_width", None)
|
||||
if csr_bus_address_width is None:
|
||||
csr_bus_address_width = constant_csr_bus_address_width
|
||||
if csr_bus_address_width is None:
|
||||
raise KeyError("csr_bus_address_width not found in constants, please provide!")
|
||||
if csr_bus_address_width != constant_csr_bus_address_width:
|
||||
raise KeyError("csr_bus_address_width of {} provided but {} found in constants".format(
|
||||
csr_bus_address_width, constant_csr_bus_address_width))
|
||||
|
||||
self.csr_data_width = csr_data_width
|
||||
self.csr_bus_address_width = csr_bus_address_width
|
||||
self.bases = self.build_bases()
|
||||
self.regs = self.build_registers(comm.read, comm.write)
|
||||
self.mems = self.build_memories()
|
||||
|
|
|
@ -59,6 +59,8 @@ def get_field_data(field, datas):
|
|||
|
||||
pack_to_uint32 = struct.Struct('>I').pack
|
||||
unpack_uint32_from = struct.Struct('>I').unpack
|
||||
pack_to_uint64 = struct.Struct('>Q').pack
|
||||
unpack_uint64_from = struct.Struct('>Q').unpack
|
||||
|
||||
# Packet -------------------------------------------------------------------------------------------
|
||||
|
||||
|
@ -88,13 +90,15 @@ class EtherboneRead:
|
|||
# Etherbone Writes ---------------------------------------------------------------------------------
|
||||
|
||||
class EtherboneWrites(Packet):
|
||||
def __init__(self, init=[], base_addr=0, datas=[]):
|
||||
def __init__(self, addr_size, init=[], base_addr=0, datas=[]):
|
||||
if isinstance(datas, list) and len(datas) > 255:
|
||||
raise ValueError(f"Burst size of {len(datas)} exceeds maximum of 255 allowed by Etherbone.")
|
||||
assert addr_size in [1, 2, 4, 8]
|
||||
Packet.__init__(self, init)
|
||||
self.base_addr = base_addr
|
||||
self.writes = []
|
||||
self.encoded = init != []
|
||||
self.addr_size = addr_size
|
||||
for data in datas:
|
||||
self.add(EtherboneWrite(data))
|
||||
|
||||
|
@ -111,7 +115,10 @@ class EtherboneWrites(Packet):
|
|||
if self.encoded:
|
||||
raise ValueError
|
||||
ba = bytearray()
|
||||
ba += pack_to_uint32(self.base_addr)
|
||||
if self.addr_size == 4:
|
||||
ba += pack_to_uint32(self.base_addr)
|
||||
else:
|
||||
ba += pack_to_uint64(self.base_addr)
|
||||
for write in self.writes:
|
||||
ba += pack_to_uint32(write.data)
|
||||
self.bytes = ba
|
||||
|
@ -121,9 +128,12 @@ class EtherboneWrites(Packet):
|
|||
if not self.encoded:
|
||||
raise ValueError
|
||||
ba = self.bytes
|
||||
self.base_addr = unpack_uint32_from(ba[:4])[0]
|
||||
if self.addr_size == 4:
|
||||
self.base_addr = unpack_uint32_from(ba[:self.addr_size])[0]
|
||||
else:
|
||||
self.base_addr = unpack_uint64_from(ba[:self.addr_size])[0]
|
||||
writes = []
|
||||
offset = 4
|
||||
offset = self.addr_size
|
||||
length = len(ba)
|
||||
while length > offset:
|
||||
writes.append(EtherboneWrite(unpack_uint32_from(ba[offset:offset+4])[0]))
|
||||
|
@ -142,13 +152,15 @@ class EtherboneWrites(Packet):
|
|||
# Etherbone Reads ----------------------------------------------------------------------------------
|
||||
|
||||
class EtherboneReads(Packet):
|
||||
def __init__(self, init=[], base_ret_addr=0, addrs=[]):
|
||||
def __init__(self, addr_size, init=[], base_ret_addr=0, addrs=[]):
|
||||
if isinstance(addrs, list) and len(addrs) > 255:
|
||||
raise ValueError(f"Burst size of {len(addrs)} exceeds maximum of 255 allowed by Etherbone.")
|
||||
assert addr_size in [1, 2, 4, 8]
|
||||
Packet.__init__(self, init)
|
||||
self.base_ret_addr = base_ret_addr
|
||||
self.reads = []
|
||||
self.encoded = init != []
|
||||
self.reads = []
|
||||
self.encoded = init != []
|
||||
self.addr_size = addr_size
|
||||
for addr in addrs:
|
||||
self.add(EtherboneRead(addr))
|
||||
|
||||
|
@ -165,9 +177,15 @@ class EtherboneReads(Packet):
|
|||
if self.encoded:
|
||||
raise ValueError
|
||||
ba = bytearray()
|
||||
ba += pack_to_uint32(self.base_ret_addr)
|
||||
if (self.addr_size == 4):
|
||||
ba += pack_to_uint32(self.base_ret_addr)
|
||||
else:
|
||||
ba += pack_to_uint64(self.base_ret_addr)
|
||||
for read in self.reads:
|
||||
ba += pack_to_uint32(read.addr)
|
||||
if self.addr_size == 4:
|
||||
ba += pack_to_uint32(read.addr)
|
||||
else:
|
||||
ba += pack_to_uint64(read.addr)
|
||||
self.bytes = ba
|
||||
self.encoded = True
|
||||
|
||||
|
@ -175,13 +193,20 @@ class EtherboneReads(Packet):
|
|||
if not self.encoded:
|
||||
raise ValueError
|
||||
ba = self.bytes
|
||||
base_ret_addr = unpack_uint32_from(ba[:4])[0]
|
||||
if self.addr_size == 4:
|
||||
base_ret_addr = unpack_uint32_from(ba[:self.addr_size])[0]
|
||||
else:
|
||||
base_ret_addr = unpack_uint64_from(ba[:self.addr_size])[0]
|
||||
reads = []
|
||||
offset = 4
|
||||
offset = self.addr_size
|
||||
length = len(ba)
|
||||
while length > offset:
|
||||
reads.append(EtherboneRead(unpack_uint32_from(ba[offset:offset+4])[0]))
|
||||
offset += 4
|
||||
v = ba[offset:offset+self.addr_size]
|
||||
if self.addr_size == 4:
|
||||
reads.append(EtherboneRead(unpack_uint32_from(v)[0]))
|
||||
else:
|
||||
reads.append(EtherboneRead(unpack_uint64_from(v)[0]))
|
||||
offset += self.addr_size
|
||||
self.reads = reads
|
||||
self.encoded = False
|
||||
|
||||
|
@ -196,7 +221,9 @@ class EtherboneReads(Packet):
|
|||
# Etherbone Record ---------------------------------------------------------------------------------
|
||||
|
||||
class EtherboneRecord(Packet):
|
||||
def __init__(self, init=[]):
|
||||
def __init__(self, addr_size, init=[]):
|
||||
assert addr_size in [1, 2, 4, 8]
|
||||
|
||||
Packet.__init__(self, init)
|
||||
self.writes = None
|
||||
self.reads = None
|
||||
|
@ -210,6 +237,7 @@ class EtherboneRecord(Packet):
|
|||
self.wcount = 0
|
||||
self.rcount = 0
|
||||
self.encoded = init != []
|
||||
self.addr_size = addr_size
|
||||
|
||||
def decode(self):
|
||||
if not self.encoded:
|
||||
|
@ -223,14 +251,20 @@ class EtherboneRecord(Packet):
|
|||
|
||||
# Decode writes
|
||||
if self.wcount:
|
||||
self.writes = EtherboneWrites(self.bytes[offset:offset + 4*(self.wcount+1)])
|
||||
offset += 4*(self.wcount+1)
|
||||
init_length = (4 * self.wcount) + (self.addr_size)
|
||||
self.writes = EtherboneWrites(
|
||||
addr_size = self.addr_size,
|
||||
init = self.bytes[offset:offset + init_length])
|
||||
offset += init_length
|
||||
self.writes.decode()
|
||||
|
||||
# Decode reads
|
||||
if self.rcount:
|
||||
self.reads = EtherboneReads(self.bytes[offset:offset + 4*(self.rcount+1)])
|
||||
offset += 4*(self.rcount+1)
|
||||
init_length = (self.rcount + 1) * self.addr_size
|
||||
self.reads = EtherboneReads(
|
||||
addr_size = self.addr_size,
|
||||
init = self.bytes[offset:offset + init_length])
|
||||
offset += init_length
|
||||
self.reads.decode()
|
||||
|
||||
self.encoded = False
|
||||
|
@ -283,15 +317,17 @@ class EtherboneRecord(Packet):
|
|||
# Etherbone Packet ---------------------------------------------------------------------------------
|
||||
|
||||
class EtherbonePacket(Packet):
|
||||
def __init__(self, init=[]):
|
||||
def __init__(self, addr_width, init=[]):
|
||||
assert addr_width in [8, 16, 32, 64]
|
||||
|
||||
Packet.__init__(self, init)
|
||||
self.encoded = init != []
|
||||
self.records = []
|
||||
|
||||
self.magic = etherbone_magic
|
||||
self.version = etherbone_version
|
||||
self.addr_size = 32//8
|
||||
self.port_size = 32//8
|
||||
self.addr_size = addr_width//8
|
||||
self.port_size = 4 # FIXME: use data_size
|
||||
self.nr = 0
|
||||
self.pr = 0
|
||||
self.pf = 0
|
||||
|
@ -311,14 +347,14 @@ class EtherbonePacket(Packet):
|
|||
# Decode records
|
||||
length = len(ba)
|
||||
while length > offset:
|
||||
record = EtherboneRecord(ba[offset:])
|
||||
record = EtherboneRecord(addr_size=self.addr_size, init=ba[offset:])
|
||||
record.decode()
|
||||
self.records.append(record)
|
||||
offset += etherbone_record_header.length
|
||||
if record.wcount:
|
||||
offset += 4*(record.wcount + 1)
|
||||
offset += (4*record.wcount) + self.addr_size
|
||||
if record.rcount:
|
||||
offset += 4*(record.rcount + 1)
|
||||
offset += (record.rcount + 1) * self.addr_size
|
||||
|
||||
self.encoded = False
|
||||
|
||||
|
@ -362,7 +398,8 @@ class EtherboneIPC:
|
|||
def send_packet(self, socket, packet):
|
||||
socket.sendall(packet.bytes)
|
||||
|
||||
def receive_packet(self, socket):
|
||||
def receive_packet(self, socket, addr_size):
|
||||
assert addr_size in [1, 2, 4, 8]
|
||||
header_length = etherbone_packet_header_length + etherbone_record_header_length
|
||||
packet = bytes()
|
||||
while len(packet) < header_length:
|
||||
|
@ -373,7 +410,11 @@ class EtherboneIPC:
|
|||
packet += chunk
|
||||
wcount, rcount = struct.unpack(">BB", packet[header_length-2:])
|
||||
counts = wcount + rcount
|
||||
packet_size = header_length + 4*(counts + 1)
|
||||
packet_size = header_length
|
||||
if wcount != 0:
|
||||
packet_size += 4 * (wcount ) + addr_size
|
||||
if rcount != 0:
|
||||
packet_size += (rcount + 1 ) * addr_size
|
||||
while len(packet) < packet_size:
|
||||
chunk = socket.recv(packet_size - len(packet))
|
||||
if len(chunk) == 0:
|
||||
|
|
Loading…
Reference in a new issue