From: Florent Kermarrec Date: Sat, 20 Apr 2019 08:44:53 +0000 (+0200) Subject: tools: move from litex.soc.tools to litex.tools and fix usb.core import X-Git-Tag: 24jan2021_ls180~1318 X-Git-Url: https://git.libre-soc.org/?a=commitdiff_plain;h=9ee6c35b42be6eadcf9fdd3c4497e1523900f802;p=litex.git tools: move from litex.soc.tools to litex.tools and fix usb.core import --- diff --git a/litex/__init__.py b/litex/__init__.py index c489b99c..19eec2b3 100644 --- a/litex/__init__.py +++ b/litex/__init__.py @@ -1 +1 @@ -from litex.soc.tools.remote import RemoteClient +from litex.tools.litex_client import RemoteClient \ No newline at end of file diff --git a/litex/soc/software/bios/Makefile b/litex/soc/software/bios/Makefile index 534c09db..a454a17e 100755 --- a/litex/soc/software/bios/Makefile +++ b/litex/soc/software/bios/Makefile @@ -16,9 +16,9 @@ ifneq ($(OS),Windows_NT) chmod -x $@ endif ifeq ($(CPUENDIANNESS),little) - $(PYTHON) -m litex.soc.tools.mkmscimg $@ --little + $(PYTHON) -m litex.soc.software.mkmscimg $@ --little else - $(PYTHON) -m litex.soc.tools.mkmscimg $@ + $(PYTHON) -m litex.soc.software.mkmscimg $@ endif bios.elf: $(BIOS_DIRECTORY)/linker.ld $(OBJECTS) diff --git a/litex/soc/software/mkmscimg.py b/litex/soc/software/mkmscimg.py new file mode 100644 index 00000000..54f27e1d --- /dev/null +++ b/litex/soc/software/mkmscimg.py @@ -0,0 +1,39 @@ +#!/usr/bin/env python3 + +import argparse +import binascii + + +def insert_crc(i_filename, fbi_mode=False, o_filename=None, little_endian=False): + endian = "little" if little_endian else "big" + + if o_filename is None: + o_filename = i_filename + + with open(i_filename, "rb") as f: + fdata = f.read() + fcrc = binascii.crc32(fdata).to_bytes(4, byteorder=endian) + flength = len(fdata).to_bytes(4, byteorder=endian) + + with open(o_filename, "wb") as f: + if fbi_mode: + f.write(flength) + f.write(fcrc) + f.write(fdata) + else: + f.write(fdata) + f.write(fcrc) + + +def main(): + parser = argparse.ArgumentParser(description="CRC32 computation tool and MiSoC image file writer.") + parser.add_argument("input", help="input file") + parser.add_argument("-o", "--output", default=None, help="output file (if not specified, use input file)") + parser.add_argument("-f", "--fbi", default=False, action="store_true", help="build flash boot image (FBI) file") + parser.add_argument("-l", "--little", default=False, action="store_true", help="Use little endian to write the CRC32") + args = parser.parse_args() + insert_crc(args.input, args.fbi, args.output, args.little) + + +if __name__ == "__main__": + main() diff --git a/litex/soc/tools/__init__.py b/litex/soc/tools/__init__.py deleted file mode 100644 index e69de29b..00000000 diff --git a/litex/soc/tools/mkmscimg.py b/litex/soc/tools/mkmscimg.py deleted file mode 100644 index 54f27e1d..00000000 --- a/litex/soc/tools/mkmscimg.py +++ /dev/null @@ -1,39 +0,0 @@ -#!/usr/bin/env python3 - -import argparse -import binascii - - -def insert_crc(i_filename, fbi_mode=False, o_filename=None, little_endian=False): - endian = "little" if little_endian else "big" - - if o_filename is None: - o_filename = i_filename - - with open(i_filename, "rb") as f: - fdata = f.read() - fcrc = binascii.crc32(fdata).to_bytes(4, byteorder=endian) - flength = len(fdata).to_bytes(4, byteorder=endian) - - with open(o_filename, "wb") as f: - if fbi_mode: - f.write(flength) - f.write(fcrc) - f.write(fdata) - else: - f.write(fdata) - f.write(fcrc) - - -def main(): - parser = argparse.ArgumentParser(description="CRC32 computation tool and MiSoC image file writer.") - parser.add_argument("input", help="input file") - parser.add_argument("-o", "--output", default=None, help="output file (if not specified, use input file)") - parser.add_argument("-f", "--fbi", default=False, action="store_true", help="build flash boot image (FBI) file") - parser.add_argument("-l", "--little", default=False, action="store_true", help="Use little endian to write the CRC32") - args = parser.parse_args() - insert_crc(args.input, args.fbi, args.output, args.little) - - -if __name__ == "__main__": - main() diff --git a/litex/soc/tools/remote/__init__.py b/litex/soc/tools/remote/__init__.py deleted file mode 100644 index d61f3888..00000000 --- a/litex/soc/tools/remote/__init__.py +++ /dev/null @@ -1,5 +0,0 @@ -from litex.soc.tools.remote.comm_uart import CommUART -from litex.soc.tools.remote.comm_udp import CommUDP -from litex.soc.tools.remote.comm_pcie import CommPCIe -from litex.soc.tools.remote.comm_usb import CommUSB -from litex.soc.tools.remote.litex_client import RemoteClient diff --git a/litex/soc/tools/remote/comm_pcie.py b/litex/soc/tools/remote/comm_pcie.py deleted file mode 100644 index 3cfb0efb..00000000 --- a/litex/soc/tools/remote/comm_pcie.py +++ /dev/null @@ -1,43 +0,0 @@ -import mmap - - -class CommPCIe: - def __init__(self, bar, debug=False): - self.bar = bar - self.debug = debug - - def open(self): - if hasattr(self, "sysfs"): - return - self.sysfs = open(self.bar, "r+b") - self.sysfs.flush() - self.mmap = mmap.mmap(self.sysfs.fileno(), 0) - - def close(self): - if not hasattr(self, "sysfs"): - return - self.mmap.close() - del self.mmap - self.sysfs.close() - del self.sysfs - - def read(self, addr, length=None): - data = [] - length_int = 1 if length is None else length - for i in range(length_int): - self.mmap.seek(addr + 4*i) - value = int.from_bytes(self.mmap.read(4), byteorder="little") - if self.debug: - print("read {:08x} @ {:08x}".format(value, addr + 4*i)) - if length is None: - return value - data.append(value) - return data - - def write(self, addr, data): - data = data if isinstance(data, list) else [data] - length = len(data) - for i, value in enumerate(data): - self.mmap[addr + 4*i:addr + 4*(i + 1)] = value.to_bytes(4, byteorder="little") - if self.debug: - print("write {:08x} @ {:08x}".format(value, addr + 4*i)) diff --git a/litex/soc/tools/remote/comm_uart.py b/litex/soc/tools/remote/comm_uart.py deleted file mode 100644 index 0a056ec7..00000000 --- a/litex/soc/tools/remote/comm_uart.py +++ /dev/null @@ -1,74 +0,0 @@ -import serial -import struct - - -class CommUART: - msg_type = { - "write": 0x01, - "read": 0x02 - } - def __init__(self, port, baudrate=115200, debug=False): - self.port = port - self.baudrate = str(baudrate) - self.debug = debug - self.port = serial.serial_for_url(port, baudrate) - - def open(self): - if hasattr(self, "port"): - return - self.port.open() - - def close(self): - if not hasattr(self, "port"): - return - self.port.close() - del self.port - - def _read(self, length): - r = bytes() - while len(r) < length: - r += self.port.read(length - len(r)) - return r - - def _write(self, data): - remaining = len(data) - pos = 0 - while remaining: - written = self.port.write(data[pos:]) - remaining -= written - pos += written - - def _flush(self): - if self.port.inWaiting() > 0: - self.port.read(self.port.inWaiting()) - - def read(self, addr, length=None): - self._flush() - data = [] - length_int = 1 if length is None else length - self._write([self.msg_type["read"], 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") - if self.debug: - print("read {:08x} @ {:08x}".format(value, addr + 4*i)) - if length is None: - return value - data.append(value) - return data - - def write(self, addr, data): - 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]) - 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"))) - if self.debug: - print("write {:08x} @ {:08x}".format(value, addr + offset, 4*i)) - offset += size - length -= size diff --git a/litex/soc/tools/remote/comm_udp.py b/litex/soc/tools/remote/comm_udp.py deleted file mode 100644 index 82530c45..00000000 --- a/litex/soc/tools/remote/comm_udp.py +++ /dev/null @@ -1,62 +0,0 @@ -import socket - -from litex.soc.tools.remote.etherbone import EtherbonePacket, EtherboneRecord -from litex.soc.tools.remote.etherbone import EtherboneReads, EtherboneWrites - - -class CommUDP: - def __init__(self, server="192.168.1.50", port=1234, debug=False): - self.server = server - self.port = port - self.debug = debug - - def open(self): - if hasattr(self, "tx_socket"): - return - self.tx_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) - self.rx_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) - self.rx_socket.bind(("", self.port)) - - def close(self): - if not hasattr(self, "tx_socket"): - return - self.tx_socket.close() - del self.tx_socket - self.rx_socket.close() - del self.rx_socket - - def read(self, addr, length=None): - length_int = 1 if length is None else length - record = EtherboneRecord() - record.reads = EtherboneReads(addrs=[addr+4*j for j in range(length_int)]) - record.rcount = len(record.reads) - - packet = EtherbonePacket() - packet.records = [record] - packet.encode() - self.tx_socket.sendto(bytes(packet), (self.server, self.port)) - - datas, dummy = self.rx_socket.recvfrom(8192) - packet = EtherbonePacket(datas) - packet.decode() - datas = packet.records.pop().writes.get_datas() - if self.debug: - for i, value in enumerate(datas): - print("read {:08x} @ {:08x}".format(value, addr + 4*i)) - return datas[0] if length is None else datas - - def write(self, addr, datas): - datas = datas if isinstance(datas, list) else [datas] - length = len(datas) - record = EtherboneRecord() - record.writes = EtherboneWrites(base_addr=addr, datas=iter(datas)) - record.wcount = len(record.writes) - - packet = EtherbonePacket() - packet.records = [record] - packet.encode() - self.tx_socket.sendto(bytes(packet), (self.server, self.port)) - - if self.debug: - for i, value in enumerate(datas): - print("write {:08x} @ {:08x}".format(value, addr + 4*i)) diff --git a/litex/soc/tools/remote/comm_usb.py b/litex/soc/tools/remote/comm_usb.py deleted file mode 100644 index 1e4ea1e4..00000000 --- a/litex/soc/tools/remote/comm_usb.py +++ /dev/null @@ -1,142 +0,0 @@ -import usb.core -import time - -# Wishbone USB Protocol Bridge -# ============================ -# -# This module implements a bridge to connect LiteX to the target system's -# Wishbone bus via USB. It uses `vendor` packets to communicate, which are -# normally reserved. Since we're the vendors of this USB protocol, we take -# advantage of this packet type to implement the bridge. -# -# All traffic goes to/from USB EP0, which is guaranteed to exist regardless -# of the user's device implementation. The 8th bit of the first SETUP DATA -# transaction contains all the information we need to mark this as a -# Wishbone packet. -# -# Packets going to EP0 always start with a SETUP packet, followed by an IN -# or an OUT packet, followed by an OUT or an IN packet. -# -# The SETUP packet looks like this: -# -# +----+----+----------+----+----+ -# | C0 | 00 | ADDRESS | 04 | 00 | read packet -# +----+----+----------+----+----+ -# 1 1 4 1 1 -# -# +----+----+----------+----+----+ -# | 40 | 00 | ADDRESS | 04 | 00 | write packet -# +----+----+----------+----+----+ -# 1 1 4 1 1 -# -# If the transaction is a "read" transaction, the device responds with an OUT -# packet with the data. If the transaction is a "write" transaction, the host -# responds with an IN packet with the data. -# -# Much like other Wishbone bridges, there are two types of packets. The first -# byte indicates what type of packet it is, and that it is a Wishbone Bridge -# packet. This is the value "0x40" (VENDOR type packet destined for DEVICE) -# with the "Data Phase Transfer" bit either set or cleared: -# - Read: 0xc0 -# - Write: 0x40 -# -# The next byte is bRequest, which in the current implementation is unused. -# Set this value to 0. -# -# The next four bytes form the wValue and wIndex values of the SETUP token. -# We reuse these two 16-bit values as a single 32-bit ADDRESS packet. Note that -# USB is big endian. -# -# Finally, the last two bytes indicate the length of the transaction. Since -# we only support 32-bit reads and writes, this is always 4. On big endian -# USB, this has the value {04, 00}. - -class CommUSB: - def __init__(self, vid=None, pid=None, max_retries=10, debug=False): - self.vid = vid - self.pid = pid - self.debug = debug - self.max_retries = max_retries - self.MAX_RECURSION_COUNT = 5 - - def open(self): - if hasattr(self, "dev"): - return - for t in range(self.max_retries): - args = {} - if self.vid is not None: - args['idVendor'] = self.vid - if self.pid is not None: - args['idProduct'] = self.pid - self.dev = usb.core.find(**args) - if self.dev is not None: - if self.debug: - print("device connected after {} tries".format(t+1)) - return True - del self.dev - time.sleep(0.2 * t) - print("unable to find usb device after {} tries".format(self.max_retries)) - return False - - - def close(self): - if not hasattr(self, "dev"): - return - del self.dev - - def read(self, addr, length=None): - data = [] - length_int = 1 if length is None else length - for i in range(length_int): - value = self.usb_read(addr) - # Note that sometimes, the value ends up as None when the device - # disconnects during a transaction. Paper over this fact by - # replacing it with a sentinal. - if value is None: - value = 0xffffffff - if self.debug: - print("read {:08x} @ {:08x}".format(value, addr)) - if length is None: - return value - data.append(value) - return data - - def usb_read(self, addr, depth=0): - try: - value = self.dev.ctrl_transfer(bmRequestType=0xc0, - bRequest=0x00, - wValue=addr & 0xffff, - wIndex=(addr >> 16) & 0xffff, - data_or_wLength=4) - if value is None: - raise TypeError - return int.from_bytes(value, byteorder="little") - except (usb.core.USBError, TypeError): - self.close() - self.open() - if depth < self.MAX_RECURSION_COUNT: - return self.usb_read(addr, depth+1) - - def write(self, addr, data): - data = data if isinstance(data, list) else [data] - length = len(data) - for i, value in enumerate(data): - self.usb_write(addr, value) - if self.debug: - print("write {:08x} @ {:08x}".format(value, addr + 4*i)) - - def usb_write(self, addr, value, depth=0): - try: - self.dev.ctrl_transfer(bmRequestType=0x40, bRequest=0x00, - wValue=addr & 0xffff, - wIndex=(addr >> 16) & 0xffff, - data_or_wLength=bytes([(value >> 0) & 0xff, - (value >> 8) & 0xff, - (value >> 16) & 0xff, - (value >> 24) & 0xff] - ), timeout=None) - except usb.core.USBError: - self.close() - self.open() - if depth < self.MAX_RECURSION_COUNT: - return self.usb_write(addr, value, depth+1) diff --git a/litex/soc/tools/remote/csr_builder.py b/litex/soc/tools/remote/csr_builder.py deleted file mode 100644 index 1bbb54a7..00000000 --- a/litex/soc/tools/remote/csr_builder.py +++ /dev/null @@ -1,116 +0,0 @@ -import csv - - -class CSRElements: - def __init__(self, d): - self.__dict__.update(d) - - @property - def d(self): - return self.__dict__ - - def __getattr__(self, attr): - try: - return self.__dict__[attr] - except KeyError: - pass - raise AttributeError("No such element " + attr) - - -class CSRRegister: - def __init__(self, readfn, writefn, name, addr, length, data_width, mode): - self.readfn = readfn - self.writefn = writefn - self.name = name - self.addr = addr - self.length = length - self.data_width = data_width - self.mode = mode - - def read(self): - if self.mode not in ["rw", "ro"]: - raise KeyError(self.name + "register not readable") - datas = self.readfn(self.addr, length=self.length) - if isinstance(datas, int): - return datas - else: - data = 0 - for i in range(self.length): - data = data << self.data_width - data |= datas[i] - return data - - def write(self, value): - if self.mode not in ["rw", "wo"]: - raise KeyError(self.name + "register not writable") - datas = [] - for i in range(self.length): - datas.append((value >> ((self.length-1-i)*self.data_width)) & (2**self.data_width-1)) - self.writefn(self.addr, datas) - - -class CSRMemoryRegion: - def __init__(self, base, size): - self.base = base - self.size = size - - -class CSRBuilder: - def __init__(self, comm, csr_csv, csr_data_width=None): - self.constants = self.build_constants(csr_csv) - - # Load csr_data_width from the constants, otherwise it must be provided - constant_csr_data_width = self.constants.d.get('csr_data_width', None) - if csr_data_width is None: - csr_data_width = constant_csr_data_width - if csr_data_width is None: - raise KeyError('csr_data_width not found in constants, please provide!') - if csr_data_width != constant_csr_data_width: - 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 - self.bases = self.build_bases(csr_csv) - self.regs = self.build_registers(csr_csv, comm.read, comm.write) - self.mems = self.build_memories(csr_csv) - - def build_bases(self, csr_csv): - csv_reader = csv.reader(open(csr_csv), delimiter=',', quotechar='#') - d = {} - for item in csv_reader: - group, name, addr, dummy0, dummy1 = item - if group == "csr_base": - d[name] = int(addr.replace("0x", ""), 16) - return CSRElements(d) - - def build_registers(self, csr_csv, readfn, writefn): - csv_reader = csv.reader(open(csr_csv), delimiter=',', quotechar='#') - d = {} - for item in csv_reader: - group, name, addr, length, mode = item - if group == "csr_register": - addr = int(addr.replace("0x", ""), 16) - length = int(length) - d[name] = CSRRegister(readfn, writefn, name, addr, length, self.csr_data_width, mode) - return CSRElements(d) - - def build_constants(self, csr_csv): - csv_reader = csv.reader(open(csr_csv), delimiter=',', quotechar='#') - d = {} - for item in csv_reader: - group, name, value, dummy0, dummy1 = item - if group == "constant": - try: - d[name] = int(value) - except: - d[name] = value - return CSRElements(d) - - def build_memories(self, csr_csv): - csv_reader = csv.reader(open(csr_csv), delimiter=',', quotechar='#') - d = {} - for item in csv_reader: - group, name, base, size, dummy1 = item - if group == "memory_region": - d[name] = CSRMemoryRegion(int(base, 16), int(size)) - return CSRElements(d) diff --git a/litex/soc/tools/remote/etherbone.py b/litex/soc/tools/remote/etherbone.py deleted file mode 100644 index 068f65a0..00000000 --- a/litex/soc/tools/remote/etherbone.py +++ /dev/null @@ -1,375 +0,0 @@ -import math -import struct - -from litex.soc.interconnect.stream_packet import HeaderField, Header - - -etherbone_magic = 0x4e6f -etherbone_version = 1 -etherbone_packet_header_length = 8 -etherbone_packet_header_fields = { - "magic": HeaderField(0, 0, 16), - - "version": HeaderField(2, 4, 4), - "nr": HeaderField(2, 2, 1), # No Reads - "pr": HeaderField(2, 1, 1), # Probe Reply - "pf": HeaderField(2, 0, 1), # Probe Flag - - "addr_size": HeaderField(3, 4, 4), # 1=8bits, 2=16bits, 4=32bits, 8=64bits - "port_size": HeaderField(3, 0, 4), # Same as above -} -etherbone_packet_header = Header(etherbone_packet_header_fields, - etherbone_packet_header_length, - swap_field_bytes=True) - -# When reading/writing to a FIFO, you don't increase -# the address after each write. -etherbone_record_header_length = 4 -etherbone_record_header_fields = { - "bca": HeaderField(0, 0, 1), # ReplyToCfgSpace - ??? (C)onfig (A)dress - "rca": HeaderField(0, 1, 1), # ReadFromCfgSpace - (R)ead from (C)onfig (A)dress - "rff": HeaderField(0, 2, 1), # ReadFIFO - (R)ead (F)I(F)O - "cyc": HeaderField(0, 4, 1), # DropCycle - Drop(Cyc)le - "wca": HeaderField(0, 5, 1), # WriteToCfgSpace - (W)rite to (C)onfig (A)dress - "wff": HeaderField(0, 6, 1), # WriteFIFO - (W)rite (F)I(F)O - - "byte_enable": HeaderField(1, 0, 8), # Select - - "wcount": HeaderField(2, 0, 8), # Writes - - "rcount": HeaderField(3, 0, 8), # Reads -} -etherbone_record_header = Header(etherbone_record_header_fields, - etherbone_record_header_length, - swap_field_bytes=True) - - -def split_bytes(v, n, endianness="big"): - r = [] - return v.to_bytes(n, byteorder=endianness) - - -def merge_bytes(b, endianness="big"): - return int.from_bytes(b, endianness) - - -def get_field_data(field, datas): - v = merge_bytes(datas[field.byte:field.byte+math.ceil(field.width/8)]) - return (v >> field.offset) & (2**field.width-1) - - -class Packet(list): - def __init__(self, init=[]): - self.ongoing = False - self.done = False - for data in init: - self.append(data) - - -class EtherboneWrite: - def __init__(self, data): - self.data = data - - def __repr__(self): - return "WR32 0x{:08x}".format(self.data) - - -class EtherboneRead: - def __init__(self, addr): - self.addr = addr - - def __repr__(self): - return "RD32 @ 0x{:08x}".format(self.addr) - - -class EtherboneWrites(Packet): - def __init__(self, init=[], base_addr=0, datas=[]): - Packet.__init__(self, init) - self.base_addr = base_addr - self.writes = [] - self.encoded = init != [] - for data in datas: - self.add(EtherboneWrite(data)) - - def add(self, write): - self.writes.append(write) - - def get_datas(self): - datas = [] - for write in self.writes: - datas.append(write.data) - return datas - - def encode(self): - if self.encoded: - raise ValueError - for byte in split_bytes(self.base_addr, 4): - self.append(byte) - for write in self.writes: - for byte in split_bytes(write.data, 4): - self.append(byte) - self.encoded = True - - def decode(self): - if not self.encoded: - raise ValueError - base_addr = [] - for i in range(4): - base_addr.append(self.pop(0)) - self.base_addr = merge_bytes(base_addr) - self.writes = [] - while len(self) != 0: - write = [] - for i in range(4): - write.append(self.pop(0)) - self.writes.append(EtherboneWrite(merge_bytes(write))) - self.encoded = False - - def __repr__(self): - r = "Writes\n" - r += "--------\n" - r += "BaseAddr @ 0x{:08x}\n".format(self.base_addr) - for write in self.writes: - r += write.__repr__() + "\n" - return r - - -class EtherboneReads(Packet): - def __init__(self, init=[], base_ret_addr=0, addrs=[]): - Packet.__init__(self, init) - self.base_ret_addr = base_ret_addr - self.reads = [] - self.encoded = init != [] - for addr in addrs: - self.add(EtherboneRead(addr)) - - def add(self, read): - self.reads.append(read) - - def get_addrs(self): - addrs = [] - for read in self.reads: - addrs.append(read.addr) - return addrs - - def encode(self): - if self.encoded: - raise ValueError - for byte in split_bytes(self.base_ret_addr, 4): - self.append(byte) - for read in self.reads: - for byte in split_bytes(read.addr, 4): - self.append(byte) - self.encoded = True - - def decode(self): - if not self.encoded: - raise ValueError - base_ret_addr = [] - for i in range(4): - base_ret_addr.append(self.pop(0)) - self.base_ret_addr = merge_bytes(base_ret_addr) - self.reads = [] - while len(self) != 0: - read = [] - for i in range(4): - read.append(self.pop(0)) - self.reads.append(EtherboneRead(merge_bytes(read))) - self.encoded = False - - def __repr__(self): - r = "Reads\n" - r += "--------\n" - r += "BaseRetAddr @ 0x{:08x}\n".format(self.base_ret_addr) - for read in self.reads: - r += read.__repr__() + "\n" - return r - - -class EtherboneRecord(Packet): - def __init__(self, init=[]): - Packet.__init__(self, init) - self.writes = None - self.reads = None - self.bca = 0 - self.rca = 0 - self.rff = 0 - self.cyc = 0 - self.wca = 0 - self.wff = 0 - self.byte_enable = 0xf - self.wcount = 0 - self.rcount = 0 - self.encoded = init != [] - - - def get_writes(self): - if self.wcount == 0: - return None - else: - writes = [] - for i in range((self.wcount+1)*4): - writes.append(self.pop(0)) - return EtherboneWrites(writes) - - def get_reads(self): - if self.rcount == 0: - return None - else: - reads = [] - for i in range((self.rcount+1)*4): - reads.append(self.pop(0)) - return EtherboneReads(reads) - - def decode(self): - if not self.encoded: - raise ValueError - header = [] - for byte in self[:etherbone_record_header.length]: - header.append(self.pop(0)) - for k, v in sorted(etherbone_record_header.fields.items()): - setattr(self, k, get_field_data(v, header)) - self.writes = self.get_writes() - if self.writes is not None: - self.writes.decode() - self.reads = self.get_reads() - if self.reads is not None: - self.reads.decode() - self.encoded = False - - def set_writes(self, writes): - self.wcount = len(writes.writes) - writes.encode() - for byte in writes: - self.append(byte) - - def set_reads(self, reads): - self.rcount = len(reads.reads) - reads.encode() - for byte in reads: - self.append(byte) - - def encode(self): - if self.encoded: - raise ValueError - if self.writes is not None: - self.set_writes(self.writes) - if self.reads is not None: - self.set_reads(self.reads) - header = 0 - for k, v in sorted(etherbone_record_header.fields.items()): - value = merge_bytes(split_bytes(getattr(self, k), - math.ceil(v.width/8)), - "little") - header += (value << v.offset+(v.byte*8)) - for d in split_bytes(header, etherbone_record_header.length): - self.insert(0, d) - self.encoded = True - - def __repr__(self, n=0): - r = "Record {}\n".format(n) - r += "--------\n" - if self.encoded: - for d in self: - r += "{:02x}".format(d) - else: - for k in sorted(etherbone_record_header.fields.keys()): - r += k + " : 0x{:0x}\n".format(getattr(self, k)) - if self.wcount != 0: - r += self.writes.__repr__() - if self.rcount != 0: - r += self.reads.__repr__() - return r - - -class EtherbonePacket(Packet): - def __init__(self, init=[]): - 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.nr = 0 - self.pr = 0 - self.pf = 0 - - def get_records(self): - records = [] - done = False - payload = self - while len(payload) != 0: - record = EtherboneRecord(payload) - record.decode() - records.append(record) - payload = record[:] - return records - - def decode(self): - if not self.encoded: - raise ValueError - header = [] - for byte in self[:etherbone_packet_header.length]: - header.append(self.pop(0)) - for k, v in sorted(etherbone_packet_header.fields.items()): - setattr(self, k, get_field_data(v, header)) - self.records = self.get_records() - self.encoded = False - - def set_records(self, records): - for record in records: - record.encode() - for byte in record: - self.append(byte) - - def encode(self): - if self.encoded: - raise ValueError - self.set_records(self.records) - header = 0 - for k, v in sorted(etherbone_packet_header.fields.items()): - value = merge_bytes(split_bytes(getattr(self, k), math.ceil(v.width/8)), "little") - header += (value << v.offset+(v.byte*8)) - for d in split_bytes(header, etherbone_packet_header.length): - self.insert(0, d) - self.encoded = True - - def __repr__(self): - r = "Packet\n" - r += "--------\n" - if self.encoded: - for d in self: - r += "{:02x}".format(d) - else: - for k in sorted(etherbone_packet_header.fields.keys()): - r += k + " : 0x{:0x}\n".format(getattr(self, k)) - for i, record in enumerate(self.records): - r += record.__repr__(i) - return r - - -class EtherboneIPC: - def send_packet(self, socket, packet): - socket.sendall(bytes(packet)) - - def receive_packet(self, socket): - header_length = etherbone_packet_header_length + etherbone_record_header_length - packet = bytes() - while len(packet) < header_length: - chunk = socket.recv(header_length - len(packet)) - if len(chunk) == 0: - return 0 - else: - packet += chunk - wcount, rcount = struct.unpack(">BB", packet[header_length-2:]) - counts = wcount + rcount - packet_size = header_length + 4*(counts + 1) - while len(packet) < packet_size: - chunk = socket.recv(packet_size - len(packet)) - if len(chunk) == 0: - return 0 - else: - packet += chunk - return packet diff --git a/litex/soc/tools/remote/litex_client.py b/litex/soc/tools/remote/litex_client.py deleted file mode 100644 index bddbbe7f..00000000 --- a/litex/soc/tools/remote/litex_client.py +++ /dev/null @@ -1,66 +0,0 @@ -import socket - -from litex.soc.tools.remote.etherbone import EtherbonePacket, EtherboneRecord -from litex.soc.tools.remote.etherbone import EtherboneReads, EtherboneWrites -from litex.soc.tools.remote.etherbone import EtherboneIPC -from litex.soc.tools.remote.csr_builder import CSRBuilder - - -class RemoteClient(EtherboneIPC, CSRBuilder): - def __init__(self, host="localhost", port=1234, csr_csv="csr.csv", csr_data_width=None, debug=False): - if csr_csv is not None: - CSRBuilder.__init__(self, self, csr_csv, csr_data_width) - else: - assert csr_data_width is not None - self.host = host - self.port = port - self.debug = debug - - def open(self): - if hasattr(self, "socket"): - return - self.socket = socket.create_connection((self.host, self.port), 5.0) - self.socket.settimeout(5.0) - - def close(self): - if not hasattr(self, "socket"): - return - self.socket.close() - del self.socket - - def read(self, addr, length=None): - length_int = 1 if length is None else length - # prepare packet - record = EtherboneRecord() - record.reads = EtherboneReads(addrs=[addr + 4*j for j in range(length_int)]) - record.rcount = len(record.reads) - - # send packet - packet = EtherbonePacket() - packet.records = [record] - packet.encode() - self.send_packet(self.socket, packet[:]) - - # receive response - packet = EtherbonePacket(self.receive_packet(self.socket)) - packet.decode() - datas = packet.records.pop().writes.get_datas() - if self.debug: - for i, data in enumerate(datas): - print("read {:08x} @ {:08x}".format(data, addr + 4*i)) - return datas[0] if length is None else datas - - def write(self, addr, datas): - datas = datas if isinstance(datas, list) else [datas] - record = EtherboneRecord() - record.writes = EtherboneWrites(base_addr=addr, datas=[d for d in datas]) - record.wcount = len(record.writes) - - packet = EtherbonePacket() - packet.records = [record] - packet.encode() - self.send_packet(self.socket, packet) - - if self.debug: - for i, data in enumerate(datas): - print("write {:08x} @ {:08x}".format(data, addr + 4*i)) diff --git a/litex/tools/__init__.py b/litex/tools/__init__.py new file mode 100644 index 00000000..8b137891 --- /dev/null +++ b/litex/tools/__init__.py @@ -0,0 +1 @@ + diff --git a/litex/tools/litex_client.py b/litex/tools/litex_client.py new file mode 100644 index 00000000..c9e0d43e --- /dev/null +++ b/litex/tools/litex_client.py @@ -0,0 +1,66 @@ +import socket + +from litex.tools.remote.etherbone import EtherbonePacket, EtherboneRecord +from litex.tools.remote.etherbone import EtherboneReads, EtherboneWrites +from litex.tools.remote.etherbone import EtherboneIPC +from litex.tools.remote.csr_builder import CSRBuilder + + +class RemoteClient(EtherboneIPC, CSRBuilder): + def __init__(self, host="localhost", port=1234, csr_csv="csr.csv", csr_data_width=None, debug=False): + if csr_csv is not None: + CSRBuilder.__init__(self, self, csr_csv, csr_data_width) + else: + assert csr_data_width is not None + self.host = host + self.port = port + self.debug = debug + + def open(self): + if hasattr(self, "socket"): + return + self.socket = socket.create_connection((self.host, self.port), 5.0) + self.socket.settimeout(5.0) + + def close(self): + if not hasattr(self, "socket"): + return + self.socket.close() + del self.socket + + def read(self, addr, length=None): + length_int = 1 if length is None else length + # prepare packet + record = EtherboneRecord() + record.reads = EtherboneReads(addrs=[addr + 4*j for j in range(length_int)]) + record.rcount = len(record.reads) + + # send packet + packet = EtherbonePacket() + packet.records = [record] + packet.encode() + self.send_packet(self.socket, packet[:]) + + # receive response + packet = EtherbonePacket(self.receive_packet(self.socket)) + packet.decode() + datas = packet.records.pop().writes.get_datas() + if self.debug: + for i, data in enumerate(datas): + print("read {:08x} @ {:08x}".format(data, addr + 4*i)) + return datas[0] if length is None else datas + + def write(self, addr, datas): + datas = datas if isinstance(datas, list) else [datas] + record = EtherboneRecord() + record.writes = EtherboneWrites(base_addr=addr, datas=[d for d in datas]) + record.wcount = len(record.writes) + + packet = EtherbonePacket() + packet.records = [record] + packet.encode() + self.send_packet(self.socket, packet) + + if self.debug: + for i, data in enumerate(datas): + print("write {:08x} @ {:08x}".format(data, addr + 4*i)) diff --git a/litex/tools/litex_read_verilog.py b/litex/tools/litex_read_verilog.py new file mode 100755 index 00000000..972d2153 --- /dev/null +++ b/litex/tools/litex_read_verilog.py @@ -0,0 +1,60 @@ +#!/usr/bin/env python3 +import os +import sys +import json + +from litex.build import tools + +def main(): + if len(sys.argv) < 2: + print("usage: litex_read_verilog verilog_file [module]") + exit(1) + + verilog_file = sys.argv[1] + json_file = verilog_file + ".json" + module = None if len(sys.argv) < 3 else sys.argv[2] + + # use yosys to convert verilog to json + yosys_v2j = "\n".join([ + "read_verilog -sv {}".format(verilog_file), + "write_json {}.json".format(verilog_file) + ]) + tools.write_to_file("yosys_v2j.ys", yosys_v2j) + os.system("yosys -q yosys_v2j.ys") + + # load json and convert to migen module + f = open(json_file, "r") + j = json.load(f) + + # create list of modules + modules = [module] if module is not None else j["modules"].keys() + + # create migen definitions + for module in modules: + migen_def = [] + migen_def.append("class {}(Module):".format(module)) + migen_def.append(" "*4 + "def __init__(self):") + for name, info in j["modules"][module]["ports"].items(): + length = "" if len(info["bits"]) == 1 else len(info["bits"]) + migen_def.append(" " * 8 + "self.{} = Signal({})".format(name, length)) + migen_def.append("") + migen_def.append(" "*8 + "# # #") + migen_def.append("") + migen_def.append(" "*8 + "self.specials += Instance(\"{}\",".format(module)) + for name, info in j["modules"][module]["ports"].items(): + io_prefix = { + "input": "i", + "output": "o", + "inout": "io" + }[info["direction"]] + migen_def.append(" "*12 + "{}_{}=self.{},".format(io_prefix, name, name)) + migen_def.append(" "*8 + ")") + migen_def.append("") + print("\n".join(migen_def)) + + # keep things clean after us + os.system("rm " + json_file) + + +if __name__ == "__main__": + main() diff --git a/litex/tools/litex_server.py b/litex/tools/litex_server.py new file mode 100755 index 00000000..702f0349 --- /dev/null +++ b/litex/tools/litex_server.py @@ -0,0 +1,188 @@ +#!/usr/bin/env python3 + +import argparse + +import sys +import socket +import time +import threading + +from litex.tools.remote.etherbone import EtherbonePacket, EtherboneRecord, EtherboneWrites +from litex.tools.remote.etherbone import EtherboneIPC + + +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 open(self): + if hasattr(self, "socket"): + return + self.socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + self.socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEPORT, 1) + self.socket.bind((self.bind_ip, self.bind_port)) + print("tcp port: {:d}".format(self.bind_port)) + self.socket.listen(1) + self.comm.open() + + def close(self): + self.comm.close() + if not hasattr(self, "socket"): + return + self.socket.close() + del self.socket + + def _serve_thread(self): + while True: + client_socket, addr = self.socket.accept() + print("Connected with " + addr[0] + ":" + str(addr[1])) + try: + while True: + try: + packet = self.receive_packet(client_socket) + if packet == 0: + break + except: + break + packet = EtherbonePacket(packet) + packet.decode() + + record = packet.records.pop() + + # wait for lock + while self.lock: + time.sleep(0.01) + + # set lock + self.lock = True + + # handle writes: + if record.writes != None: + self.comm.write(record.writes.base_addr, record.writes.get_datas()) + + # handle reads + if record.reads != None: + reads = [] + for addr in record.reads.get_addrs(): + reads.append(self.comm.read(addr)) + + record = EtherboneRecord() + record.writes = EtherboneWrites(datas=reads) + record.wcount = len(record.writes) + + packet = EtherbonePacket() + packet.records = [record] + packet.encode() + self.send_packet(client_socket, packet) + + # release lock + self.lock = False + + finally: + print("Disconnect") + client_socket.close() + + def start(self, nthreads): + for i in range(nthreads): + self.serve_thread = threading.Thread(target=self._serve_thread) + self.serve_thread.setDaemon(True) + self.serve_thread.start() + + +def main(): + print("LiteX remote server") + parser = argparse.ArgumentParser() + # Common arguments + parser.add_argument("--bind-ip", default="localhost", + help="Host bind address") + parser.add_argument("--bind-port", default=1234, + help="Host bind port") + + # UART arguments + parser.add_argument("--uart", action="store_true", + help="Select UART interface") + parser.add_argument("--uart-port", default=None, + help="Set UART port") + parser.add_argument("--uart-baudrate", default=115200, + help="Set UART baudrate") + + # UDP arguments + parser.add_argument("--udp", action="store_true", + help="Select UDP interface") + parser.add_argument("--udp-ip", default="192.168.1.50", + help="Set UDP remote IP address") + parser.add_argument("--udp-port", default=1234, + help="Set UDP remote port") + + # PCIe arguments + parser.add_argument("--pcie", action="store_true", + help="Select PCIe interface") + parser.add_argument("--pcie-bar", default=None, + help="Set PCIe BAR") + + # USB arguments + parser.add_argument("--usb", action="store_true", + help="Select USB interface") + parser.add_argument("--usb-vid", default=None, + help="Set USB vendor ID") + parser.add_argument("--usb-pid", default=None, + help="Set USB product ID") + parser.add_argument("--usb-max-retries", default=10, + help="Number of times to try reconnecting to USB") + args = parser.parse_args() + + + if args.uart: + from litex.tools.remote import CommUART + if args.uart_port is None: + print("Need to specify --uart-port, exiting.") + exit() + 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) + elif args.udp: + from litex.tools.remote import CommUDP + udp_ip = args.udp_ip + udp_port = int(args.udp_port) + print("[CommUDP] ip: {} / port: {} / ".format(udp_ip, udp_port), end="") + comm = CommUDP(udp_ip, udp_port) + elif args.pcie: + from litex.tools.remote import CommPCIe + pcie_bar = args.pcie_bar + if args.pcie_bar is None: + print("Need to speficy --pcie-bar, exiting.") + exit() + print("[CommPCIe] bar: {} / ".format(args.pcie_bar), end="") + comm = CommPCIe(args.pcie_bar) + elif args.usb: + from litex.tools.remote import CommUSB + if args.usb_pid is None and args.usb_vid is None: + print("Need to speficy --usb-vid or --usb-pid, exiting.") + exit() + print("[CommUSB] vid: {} / pid: {} / ".format(args.usb_vid, args.usb_pid), end="") + pid = args.usb_pid + if pid is not None: + pid = int(pid, base=0) + vid = args.usb_vid + if vid is not None: + vid = int(vid, base=0) + comm = CommUSB(vid=vid, pid=pid, max_retries=args.usb_max_retries) + else: + parser.print_help() + exit() + + server = RemoteServer(comm, args.bind_ip, int(args.bind_port)) + server.open() + server.start(4) + try: + import time + while True: time.sleep(100) + except KeyboardInterrupt: + pass + +if __name__ == "__main__": + main() diff --git a/litex/tools/litex_sim.py b/litex/tools/litex_sim.py new file mode 100755 index 00000000..808bd8b9 --- /dev/null +++ b/litex/tools/litex_sim.py @@ -0,0 +1,255 @@ +#!/usr/bin/env python3 + +import argparse + +from migen import * +from migen.genlib.io import CRG + +from litex.build.generic_platform import * +from litex.build.sim import SimPlatform +from litex.build.sim.config import SimConfig + +from litex.soc.integration.soc_core import * +from litex.soc.integration.soc_sdram import * +from litex.soc.integration.builder import * +from litex.soc.cores import uart +from litex.soc.integration.soc_core import mem_decoder + +from litedram.common import PhySettings +from litedram.modules import MT48LC16M16 +from litedram.phy.model import SDRAMPHYModel + +from liteeth.common import convert_ip +from liteeth.phy.model import LiteEthPHYModel +from liteeth.core.mac import LiteEthMAC +from liteeth.core import LiteEthUDPIPCore +from liteeth.frontend.etherbone import LiteEthEtherbone + +from litescope import LiteScopeAnalyzer + + +class SimPins(Pins): + def __init__(self, n=1): + Pins.__init__(self, "s "*n) + + +_io = [ + ("sys_clk", 0, SimPins(1)), + ("sys_rst", 0, SimPins(1)), + ("serial", 0, + Subsignal("source_valid", SimPins()), + Subsignal("source_ready", SimPins()), + Subsignal("source_data", SimPins(8)), + + Subsignal("sink_valid", SimPins()), + Subsignal("sink_ready", SimPins()), + Subsignal("sink_data", SimPins(8)), + ), + ("eth_clocks", 0, + Subsignal("none", SimPins()), + ), + ("eth", 0, + Subsignal("source_valid", SimPins()), + Subsignal("source_ready", SimPins()), + Subsignal("source_data", SimPins(8)), + + Subsignal("sink_valid", SimPins()), + Subsignal("sink_ready", SimPins()), + Subsignal("sink_data", SimPins(8)), + ), + ("eth_clocks", 1, + Subsignal("none", SimPins()), + ), + ("eth", 1, + Subsignal("source_valid", SimPins()), + Subsignal("source_ready", SimPins()), + Subsignal("source_data", SimPins(8)), + + Subsignal("sink_valid", SimPins()), + Subsignal("sink_ready", SimPins()), + Subsignal("sink_data", SimPins(8)), + ), +] + + +class Platform(SimPlatform): + default_clk_name = "sys_clk" + default_clk_period = 1000 # ~ 1MHz + + def __init__(self): + SimPlatform.__init__(self, "SIM", _io) + + def do_finalize(self, fragment): + pass + + +class SimSoC(SoCSDRAM): + csr_peripherals = [ + "ethphy", + "ethmac", + + "etherbonephy", + "etherbonecore", + + "analyzer", + ] + csr_map_update(SoCSDRAM.csr_map, csr_peripherals) + + interrupt_map = { + "ethmac": 3, + } + interrupt_map.update(SoCSDRAM.interrupt_map) + + mem_map = { + "ethmac": 0x30000000, # (shadow @0xb0000000) + } + mem_map.update(SoCSDRAM.mem_map) + + def __init__(self, + with_sdram=False, + with_ethernet=False, + with_etherbone=False, etherbone_mac_address=0x10e2d5000000, etherbone_ip_address="192.168.1.50", + with_analyzer=False, + **kwargs): + platform = Platform() + sys_clk_freq = int(1e6) + SoCSDRAM.__init__(self, platform, clk_freq=sys_clk_freq, + integrated_rom_size=0x8000, + ident="LiteX Simulation", ident_version=True, + with_uart=False, + **kwargs) + # crg + self.submodules.crg = CRG(platform.request("sys_clk")) + + # serial + self.submodules.uart_phy = uart.RS232PHYModel(platform.request("serial")) + self.submodules.uart = uart.UART(self.uart_phy) + + # sdram + if with_sdram: + sdram_module = MT48LC16M16(100e6, "1:1") # use 100MHz timings + phy_settings = PhySettings( + memtype="SDR", + dfi_databits=16, + nphases=1, + rdphase=0, + wrphase=0, + rdcmdphase=0, + wrcmdphase=0, + cl=2, + read_latency=4, + write_latency=0 + ) + self.submodules.sdrphy = SDRAMPHYModel(sdram_module, phy_settings) + self.register_sdram( + self.sdrphy, + sdram_module.geom_settings, + sdram_module.timing_settings) + # reduce memtest size for simulation speedup + self.add_constant("MEMTEST_DATA_SIZE", 8*1024) + self.add_constant("MEMTEST_ADDR_SIZE", 8*1024) + + assert not (with_ethernet and with_etherbone) # FIXME: fix simulator with 2 ethernet interfaces + + # ethernet + if with_ethernet: + # eth phy + self.submodules.ethphy = LiteEthPHYModel(self.platform.request("eth", 0)) + # eth mac + ethmac = LiteEthMAC(phy=self.ethphy, dw=32, + interface="wishbone", endianness=self.cpu.endianness) + if with_etherbone: + ethmac = ClockDomainsRenamer({"eth_tx": "ethphy_eth_tx", "eth_rx": "ethphy_eth_rx"})(ethmac) + self.submodules.ethmac = ethmac + self.add_wb_slave(mem_decoder(self.mem_map["ethmac"]), self.ethmac.bus) + self.add_memory_region("ethmac", self.mem_map["ethmac"] | self.shadow_base, 0x2000) + + # etherbone + if with_etherbone: + # eth phy + self.submodules.etherbonephy = LiteEthPHYModel(self.platform.request("eth", 0)) # FIXME + # eth core + etherbonecore = LiteEthUDPIPCore(self.etherbonephy, + etherbone_mac_address, convert_ip(etherbone_ip_address), sys_clk_freq) + if with_ethernet: + etherbonecore = ClockDomainsRenamer({"eth_tx": "etherbonephy_eth_tx", "eth_rx": "etherbonephy_eth_rx"})(etherbonecore) + self.submodules.etherbonecore = etherbonecore + # etherbone + self.submodules.etherbone = LiteEthEtherbone(self.etherbonecore.udp, 1234, mode="master") + self.add_wb_master(self.etherbone.wishbone.bus) + + # analyzer + if with_analyzer: + analyzer_signals = [ + # FIXME: find interesting signals to probe + self.cpu.ibus, + self.cpu.dbus + ] + self.submodules.analyzer = LiteScopeAnalyzer(analyzer_signals, 512) + + +def main(): + parser = argparse.ArgumentParser(description="Generic LiteX SoC Simulation") + builder_args(parser) + soc_sdram_args(parser) + parser.add_argument("--threads", default=1, + help="set number of threads (default=1)") + parser.add_argument("--rom-init", default=None, + help="rom_init file") + parser.add_argument("--ram-init", default=None, + help="ram_init file") + parser.add_argument("--with-sdram", action="store_true", + help="enable SDRAM support") + parser.add_argument("--with-ethernet", action="store_true", + help="enable Ethernet support") + parser.add_argument("--with-etherbone", action="store_true", + help="enable Etherbone support") + parser.add_argument("--with-analyzer", action="store_true", + help="enable Analyzer support") + parser.add_argument("--trace", action="store_true", + help="enable VCD tracing") + args = parser.parse_args() + + soc_kwargs = soc_sdram_argdict(args) + builder_kwargs = builder_argdict(args) + + sim_config = SimConfig(default_clk="sys_clk") + sim_config.add_module("serial2console", "serial") + + cpu_endianness = "big" + if "cpu_type" in soc_kwargs: + if soc_kwargs["cpu_type"] in ["picorv32", "vexriscv"]: + cpu_endianness = "little" + + if args.rom_init: + soc_kwargs["integrated_rom_init"] = get_mem_data(args.rom_init, cpu_endianness) + if not args.with_sdram: + soc_kwargs["integrated_main_ram_size"] = 0x10000000 # 256 MB + if args.ram_init is not None: + soc_kwargs["integrated_main_ram_init"] = get_mem_data(args.ram_init, cpu_endianness) + else: + assert args.ram_init is None + soc_kwargs["integrated_main_ram_size"] = 0x0 + if args.with_ethernet: + sim_config.add_module("ethernet", "eth", args={"interface": "tap0", "ip": "192.168.1.100"}) + if args.with_etherbone: + sim_config.add_module('ethernet', "eth", args={"interface": "tap1", "ip": "192.168.1.101"}) + + soc = SimSoC( + with_sdram=args.with_sdram, + with_ethernet=args.with_ethernet, + with_etherbone=args.with_etherbone, + with_analyzer=args.with_analyzer, + **soc_kwargs) + if args.ram_init is not None: + soc.add_constant("ROM_BOOT_ADDRESS", 0x40000000) + builder_kwargs["csr_csv"] = "csr.csv" + builder = Builder(soc, **builder_kwargs) + vns = builder.build(run=False, threads=args.threads, sim_config=sim_config, trace=args.trace) + if args.with_analyzer: + soc.analyzer.export_csv(vns, "analyzer.csv") + builder.build(build=False, threads=args.threads, sim_config=sim_config, trace=args.trace) + + +if __name__ == "__main__": + main() diff --git a/litex/tools/litex_term.py b/litex/tools/litex_term.py new file mode 100755 index 00000000..0a7341ad --- /dev/null +++ b/litex/tools/litex_term.py @@ -0,0 +1,319 @@ +#!/usr/bin/env python3 + +import sys +import os +import time +import serial +import threading +import argparse + + +if sys.platform == "win32": + import msvcrt + class Console: + def configure(self): + pass + + def unconfigure(self): + pass + + def getkey(self): + return msvcrt.getch() +else: + import termios + class Console: + def __init__(self): + self.fd = sys.stdin.fileno() + self.default_settings = termios.tcgetattr(self.fd) + + def configure(self): + settings = termios.tcgetattr(self.fd) + settings[3] = settings[3] & ~termios.ICANON & ~termios.ECHO + settings[6][termios.VMIN] = 1 + settings[6][termios.VTIME] = 0 + termios.tcsetattr(self.fd, termios.TCSANOW, settings) + + def unconfigure(self): + termios.tcsetattr(self.fd, termios.TCSAFLUSH, self.default_settings) + + def getkey(self): + return os.read(self.fd, 1) + + +sfl_prompt_req = b"F7: boot from serial\n" +sfl_prompt_ack = b"\x06" + +sfl_magic_req = b"sL5DdSMmkekro\n" +sfl_magic_ack = b"z6IHG7cYDID6o\n" + +# General commands +sfl_cmd_abort = b"\x00" +sfl_cmd_load = b"\x01" +sfl_cmd_jump = b"\x02" + +# Replies +sfl_ack_success = b"K" +sfl_ack_crcerror = b"C" +sfl_ack_unknown = b"U" +sfl_ack_error = b"E" + + +crc16_table = [ + 0x0000, 0x1021, 0x2042, 0x3063, 0x4084, 0x50A5, 0x60C6, 0x70E7, + 0x8108, 0x9129, 0xA14A, 0xB16B, 0xC18C, 0xD1AD, 0xE1CE, 0xF1EF, + 0x1231, 0x0210, 0x3273, 0x2252, 0x52B5, 0x4294, 0x72F7, 0x62D6, + 0x9339, 0x8318, 0xB37B, 0xA35A, 0xD3BD, 0xC39C, 0xF3FF, 0xE3DE, + 0x2462, 0x3443, 0x0420, 0x1401, 0x64E6, 0x74C7, 0x44A4, 0x5485, + 0xA56A, 0xB54B, 0x8528, 0x9509, 0xE5EE, 0xF5CF, 0xC5AC, 0xD58D, + 0x3653, 0x2672, 0x1611, 0x0630, 0x76D7, 0x66F6, 0x5695, 0x46B4, + 0xB75B, 0xA77A, 0x9719, 0x8738, 0xF7DF, 0xE7FE, 0xD79D, 0xC7BC, + 0x48C4, 0x58E5, 0x6886, 0x78A7, 0x0840, 0x1861, 0x2802, 0x3823, + 0xC9CC, 0xD9ED, 0xE98E, 0xF9AF, 0x8948, 0x9969, 0xA90A, 0xB92B, + 0x5AF5, 0x4AD4, 0x7AB7, 0x6A96, 0x1A71, 0x0A50, 0x3A33, 0x2A12, + 0xDBFD, 0xCBDC, 0xFBBF, 0xEB9E, 0x9B79, 0x8B58, 0xBB3B, 0xAB1A, + 0x6CA6, 0x7C87, 0x4CE4, 0x5CC5, 0x2C22, 0x3C03, 0x0C60, 0x1C41, + 0xEDAE, 0xFD8F, 0xCDEC, 0xDDCD, 0xAD2A, 0xBD0B, 0x8D68, 0x9D49, + 0x7E97, 0x6EB6, 0x5ED5, 0x4EF4, 0x3E13, 0x2E32, 0x1E51, 0x0E70, + 0xFF9F, 0xEFBE, 0xDFDD, 0xCFFC, 0xBF1B, 0xAF3A, 0x9F59, 0x8F78, + 0x9188, 0x81A9, 0xB1CA, 0xA1EB, 0xD10C, 0xC12D, 0xF14E, 0xE16F, + 0x1080, 0x00A1, 0x30C2, 0x20E3, 0x5004, 0x4025, 0x7046, 0x6067, + 0x83B9, 0x9398, 0xA3FB, 0xB3DA, 0xC33D, 0xD31C, 0xE37F, 0xF35E, + 0x02B1, 0x1290, 0x22F3, 0x32D2, 0x4235, 0x5214, 0x6277, 0x7256, + 0xB5EA, 0xA5CB, 0x95A8, 0x8589, 0xF56E, 0xE54F, 0xD52C, 0xC50D, + 0x34E2, 0x24C3, 0x14A0, 0x0481, 0x7466, 0x6447, 0x5424, 0x4405, + 0xA7DB, 0xB7FA, 0x8799, 0x97B8, 0xE75F, 0xF77E, 0xC71D, 0xD73C, + 0x26D3, 0x36F2, 0x0691, 0x16B0, 0x6657, 0x7676, 0x4615, 0x5634, + 0xD94C, 0xC96D, 0xF90E, 0xE92F, 0x99C8, 0x89E9, 0xB98A, 0xA9AB, + 0x5844, 0x4865, 0x7806, 0x6827, 0x18C0, 0x08E1, 0x3882, 0x28A3, + 0xCB7D, 0xDB5C, 0xEB3F, 0xFB1E, 0x8BF9, 0x9BD8, 0xABBB, 0xBB9A, + 0x4A75, 0x5A54, 0x6A37, 0x7A16, 0x0AF1, 0x1AD0, 0x2AB3, 0x3A92, + 0xFD2E, 0xED0F, 0xDD6C, 0xCD4D, 0xBDAA, 0xAD8B, 0x9DE8, 0x8DC9, + 0x7C26, 0x6C07, 0x5C64, 0x4C45, 0x3CA2, 0x2C83, 0x1CE0, 0x0CC1, + 0xEF1F, 0xFF3E, 0xCF5D, 0xDF7C, 0xAF9B, 0xBFBA, 0x8FD9, 0x9FF8, + 0x6E17, 0x7E36, 0x4E55, 0x5E74, 0x2E93, 0x3EB2, 0x0ED1, 0x1EF0 +] + + +def crc16(l): + crc = 0 + for d in l: + crc = crc16_table[((crc >> 8) ^ d) & 0xff] ^ (crc << 8) + return crc & 0xffff + + +class SFLFrame: + def __init__(self): + self.cmd = bytes() + self.payload = bytes() + + def compute_crc(self): + return crc16(self.cmd + self.payload) + + def encode(self): + packet = bytes([len(self.payload)]) + packet += self.compute_crc().to_bytes(2, "big") + packet += self.cmd + packet += self.payload + return packet + + +class LiteXTerm: + def __init__(self, serial_boot, kernel_image, kernel_address): + self.serial_boot = serial_boot + self.kernel_image = kernel_image + self.kernel_address = kernel_address + + self.reader_alive = False + self.writer_alive = False + + self.prompt_detect_buffer = bytes(len(sfl_prompt_req)) + self.magic_detect_buffer = bytes(len(sfl_magic_req)) + + self.console = Console() + + def open(self, port, baudrate): + if hasattr(self, "port"): + return + self.port = serial.serial_for_url(port, baudrate) + + def close(self): + if not hasattr(self, "port"): + return + self.port.close() + del self.port + + def send_frame(self, frame): + retry = 1 + while retry: + self.port.write(frame.encode()) + # Get the reply from the device + reply = self.port.read() + if reply == sfl_ack_success: + retry = 0 + elif reply == sfl_ack_crcerror: + retry = 1 + else: + print("[TERM] Got unknown reply '{}' from the device, aborting.".format(reply)) + return 0 + return 1 + + def upload(self, filename, address): + with open(filename, "rb") as f: + data = f.read() + print("[TERM] Uploading {} ({} bytes)...".format(filename, len(data))) + current_address = address + position = 0 + length = len(data) + start = time.time() + while len(data): + sys.stdout.write("|{}>{}| {}%\r".format('=' * (20*position//length), + ' ' * (20-20*position//length), + 100*position//length)) + sys.stdout.flush() + frame = SFLFrame() + frame_data = data[:251] + frame.cmd = sfl_cmd_load + frame.payload = current_address.to_bytes(4, "big") + frame.payload += frame_data + if self.send_frame(frame) == 0: + return + current_address += len(frame_data) + position += len(frame_data) + try: + data = data[251:] + except: + data = [] + end = time.time() + elapsed = end - start + print("[TERM] Upload complete ({0:.1f}KB/s).".format(length/(elapsed*1024))) + return length + + def boot(self): + print("[TERM] Booting the device.") + frame = SFLFrame() + frame.cmd = sfl_cmd_jump + frame.payload = self.kernel_address.to_bytes(4, "big") + self.send_frame(frame) + + def detect_prompt(self, data): + if len(data): + self.prompt_detect_buffer = self.prompt_detect_buffer[1:] + data + return self.prompt_detect_buffer == sfl_prompt_req + else: + return False + + def answer_prompt(self): + print("[TERM] Received serial boot prompt from the device.") + self.port.write(sfl_prompt_ack) + + def detect_magic(self, data): + if len(data): + self.magic_detect_buffer = self.magic_detect_buffer[1:] + data + return self.magic_detect_buffer == sfl_magic_req + else: + return False + + def answer_magic(self): + print("[TERM] Received firmware download request from the device.") + if os.path.exists(self.kernel_image): + self.port.write(sfl_magic_ack) + self.upload(self.kernel_image, self.kernel_address) + self.boot() + print("[TERM] Done."); + + def reader(self): + try: + while self.reader_alive: + c = self.port.read() + if c == b"\r": + sys.stdout.buffer.write(b"\n") + else: + sys.stdout.buffer.write(c) + sys.stdout.flush() + + if self.kernel_image is not None: + if self.serial_boot and self.detect_prompt(c): + self.answer_prompt() + if self.detect_magic(c): + self.answer_magic() + + except serial.SerialException: + self.reader_alive = False + raise + + def start_reader(self): + self.reader_alive = True + self.reader_thread = threading.Thread(target=self.reader) + self.reader_thread.setDaemon(True) + self.reader_thread.start() + + def stop_reader(self): + self.reader_alive = False + self.reader_thread.join() + + def writer(self): + try: + while self.writer_alive: + b = self.console.getkey() + if b == b"\x03": + self.stop() + elif b == b"\n": + self.port.write(b"\x0a") + else: + self.port.write(b) + except: + self.writer_alive = False + raise + + def start_writer(self): + self.writer_alive = True + self.writer_thread = threading.Thread(target=self.writer) + self.writer_thread.setDaemon(True) + self.writer_thread.start() + + def stop_writer(self): + self.writer_alive = False + self.writer_thread.join() + + def start(self): + print("[TERM] Starting....") + self.start_reader() + self.start_writer() + + def stop(self): + self.reader_alive = False + self.writer_alive = False + + def join(self, writer_only=False): + self.writer_thread.join() + if not writer_only: + self.reader_thread.join() + + +def _get_args(): + parser = argparse.ArgumentParser() + parser.add_argument("port", help="serial port") + parser.add_argument("--speed", default=115200, help="serial baudrate") + parser.add_argument("--serial-boot", default=False, action='store_true', + help="automatically initiate serial boot") + parser.add_argument("--kernel", default=None, help="kernel image") + parser.add_argument("--kernel-adr", type=lambda a: int(a, 0), default=0x40000000, help="kernel address") + return parser.parse_args() + + +def main(): + args = _get_args() + term = LiteXTerm(args.serial_boot, args.kernel, args.kernel_adr) + term.console.configure() + try: + term.open(args.port, args.speed) + term.start() + term.join(True) + except KeyboardInterrupt: + term.console.unconfigure() + finally: + term.console.unconfigure() + term.close() + +if __name__ == "__main__": + main() diff --git a/litex/tools/remote/__init__.py b/litex/tools/remote/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/litex/tools/remote/comm_pcie.py b/litex/tools/remote/comm_pcie.py new file mode 100644 index 00000000..3cfb0efb --- /dev/null +++ b/litex/tools/remote/comm_pcie.py @@ -0,0 +1,43 @@ +import mmap + + +class CommPCIe: + def __init__(self, bar, debug=False): + self.bar = bar + self.debug = debug + + def open(self): + if hasattr(self, "sysfs"): + return + self.sysfs = open(self.bar, "r+b") + self.sysfs.flush() + self.mmap = mmap.mmap(self.sysfs.fileno(), 0) + + def close(self): + if not hasattr(self, "sysfs"): + return + self.mmap.close() + del self.mmap + self.sysfs.close() + del self.sysfs + + def read(self, addr, length=None): + data = [] + length_int = 1 if length is None else length + for i in range(length_int): + self.mmap.seek(addr + 4*i) + value = int.from_bytes(self.mmap.read(4), byteorder="little") + if self.debug: + print("read {:08x} @ {:08x}".format(value, addr + 4*i)) + if length is None: + return value + data.append(value) + return data + + def write(self, addr, data): + data = data if isinstance(data, list) else [data] + length = len(data) + for i, value in enumerate(data): + self.mmap[addr + 4*i:addr + 4*(i + 1)] = value.to_bytes(4, byteorder="little") + if self.debug: + print("write {:08x} @ {:08x}".format(value, addr + 4*i)) diff --git a/litex/tools/remote/comm_uart.py b/litex/tools/remote/comm_uart.py new file mode 100644 index 00000000..0a056ec7 --- /dev/null +++ b/litex/tools/remote/comm_uart.py @@ -0,0 +1,74 @@ +import serial +import struct + + +class CommUART: + msg_type = { + "write": 0x01, + "read": 0x02 + } + def __init__(self, port, baudrate=115200, debug=False): + self.port = port + self.baudrate = str(baudrate) + self.debug = debug + self.port = serial.serial_for_url(port, baudrate) + + def open(self): + if hasattr(self, "port"): + return + self.port.open() + + def close(self): + if not hasattr(self, "port"): + return + self.port.close() + del self.port + + def _read(self, length): + r = bytes() + while len(r) < length: + r += self.port.read(length - len(r)) + return r + + def _write(self, data): + remaining = len(data) + pos = 0 + while remaining: + written = self.port.write(data[pos:]) + remaining -= written + pos += written + + def _flush(self): + if self.port.inWaiting() > 0: + self.port.read(self.port.inWaiting()) + + def read(self, addr, length=None): + self._flush() + data = [] + length_int = 1 if length is None else length + self._write([self.msg_type["read"], 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") + if self.debug: + print("read {:08x} @ {:08x}".format(value, addr + 4*i)) + if length is None: + return value + data.append(value) + return data + + def write(self, addr, data): + 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]) + 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"))) + if self.debug: + print("write {:08x} @ {:08x}".format(value, addr + offset, 4*i)) + offset += size + length -= size diff --git a/litex/tools/remote/comm_udp.py b/litex/tools/remote/comm_udp.py new file mode 100644 index 00000000..e938f2d7 --- /dev/null +++ b/litex/tools/remote/comm_udp.py @@ -0,0 +1,62 @@ +import socket + +from litex.tools.remote.etherbone import EtherbonePacket, EtherboneRecord +from litex.tools.remote.etherbone import EtherboneReads, EtherboneWrites + + +class CommUDP: + def __init__(self, server="192.168.1.50", port=1234, debug=False): + self.server = server + self.port = port + self.debug = debug + + def open(self): + if hasattr(self, "tx_socket"): + return + self.tx_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) + self.rx_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) + self.rx_socket.bind(("", self.port)) + + def close(self): + if not hasattr(self, "tx_socket"): + return + self.tx_socket.close() + del self.tx_socket + self.rx_socket.close() + del self.rx_socket + + def read(self, addr, length=None): + length_int = 1 if length is None else length + record = EtherboneRecord() + record.reads = EtherboneReads(addrs=[addr+4*j for j in range(length_int)]) + record.rcount = len(record.reads) + + packet = EtherbonePacket() + packet.records = [record] + packet.encode() + self.tx_socket.sendto(bytes(packet), (self.server, self.port)) + + datas, dummy = self.rx_socket.recvfrom(8192) + packet = EtherbonePacket(datas) + packet.decode() + datas = packet.records.pop().writes.get_datas() + if self.debug: + for i, value in enumerate(datas): + print("read {:08x} @ {:08x}".format(value, addr + 4*i)) + return datas[0] if length is None else datas + + def write(self, addr, datas): + datas = datas if isinstance(datas, list) else [datas] + length = len(datas) + record = EtherboneRecord() + record.writes = EtherboneWrites(base_addr=addr, datas=iter(datas)) + record.wcount = len(record.writes) + + packet = EtherbonePacket() + packet.records = [record] + packet.encode() + self.tx_socket.sendto(bytes(packet), (self.server, self.port)) + + if self.debug: + for i, value in enumerate(datas): + print("write {:08x} @ {:08x}".format(value, addr + 4*i)) diff --git a/litex/tools/remote/comm_usb.py b/litex/tools/remote/comm_usb.py new file mode 100644 index 00000000..1e4ea1e4 --- /dev/null +++ b/litex/tools/remote/comm_usb.py @@ -0,0 +1,142 @@ +import usb.core +import time + +# Wishbone USB Protocol Bridge +# ============================ +# +# This module implements a bridge to connect LiteX to the target system's +# Wishbone bus via USB. It uses `vendor` packets to communicate, which are +# normally reserved. Since we're the vendors of this USB protocol, we take +# advantage of this packet type to implement the bridge. +# +# All traffic goes to/from USB EP0, which is guaranteed to exist regardless +# of the user's device implementation. The 8th bit of the first SETUP DATA +# transaction contains all the information we need to mark this as a +# Wishbone packet. +# +# Packets going to EP0 always start with a SETUP packet, followed by an IN +# or an OUT packet, followed by an OUT or an IN packet. +# +# The SETUP packet looks like this: +# +# +----+----+----------+----+----+ +# | C0 | 00 | ADDRESS | 04 | 00 | read packet +# +----+----+----------+----+----+ +# 1 1 4 1 1 +# +# +----+----+----------+----+----+ +# | 40 | 00 | ADDRESS | 04 | 00 | write packet +# +----+----+----------+----+----+ +# 1 1 4 1 1 +# +# If the transaction is a "read" transaction, the device responds with an OUT +# packet with the data. If the transaction is a "write" transaction, the host +# responds with an IN packet with the data. +# +# Much like other Wishbone bridges, there are two types of packets. The first +# byte indicates what type of packet it is, and that it is a Wishbone Bridge +# packet. This is the value "0x40" (VENDOR type packet destined for DEVICE) +# with the "Data Phase Transfer" bit either set or cleared: +# - Read: 0xc0 +# - Write: 0x40 +# +# The next byte is bRequest, which in the current implementation is unused. +# Set this value to 0. +# +# The next four bytes form the wValue and wIndex values of the SETUP token. +# We reuse these two 16-bit values as a single 32-bit ADDRESS packet. Note that +# USB is big endian. +# +# Finally, the last two bytes indicate the length of the transaction. Since +# we only support 32-bit reads and writes, this is always 4. On big endian +# USB, this has the value {04, 00}. + +class CommUSB: + def __init__(self, vid=None, pid=None, max_retries=10, debug=False): + self.vid = vid + self.pid = pid + self.debug = debug + self.max_retries = max_retries + self.MAX_RECURSION_COUNT = 5 + + def open(self): + if hasattr(self, "dev"): + return + for t in range(self.max_retries): + args = {} + if self.vid is not None: + args['idVendor'] = self.vid + if self.pid is not None: + args['idProduct'] = self.pid + self.dev = usb.core.find(**args) + if self.dev is not None: + if self.debug: + print("device connected after {} tries".format(t+1)) + return True + del self.dev + time.sleep(0.2 * t) + print("unable to find usb device after {} tries".format(self.max_retries)) + return False + + + def close(self): + if not hasattr(self, "dev"): + return + del self.dev + + def read(self, addr, length=None): + data = [] + length_int = 1 if length is None else length + for i in range(length_int): + value = self.usb_read(addr) + # Note that sometimes, the value ends up as None when the device + # disconnects during a transaction. Paper over this fact by + # replacing it with a sentinal. + if value is None: + value = 0xffffffff + if self.debug: + print("read {:08x} @ {:08x}".format(value, addr)) + if length is None: + return value + data.append(value) + return data + + def usb_read(self, addr, depth=0): + try: + value = self.dev.ctrl_transfer(bmRequestType=0xc0, + bRequest=0x00, + wValue=addr & 0xffff, + wIndex=(addr >> 16) & 0xffff, + data_or_wLength=4) + if value is None: + raise TypeError + return int.from_bytes(value, byteorder="little") + except (usb.core.USBError, TypeError): + self.close() + self.open() + if depth < self.MAX_RECURSION_COUNT: + return self.usb_read(addr, depth+1) + + def write(self, addr, data): + data = data if isinstance(data, list) else [data] + length = len(data) + for i, value in enumerate(data): + self.usb_write(addr, value) + if self.debug: + print("write {:08x} @ {:08x}".format(value, addr + 4*i)) + + def usb_write(self, addr, value, depth=0): + try: + self.dev.ctrl_transfer(bmRequestType=0x40, bRequest=0x00, + wValue=addr & 0xffff, + wIndex=(addr >> 16) & 0xffff, + data_or_wLength=bytes([(value >> 0) & 0xff, + (value >> 8) & 0xff, + (value >> 16) & 0xff, + (value >> 24) & 0xff] + ), timeout=None) + except usb.core.USBError: + self.close() + self.open() + if depth < self.MAX_RECURSION_COUNT: + return self.usb_write(addr, value, depth+1) diff --git a/litex/tools/remote/csr_builder.py b/litex/tools/remote/csr_builder.py new file mode 100644 index 00000000..1bbb54a7 --- /dev/null +++ b/litex/tools/remote/csr_builder.py @@ -0,0 +1,116 @@ +import csv + + +class CSRElements: + def __init__(self, d): + self.__dict__.update(d) + + @property + def d(self): + return self.__dict__ + + def __getattr__(self, attr): + try: + return self.__dict__[attr] + except KeyError: + pass + raise AttributeError("No such element " + attr) + + +class CSRRegister: + def __init__(self, readfn, writefn, name, addr, length, data_width, mode): + self.readfn = readfn + self.writefn = writefn + self.name = name + self.addr = addr + self.length = length + self.data_width = data_width + self.mode = mode + + def read(self): + if self.mode not in ["rw", "ro"]: + raise KeyError(self.name + "register not readable") + datas = self.readfn(self.addr, length=self.length) + if isinstance(datas, int): + return datas + else: + data = 0 + for i in range(self.length): + data = data << self.data_width + data |= datas[i] + return data + + def write(self, value): + if self.mode not in ["rw", "wo"]: + raise KeyError(self.name + "register not writable") + datas = [] + for i in range(self.length): + datas.append((value >> ((self.length-1-i)*self.data_width)) & (2**self.data_width-1)) + self.writefn(self.addr, datas) + + +class CSRMemoryRegion: + def __init__(self, base, size): + self.base = base + self.size = size + + +class CSRBuilder: + def __init__(self, comm, csr_csv, csr_data_width=None): + self.constants = self.build_constants(csr_csv) + + # Load csr_data_width from the constants, otherwise it must be provided + constant_csr_data_width = self.constants.d.get('csr_data_width', None) + if csr_data_width is None: + csr_data_width = constant_csr_data_width + if csr_data_width is None: + raise KeyError('csr_data_width not found in constants, please provide!') + if csr_data_width != constant_csr_data_width: + 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 + self.bases = self.build_bases(csr_csv) + self.regs = self.build_registers(csr_csv, comm.read, comm.write) + self.mems = self.build_memories(csr_csv) + + def build_bases(self, csr_csv): + csv_reader = csv.reader(open(csr_csv), delimiter=',', quotechar='#') + d = {} + for item in csv_reader: + group, name, addr, dummy0, dummy1 = item + if group == "csr_base": + d[name] = int(addr.replace("0x", ""), 16) + return CSRElements(d) + + def build_registers(self, csr_csv, readfn, writefn): + csv_reader = csv.reader(open(csr_csv), delimiter=',', quotechar='#') + d = {} + for item in csv_reader: + group, name, addr, length, mode = item + if group == "csr_register": + addr = int(addr.replace("0x", ""), 16) + length = int(length) + d[name] = CSRRegister(readfn, writefn, name, addr, length, self.csr_data_width, mode) + return CSRElements(d) + + def build_constants(self, csr_csv): + csv_reader = csv.reader(open(csr_csv), delimiter=',', quotechar='#') + d = {} + for item in csv_reader: + group, name, value, dummy0, dummy1 = item + if group == "constant": + try: + d[name] = int(value) + except: + d[name] = value + return CSRElements(d) + + def build_memories(self, csr_csv): + csv_reader = csv.reader(open(csr_csv), delimiter=',', quotechar='#') + d = {} + for item in csv_reader: + group, name, base, size, dummy1 = item + if group == "memory_region": + d[name] = CSRMemoryRegion(int(base, 16), int(size)) + return CSRElements(d) diff --git a/litex/tools/remote/etherbone.py b/litex/tools/remote/etherbone.py new file mode 100644 index 00000000..068f65a0 --- /dev/null +++ b/litex/tools/remote/etherbone.py @@ -0,0 +1,375 @@ +import math +import struct + +from litex.soc.interconnect.stream_packet import HeaderField, Header + + +etherbone_magic = 0x4e6f +etherbone_version = 1 +etherbone_packet_header_length = 8 +etherbone_packet_header_fields = { + "magic": HeaderField(0, 0, 16), + + "version": HeaderField(2, 4, 4), + "nr": HeaderField(2, 2, 1), # No Reads + "pr": HeaderField(2, 1, 1), # Probe Reply + "pf": HeaderField(2, 0, 1), # Probe Flag + + "addr_size": HeaderField(3, 4, 4), # 1=8bits, 2=16bits, 4=32bits, 8=64bits + "port_size": HeaderField(3, 0, 4), # Same as above +} +etherbone_packet_header = Header(etherbone_packet_header_fields, + etherbone_packet_header_length, + swap_field_bytes=True) + +# When reading/writing to a FIFO, you don't increase +# the address after each write. +etherbone_record_header_length = 4 +etherbone_record_header_fields = { + "bca": HeaderField(0, 0, 1), # ReplyToCfgSpace - ??? (C)onfig (A)dress + "rca": HeaderField(0, 1, 1), # ReadFromCfgSpace - (R)ead from (C)onfig (A)dress + "rff": HeaderField(0, 2, 1), # ReadFIFO - (R)ead (F)I(F)O + "cyc": HeaderField(0, 4, 1), # DropCycle - Drop(Cyc)le + "wca": HeaderField(0, 5, 1), # WriteToCfgSpace - (W)rite to (C)onfig (A)dress + "wff": HeaderField(0, 6, 1), # WriteFIFO - (W)rite (F)I(F)O + + "byte_enable": HeaderField(1, 0, 8), # Select + + "wcount": HeaderField(2, 0, 8), # Writes + + "rcount": HeaderField(3, 0, 8), # Reads +} +etherbone_record_header = Header(etherbone_record_header_fields, + etherbone_record_header_length, + swap_field_bytes=True) + + +def split_bytes(v, n, endianness="big"): + r = [] + return v.to_bytes(n, byteorder=endianness) + + +def merge_bytes(b, endianness="big"): + return int.from_bytes(b, endianness) + + +def get_field_data(field, datas): + v = merge_bytes(datas[field.byte:field.byte+math.ceil(field.width/8)]) + return (v >> field.offset) & (2**field.width-1) + + +class Packet(list): + def __init__(self, init=[]): + self.ongoing = False + self.done = False + for data in init: + self.append(data) + + +class EtherboneWrite: + def __init__(self, data): + self.data = data + + def __repr__(self): + return "WR32 0x{:08x}".format(self.data) + + +class EtherboneRead: + def __init__(self, addr): + self.addr = addr + + def __repr__(self): + return "RD32 @ 0x{:08x}".format(self.addr) + + +class EtherboneWrites(Packet): + def __init__(self, init=[], base_addr=0, datas=[]): + Packet.__init__(self, init) + self.base_addr = base_addr + self.writes = [] + self.encoded = init != [] + for data in datas: + self.add(EtherboneWrite(data)) + + def add(self, write): + self.writes.append(write) + + def get_datas(self): + datas = [] + for write in self.writes: + datas.append(write.data) + return datas + + def encode(self): + if self.encoded: + raise ValueError + for byte in split_bytes(self.base_addr, 4): + self.append(byte) + for write in self.writes: + for byte in split_bytes(write.data, 4): + self.append(byte) + self.encoded = True + + def decode(self): + if not self.encoded: + raise ValueError + base_addr = [] + for i in range(4): + base_addr.append(self.pop(0)) + self.base_addr = merge_bytes(base_addr) + self.writes = [] + while len(self) != 0: + write = [] + for i in range(4): + write.append(self.pop(0)) + self.writes.append(EtherboneWrite(merge_bytes(write))) + self.encoded = False + + def __repr__(self): + r = "Writes\n" + r += "--------\n" + r += "BaseAddr @ 0x{:08x}\n".format(self.base_addr) + for write in self.writes: + r += write.__repr__() + "\n" + return r + + +class EtherboneReads(Packet): + def __init__(self, init=[], base_ret_addr=0, addrs=[]): + Packet.__init__(self, init) + self.base_ret_addr = base_ret_addr + self.reads = [] + self.encoded = init != [] + for addr in addrs: + self.add(EtherboneRead(addr)) + + def add(self, read): + self.reads.append(read) + + def get_addrs(self): + addrs = [] + for read in self.reads: + addrs.append(read.addr) + return addrs + + def encode(self): + if self.encoded: + raise ValueError + for byte in split_bytes(self.base_ret_addr, 4): + self.append(byte) + for read in self.reads: + for byte in split_bytes(read.addr, 4): + self.append(byte) + self.encoded = True + + def decode(self): + if not self.encoded: + raise ValueError + base_ret_addr = [] + for i in range(4): + base_ret_addr.append(self.pop(0)) + self.base_ret_addr = merge_bytes(base_ret_addr) + self.reads = [] + while len(self) != 0: + read = [] + for i in range(4): + read.append(self.pop(0)) + self.reads.append(EtherboneRead(merge_bytes(read))) + self.encoded = False + + def __repr__(self): + r = "Reads\n" + r += "--------\n" + r += "BaseRetAddr @ 0x{:08x}\n".format(self.base_ret_addr) + for read in self.reads: + r += read.__repr__() + "\n" + return r + + +class EtherboneRecord(Packet): + def __init__(self, init=[]): + Packet.__init__(self, init) + self.writes = None + self.reads = None + self.bca = 0 + self.rca = 0 + self.rff = 0 + self.cyc = 0 + self.wca = 0 + self.wff = 0 + self.byte_enable = 0xf + self.wcount = 0 + self.rcount = 0 + self.encoded = init != [] + + + def get_writes(self): + if self.wcount == 0: + return None + else: + writes = [] + for i in range((self.wcount+1)*4): + writes.append(self.pop(0)) + return EtherboneWrites(writes) + + def get_reads(self): + if self.rcount == 0: + return None + else: + reads = [] + for i in range((self.rcount+1)*4): + reads.append(self.pop(0)) + return EtherboneReads(reads) + + def decode(self): + if not self.encoded: + raise ValueError + header = [] + for byte in self[:etherbone_record_header.length]: + header.append(self.pop(0)) + for k, v in sorted(etherbone_record_header.fields.items()): + setattr(self, k, get_field_data(v, header)) + self.writes = self.get_writes() + if self.writes is not None: + self.writes.decode() + self.reads = self.get_reads() + if self.reads is not None: + self.reads.decode() + self.encoded = False + + def set_writes(self, writes): + self.wcount = len(writes.writes) + writes.encode() + for byte in writes: + self.append(byte) + + def set_reads(self, reads): + self.rcount = len(reads.reads) + reads.encode() + for byte in reads: + self.append(byte) + + def encode(self): + if self.encoded: + raise ValueError + if self.writes is not None: + self.set_writes(self.writes) + if self.reads is not None: + self.set_reads(self.reads) + header = 0 + for k, v in sorted(etherbone_record_header.fields.items()): + value = merge_bytes(split_bytes(getattr(self, k), + math.ceil(v.width/8)), + "little") + header += (value << v.offset+(v.byte*8)) + for d in split_bytes(header, etherbone_record_header.length): + self.insert(0, d) + self.encoded = True + + def __repr__(self, n=0): + r = "Record {}\n".format(n) + r += "--------\n" + if self.encoded: + for d in self: + r += "{:02x}".format(d) + else: + for k in sorted(etherbone_record_header.fields.keys()): + r += k + " : 0x{:0x}\n".format(getattr(self, k)) + if self.wcount != 0: + r += self.writes.__repr__() + if self.rcount != 0: + r += self.reads.__repr__() + return r + + +class EtherbonePacket(Packet): + def __init__(self, init=[]): + 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.nr = 0 + self.pr = 0 + self.pf = 0 + + def get_records(self): + records = [] + done = False + payload = self + while len(payload) != 0: + record = EtherboneRecord(payload) + record.decode() + records.append(record) + payload = record[:] + return records + + def decode(self): + if not self.encoded: + raise ValueError + header = [] + for byte in self[:etherbone_packet_header.length]: + header.append(self.pop(0)) + for k, v in sorted(etherbone_packet_header.fields.items()): + setattr(self, k, get_field_data(v, header)) + self.records = self.get_records() + self.encoded = False + + def set_records(self, records): + for record in records: + record.encode() + for byte in record: + self.append(byte) + + def encode(self): + if self.encoded: + raise ValueError + self.set_records(self.records) + header = 0 + for k, v in sorted(etherbone_packet_header.fields.items()): + value = merge_bytes(split_bytes(getattr(self, k), math.ceil(v.width/8)), "little") + header += (value << v.offset+(v.byte*8)) + for d in split_bytes(header, etherbone_packet_header.length): + self.insert(0, d) + self.encoded = True + + def __repr__(self): + r = "Packet\n" + r += "--------\n" + if self.encoded: + for d in self: + r += "{:02x}".format(d) + else: + for k in sorted(etherbone_packet_header.fields.keys()): + r += k + " : 0x{:0x}\n".format(getattr(self, k)) + for i, record in enumerate(self.records): + r += record.__repr__(i) + return r + + +class EtherboneIPC: + def send_packet(self, socket, packet): + socket.sendall(bytes(packet)) + + def receive_packet(self, socket): + header_length = etherbone_packet_header_length + etherbone_record_header_length + packet = bytes() + while len(packet) < header_length: + chunk = socket.recv(header_length - len(packet)) + if len(chunk) == 0: + return 0 + else: + packet += chunk + wcount, rcount = struct.unpack(">BB", packet[header_length-2:]) + counts = wcount + rcount + packet_size = header_length + 4*(counts + 1) + while len(packet) < packet_size: + chunk = socket.recv(packet_size - len(packet)) + if len(chunk) == 0: + return 0 + else: + packet += chunk + return packet diff --git a/litex/utils/__init__.py b/litex/utils/__init__.py deleted file mode 100644 index 8b137891..00000000 --- a/litex/utils/__init__.py +++ /dev/null @@ -1 +0,0 @@ - diff --git a/litex/utils/litex_read_verilog.py b/litex/utils/litex_read_verilog.py deleted file mode 100755 index 972d2153..00000000 --- a/litex/utils/litex_read_verilog.py +++ /dev/null @@ -1,60 +0,0 @@ -#!/usr/bin/env python3 -import os -import sys -import json - -from litex.build import tools - -def main(): - if len(sys.argv) < 2: - print("usage: litex_read_verilog verilog_file [module]") - exit(1) - - verilog_file = sys.argv[1] - json_file = verilog_file + ".json" - module = None if len(sys.argv) < 3 else sys.argv[2] - - # use yosys to convert verilog to json - yosys_v2j = "\n".join([ - "read_verilog -sv {}".format(verilog_file), - "write_json {}.json".format(verilog_file) - ]) - tools.write_to_file("yosys_v2j.ys", yosys_v2j) - os.system("yosys -q yosys_v2j.ys") - - # load json and convert to migen module - f = open(json_file, "r") - j = json.load(f) - - # create list of modules - modules = [module] if module is not None else j["modules"].keys() - - # create migen definitions - for module in modules: - migen_def = [] - migen_def.append("class {}(Module):".format(module)) - migen_def.append(" "*4 + "def __init__(self):") - for name, info in j["modules"][module]["ports"].items(): - length = "" if len(info["bits"]) == 1 else len(info["bits"]) - migen_def.append(" " * 8 + "self.{} = Signal({})".format(name, length)) - migen_def.append("") - migen_def.append(" "*8 + "# # #") - migen_def.append("") - migen_def.append(" "*8 + "self.specials += Instance(\"{}\",".format(module)) - for name, info in j["modules"][module]["ports"].items(): - io_prefix = { - "input": "i", - "output": "o", - "inout": "io" - }[info["direction"]] - migen_def.append(" "*12 + "{}_{}=self.{},".format(io_prefix, name, name)) - migen_def.append(" "*8 + ")") - migen_def.append("") - print("\n".join(migen_def)) - - # keep things clean after us - os.system("rm " + json_file) - - -if __name__ == "__main__": - main() diff --git a/litex/utils/litex_server.py b/litex/utils/litex_server.py deleted file mode 100755 index 49101225..00000000 --- a/litex/utils/litex_server.py +++ /dev/null @@ -1,188 +0,0 @@ -#!/usr/bin/env python3 - -import argparse - -import sys -import socket -import time -import threading - -from litex.soc.tools.remote.etherbone import EtherbonePacket, EtherboneRecord, EtherboneWrites -from litex.soc.tools.remote.etherbone import EtherboneIPC - - -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 open(self): - if hasattr(self, "socket"): - return - self.socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) - self.socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEPORT, 1) - self.socket.bind((self.bind_ip, self.bind_port)) - print("tcp port: {:d}".format(self.bind_port)) - self.socket.listen(1) - self.comm.open() - - def close(self): - self.comm.close() - if not hasattr(self, "socket"): - return - self.socket.close() - del self.socket - - def _serve_thread(self): - while True: - client_socket, addr = self.socket.accept() - print("Connected with " + addr[0] + ":" + str(addr[1])) - try: - while True: - try: - packet = self.receive_packet(client_socket) - if packet == 0: - break - except: - break - packet = EtherbonePacket(packet) - packet.decode() - - record = packet.records.pop() - - # wait for lock - while self.lock: - time.sleep(0.01) - - # set lock - self.lock = True - - # handle writes: - if record.writes != None: - self.comm.write(record.writes.base_addr, record.writes.get_datas()) - - # handle reads - if record.reads != None: - reads = [] - for addr in record.reads.get_addrs(): - reads.append(self.comm.read(addr)) - - record = EtherboneRecord() - record.writes = EtherboneWrites(datas=reads) - record.wcount = len(record.writes) - - packet = EtherbonePacket() - packet.records = [record] - packet.encode() - self.send_packet(client_socket, packet) - - # release lock - self.lock = False - - finally: - print("Disconnect") - client_socket.close() - - def start(self, nthreads): - for i in range(nthreads): - self.serve_thread = threading.Thread(target=self._serve_thread) - self.serve_thread.setDaemon(True) - self.serve_thread.start() - - -def main(): - print("LiteX remote server") - parser = argparse.ArgumentParser() - # Common arguments - parser.add_argument("--bind-ip", default="localhost", - help="Host bind address") - parser.add_argument("--bind-port", default=1234, - help="Host bind port") - - # UART arguments - parser.add_argument("--uart", action="store_true", - help="Select UART interface") - parser.add_argument("--uart-port", default=None, - help="Set UART port") - parser.add_argument("--uart-baudrate", default=115200, - help="Set UART baudrate") - - # UDP arguments - parser.add_argument("--udp", action="store_true", - help="Select UDP interface") - parser.add_argument("--udp-ip", default="192.168.1.50", - help="Set UDP remote IP address") - parser.add_argument("--udp-port", default=1234, - help="Set UDP remote port") - - # PCIe arguments - parser.add_argument("--pcie", action="store_true", - help="Select PCIe interface") - parser.add_argument("--pcie-bar", default=None, - help="Set PCIe BAR") - - # USB arguments - parser.add_argument("--usb", action="store_true", - help="Select USB interface") - parser.add_argument("--usb-vid", default=None, - help="Set USB vendor ID") - parser.add_argument("--usb-pid", default=None, - help="Set USB product ID") - parser.add_argument("--usb-max-retries", default=10, - help="Number of times to try reconnecting to USB") - args = parser.parse_args() - - - if args.uart: - from litex.soc.tools.remote import CommUART - if args.uart_port is None: - print("Need to specify --uart-port, exiting.") - exit() - 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) - elif args.udp: - from litex.soc.tools.remote import CommUDP - udp_ip = args.udp_ip - udp_port = int(args.udp_port) - print("[CommUDP] ip: {} / port: {} / ".format(udp_ip, udp_port), end="") - comm = CommUDP(udp_ip, udp_port) - elif args.pcie: - from litex.soc.tools.remote import CommPCIe - pcie_bar = args.pcie_bar - if args.pcie_bar is None: - print("Need to speficy --pcie-bar, exiting.") - exit() - print("[CommPCIe] bar: {} / ".format(args.pcie_bar), end="") - comm = CommPCIe(args.pcie_bar) - elif args.usb: - from litex.soc.tools.remote import CommUSB - if args.usb_pid is None and args.usb_vid is None: - print("Need to speficy --usb-vid or --usb-pid, exiting.") - exit() - print("[CommUSB] vid: {} / pid: {} / ".format(args.usb_vid, args.usb_pid), end="") - pid = args.usb_pid - if pid is not None: - pid = int(pid, base=0) - vid = args.usb_vid - if vid is not None: - vid = int(vid, base=0) - comm = CommUSB(vid=vid, pid=pid, max_retries=args.usb_max_retries) - else: - parser.print_help() - exit() - - server = RemoteServer(comm, args.bind_ip, int(args.bind_port)) - server.open() - server.start(4) - try: - import time - while True: time.sleep(100) - except KeyboardInterrupt: - pass - -if __name__ == "__main__": - main() diff --git a/litex/utils/litex_sim.py b/litex/utils/litex_sim.py deleted file mode 100755 index 808bd8b9..00000000 --- a/litex/utils/litex_sim.py +++ /dev/null @@ -1,255 +0,0 @@ -#!/usr/bin/env python3 - -import argparse - -from migen import * -from migen.genlib.io import CRG - -from litex.build.generic_platform import * -from litex.build.sim import SimPlatform -from litex.build.sim.config import SimConfig - -from litex.soc.integration.soc_core import * -from litex.soc.integration.soc_sdram import * -from litex.soc.integration.builder import * -from litex.soc.cores import uart -from litex.soc.integration.soc_core import mem_decoder - -from litedram.common import PhySettings -from litedram.modules import MT48LC16M16 -from litedram.phy.model import SDRAMPHYModel - -from liteeth.common import convert_ip -from liteeth.phy.model import LiteEthPHYModel -from liteeth.core.mac import LiteEthMAC -from liteeth.core import LiteEthUDPIPCore -from liteeth.frontend.etherbone import LiteEthEtherbone - -from litescope import LiteScopeAnalyzer - - -class SimPins(Pins): - def __init__(self, n=1): - Pins.__init__(self, "s "*n) - - -_io = [ - ("sys_clk", 0, SimPins(1)), - ("sys_rst", 0, SimPins(1)), - ("serial", 0, - Subsignal("source_valid", SimPins()), - Subsignal("source_ready", SimPins()), - Subsignal("source_data", SimPins(8)), - - Subsignal("sink_valid", SimPins()), - Subsignal("sink_ready", SimPins()), - Subsignal("sink_data", SimPins(8)), - ), - ("eth_clocks", 0, - Subsignal("none", SimPins()), - ), - ("eth", 0, - Subsignal("source_valid", SimPins()), - Subsignal("source_ready", SimPins()), - Subsignal("source_data", SimPins(8)), - - Subsignal("sink_valid", SimPins()), - Subsignal("sink_ready", SimPins()), - Subsignal("sink_data", SimPins(8)), - ), - ("eth_clocks", 1, - Subsignal("none", SimPins()), - ), - ("eth", 1, - Subsignal("source_valid", SimPins()), - Subsignal("source_ready", SimPins()), - Subsignal("source_data", SimPins(8)), - - Subsignal("sink_valid", SimPins()), - Subsignal("sink_ready", SimPins()), - Subsignal("sink_data", SimPins(8)), - ), -] - - -class Platform(SimPlatform): - default_clk_name = "sys_clk" - default_clk_period = 1000 # ~ 1MHz - - def __init__(self): - SimPlatform.__init__(self, "SIM", _io) - - def do_finalize(self, fragment): - pass - - -class SimSoC(SoCSDRAM): - csr_peripherals = [ - "ethphy", - "ethmac", - - "etherbonephy", - "etherbonecore", - - "analyzer", - ] - csr_map_update(SoCSDRAM.csr_map, csr_peripherals) - - interrupt_map = { - "ethmac": 3, - } - interrupt_map.update(SoCSDRAM.interrupt_map) - - mem_map = { - "ethmac": 0x30000000, # (shadow @0xb0000000) - } - mem_map.update(SoCSDRAM.mem_map) - - def __init__(self, - with_sdram=False, - with_ethernet=False, - with_etherbone=False, etherbone_mac_address=0x10e2d5000000, etherbone_ip_address="192.168.1.50", - with_analyzer=False, - **kwargs): - platform = Platform() - sys_clk_freq = int(1e6) - SoCSDRAM.__init__(self, platform, clk_freq=sys_clk_freq, - integrated_rom_size=0x8000, - ident="LiteX Simulation", ident_version=True, - with_uart=False, - **kwargs) - # crg - self.submodules.crg = CRG(platform.request("sys_clk")) - - # serial - self.submodules.uart_phy = uart.RS232PHYModel(platform.request("serial")) - self.submodules.uart = uart.UART(self.uart_phy) - - # sdram - if with_sdram: - sdram_module = MT48LC16M16(100e6, "1:1") # use 100MHz timings - phy_settings = PhySettings( - memtype="SDR", - dfi_databits=16, - nphases=1, - rdphase=0, - wrphase=0, - rdcmdphase=0, - wrcmdphase=0, - cl=2, - read_latency=4, - write_latency=0 - ) - self.submodules.sdrphy = SDRAMPHYModel(sdram_module, phy_settings) - self.register_sdram( - self.sdrphy, - sdram_module.geom_settings, - sdram_module.timing_settings) - # reduce memtest size for simulation speedup - self.add_constant("MEMTEST_DATA_SIZE", 8*1024) - self.add_constant("MEMTEST_ADDR_SIZE", 8*1024) - - assert not (with_ethernet and with_etherbone) # FIXME: fix simulator with 2 ethernet interfaces - - # ethernet - if with_ethernet: - # eth phy - self.submodules.ethphy = LiteEthPHYModel(self.platform.request("eth", 0)) - # eth mac - ethmac = LiteEthMAC(phy=self.ethphy, dw=32, - interface="wishbone", endianness=self.cpu.endianness) - if with_etherbone: - ethmac = ClockDomainsRenamer({"eth_tx": "ethphy_eth_tx", "eth_rx": "ethphy_eth_rx"})(ethmac) - self.submodules.ethmac = ethmac - self.add_wb_slave(mem_decoder(self.mem_map["ethmac"]), self.ethmac.bus) - self.add_memory_region("ethmac", self.mem_map["ethmac"] | self.shadow_base, 0x2000) - - # etherbone - if with_etherbone: - # eth phy - self.submodules.etherbonephy = LiteEthPHYModel(self.platform.request("eth", 0)) # FIXME - # eth core - etherbonecore = LiteEthUDPIPCore(self.etherbonephy, - etherbone_mac_address, convert_ip(etherbone_ip_address), sys_clk_freq) - if with_ethernet: - etherbonecore = ClockDomainsRenamer({"eth_tx": "etherbonephy_eth_tx", "eth_rx": "etherbonephy_eth_rx"})(etherbonecore) - self.submodules.etherbonecore = etherbonecore - # etherbone - self.submodules.etherbone = LiteEthEtherbone(self.etherbonecore.udp, 1234, mode="master") - self.add_wb_master(self.etherbone.wishbone.bus) - - # analyzer - if with_analyzer: - analyzer_signals = [ - # FIXME: find interesting signals to probe - self.cpu.ibus, - self.cpu.dbus - ] - self.submodules.analyzer = LiteScopeAnalyzer(analyzer_signals, 512) - - -def main(): - parser = argparse.ArgumentParser(description="Generic LiteX SoC Simulation") - builder_args(parser) - soc_sdram_args(parser) - parser.add_argument("--threads", default=1, - help="set number of threads (default=1)") - parser.add_argument("--rom-init", default=None, - help="rom_init file") - parser.add_argument("--ram-init", default=None, - help="ram_init file") - parser.add_argument("--with-sdram", action="store_true", - help="enable SDRAM support") - parser.add_argument("--with-ethernet", action="store_true", - help="enable Ethernet support") - parser.add_argument("--with-etherbone", action="store_true", - help="enable Etherbone support") - parser.add_argument("--with-analyzer", action="store_true", - help="enable Analyzer support") - parser.add_argument("--trace", action="store_true", - help="enable VCD tracing") - args = parser.parse_args() - - soc_kwargs = soc_sdram_argdict(args) - builder_kwargs = builder_argdict(args) - - sim_config = SimConfig(default_clk="sys_clk") - sim_config.add_module("serial2console", "serial") - - cpu_endianness = "big" - if "cpu_type" in soc_kwargs: - if soc_kwargs["cpu_type"] in ["picorv32", "vexriscv"]: - cpu_endianness = "little" - - if args.rom_init: - soc_kwargs["integrated_rom_init"] = get_mem_data(args.rom_init, cpu_endianness) - if not args.with_sdram: - soc_kwargs["integrated_main_ram_size"] = 0x10000000 # 256 MB - if args.ram_init is not None: - soc_kwargs["integrated_main_ram_init"] = get_mem_data(args.ram_init, cpu_endianness) - else: - assert args.ram_init is None - soc_kwargs["integrated_main_ram_size"] = 0x0 - if args.with_ethernet: - sim_config.add_module("ethernet", "eth", args={"interface": "tap0", "ip": "192.168.1.100"}) - if args.with_etherbone: - sim_config.add_module('ethernet', "eth", args={"interface": "tap1", "ip": "192.168.1.101"}) - - soc = SimSoC( - with_sdram=args.with_sdram, - with_ethernet=args.with_ethernet, - with_etherbone=args.with_etherbone, - with_analyzer=args.with_analyzer, - **soc_kwargs) - if args.ram_init is not None: - soc.add_constant("ROM_BOOT_ADDRESS", 0x40000000) - builder_kwargs["csr_csv"] = "csr.csv" - builder = Builder(soc, **builder_kwargs) - vns = builder.build(run=False, threads=args.threads, sim_config=sim_config, trace=args.trace) - if args.with_analyzer: - soc.analyzer.export_csv(vns, "analyzer.csv") - builder.build(build=False, threads=args.threads, sim_config=sim_config, trace=args.trace) - - -if __name__ == "__main__": - main() diff --git a/litex/utils/litex_term.py b/litex/utils/litex_term.py deleted file mode 100755 index 0a7341ad..00000000 --- a/litex/utils/litex_term.py +++ /dev/null @@ -1,319 +0,0 @@ -#!/usr/bin/env python3 - -import sys -import os -import time -import serial -import threading -import argparse - - -if sys.platform == "win32": - import msvcrt - class Console: - def configure(self): - pass - - def unconfigure(self): - pass - - def getkey(self): - return msvcrt.getch() -else: - import termios - class Console: - def __init__(self): - self.fd = sys.stdin.fileno() - self.default_settings = termios.tcgetattr(self.fd) - - def configure(self): - settings = termios.tcgetattr(self.fd) - settings[3] = settings[3] & ~termios.ICANON & ~termios.ECHO - settings[6][termios.VMIN] = 1 - settings[6][termios.VTIME] = 0 - termios.tcsetattr(self.fd, termios.TCSANOW, settings) - - def unconfigure(self): - termios.tcsetattr(self.fd, termios.TCSAFLUSH, self.default_settings) - - def getkey(self): - return os.read(self.fd, 1) - - -sfl_prompt_req = b"F7: boot from serial\n" -sfl_prompt_ack = b"\x06" - -sfl_magic_req = b"sL5DdSMmkekro\n" -sfl_magic_ack = b"z6IHG7cYDID6o\n" - -# General commands -sfl_cmd_abort = b"\x00" -sfl_cmd_load = b"\x01" -sfl_cmd_jump = b"\x02" - -# Replies -sfl_ack_success = b"K" -sfl_ack_crcerror = b"C" -sfl_ack_unknown = b"U" -sfl_ack_error = b"E" - - -crc16_table = [ - 0x0000, 0x1021, 0x2042, 0x3063, 0x4084, 0x50A5, 0x60C6, 0x70E7, - 0x8108, 0x9129, 0xA14A, 0xB16B, 0xC18C, 0xD1AD, 0xE1CE, 0xF1EF, - 0x1231, 0x0210, 0x3273, 0x2252, 0x52B5, 0x4294, 0x72F7, 0x62D6, - 0x9339, 0x8318, 0xB37B, 0xA35A, 0xD3BD, 0xC39C, 0xF3FF, 0xE3DE, - 0x2462, 0x3443, 0x0420, 0x1401, 0x64E6, 0x74C7, 0x44A4, 0x5485, - 0xA56A, 0xB54B, 0x8528, 0x9509, 0xE5EE, 0xF5CF, 0xC5AC, 0xD58D, - 0x3653, 0x2672, 0x1611, 0x0630, 0x76D7, 0x66F6, 0x5695, 0x46B4, - 0xB75B, 0xA77A, 0x9719, 0x8738, 0xF7DF, 0xE7FE, 0xD79D, 0xC7BC, - 0x48C4, 0x58E5, 0x6886, 0x78A7, 0x0840, 0x1861, 0x2802, 0x3823, - 0xC9CC, 0xD9ED, 0xE98E, 0xF9AF, 0x8948, 0x9969, 0xA90A, 0xB92B, - 0x5AF5, 0x4AD4, 0x7AB7, 0x6A96, 0x1A71, 0x0A50, 0x3A33, 0x2A12, - 0xDBFD, 0xCBDC, 0xFBBF, 0xEB9E, 0x9B79, 0x8B58, 0xBB3B, 0xAB1A, - 0x6CA6, 0x7C87, 0x4CE4, 0x5CC5, 0x2C22, 0x3C03, 0x0C60, 0x1C41, - 0xEDAE, 0xFD8F, 0xCDEC, 0xDDCD, 0xAD2A, 0xBD0B, 0x8D68, 0x9D49, - 0x7E97, 0x6EB6, 0x5ED5, 0x4EF4, 0x3E13, 0x2E32, 0x1E51, 0x0E70, - 0xFF9F, 0xEFBE, 0xDFDD, 0xCFFC, 0xBF1B, 0xAF3A, 0x9F59, 0x8F78, - 0x9188, 0x81A9, 0xB1CA, 0xA1EB, 0xD10C, 0xC12D, 0xF14E, 0xE16F, - 0x1080, 0x00A1, 0x30C2, 0x20E3, 0x5004, 0x4025, 0x7046, 0x6067, - 0x83B9, 0x9398, 0xA3FB, 0xB3DA, 0xC33D, 0xD31C, 0xE37F, 0xF35E, - 0x02B1, 0x1290, 0x22F3, 0x32D2, 0x4235, 0x5214, 0x6277, 0x7256, - 0xB5EA, 0xA5CB, 0x95A8, 0x8589, 0xF56E, 0xE54F, 0xD52C, 0xC50D, - 0x34E2, 0x24C3, 0x14A0, 0x0481, 0x7466, 0x6447, 0x5424, 0x4405, - 0xA7DB, 0xB7FA, 0x8799, 0x97B8, 0xE75F, 0xF77E, 0xC71D, 0xD73C, - 0x26D3, 0x36F2, 0x0691, 0x16B0, 0x6657, 0x7676, 0x4615, 0x5634, - 0xD94C, 0xC96D, 0xF90E, 0xE92F, 0x99C8, 0x89E9, 0xB98A, 0xA9AB, - 0x5844, 0x4865, 0x7806, 0x6827, 0x18C0, 0x08E1, 0x3882, 0x28A3, - 0xCB7D, 0xDB5C, 0xEB3F, 0xFB1E, 0x8BF9, 0x9BD8, 0xABBB, 0xBB9A, - 0x4A75, 0x5A54, 0x6A37, 0x7A16, 0x0AF1, 0x1AD0, 0x2AB3, 0x3A92, - 0xFD2E, 0xED0F, 0xDD6C, 0xCD4D, 0xBDAA, 0xAD8B, 0x9DE8, 0x8DC9, - 0x7C26, 0x6C07, 0x5C64, 0x4C45, 0x3CA2, 0x2C83, 0x1CE0, 0x0CC1, - 0xEF1F, 0xFF3E, 0xCF5D, 0xDF7C, 0xAF9B, 0xBFBA, 0x8FD9, 0x9FF8, - 0x6E17, 0x7E36, 0x4E55, 0x5E74, 0x2E93, 0x3EB2, 0x0ED1, 0x1EF0 -] - - -def crc16(l): - crc = 0 - for d in l: - crc = crc16_table[((crc >> 8) ^ d) & 0xff] ^ (crc << 8) - return crc & 0xffff - - -class SFLFrame: - def __init__(self): - self.cmd = bytes() - self.payload = bytes() - - def compute_crc(self): - return crc16(self.cmd + self.payload) - - def encode(self): - packet = bytes([len(self.payload)]) - packet += self.compute_crc().to_bytes(2, "big") - packet += self.cmd - packet += self.payload - return packet - - -class LiteXTerm: - def __init__(self, serial_boot, kernel_image, kernel_address): - self.serial_boot = serial_boot - self.kernel_image = kernel_image - self.kernel_address = kernel_address - - self.reader_alive = False - self.writer_alive = False - - self.prompt_detect_buffer = bytes(len(sfl_prompt_req)) - self.magic_detect_buffer = bytes(len(sfl_magic_req)) - - self.console = Console() - - def open(self, port, baudrate): - if hasattr(self, "port"): - return - self.port = serial.serial_for_url(port, baudrate) - - def close(self): - if not hasattr(self, "port"): - return - self.port.close() - del self.port - - def send_frame(self, frame): - retry = 1 - while retry: - self.port.write(frame.encode()) - # Get the reply from the device - reply = self.port.read() - if reply == sfl_ack_success: - retry = 0 - elif reply == sfl_ack_crcerror: - retry = 1 - else: - print("[TERM] Got unknown reply '{}' from the device, aborting.".format(reply)) - return 0 - return 1 - - def upload(self, filename, address): - with open(filename, "rb") as f: - data = f.read() - print("[TERM] Uploading {} ({} bytes)...".format(filename, len(data))) - current_address = address - position = 0 - length = len(data) - start = time.time() - while len(data): - sys.stdout.write("|{}>{}| {}%\r".format('=' * (20*position//length), - ' ' * (20-20*position//length), - 100*position//length)) - sys.stdout.flush() - frame = SFLFrame() - frame_data = data[:251] - frame.cmd = sfl_cmd_load - frame.payload = current_address.to_bytes(4, "big") - frame.payload += frame_data - if self.send_frame(frame) == 0: - return - current_address += len(frame_data) - position += len(frame_data) - try: - data = data[251:] - except: - data = [] - end = time.time() - elapsed = end - start - print("[TERM] Upload complete ({0:.1f}KB/s).".format(length/(elapsed*1024))) - return length - - def boot(self): - print("[TERM] Booting the device.") - frame = SFLFrame() - frame.cmd = sfl_cmd_jump - frame.payload = self.kernel_address.to_bytes(4, "big") - self.send_frame(frame) - - def detect_prompt(self, data): - if len(data): - self.prompt_detect_buffer = self.prompt_detect_buffer[1:] + data - return self.prompt_detect_buffer == sfl_prompt_req - else: - return False - - def answer_prompt(self): - print("[TERM] Received serial boot prompt from the device.") - self.port.write(sfl_prompt_ack) - - def detect_magic(self, data): - if len(data): - self.magic_detect_buffer = self.magic_detect_buffer[1:] + data - return self.magic_detect_buffer == sfl_magic_req - else: - return False - - def answer_magic(self): - print("[TERM] Received firmware download request from the device.") - if os.path.exists(self.kernel_image): - self.port.write(sfl_magic_ack) - self.upload(self.kernel_image, self.kernel_address) - self.boot() - print("[TERM] Done."); - - def reader(self): - try: - while self.reader_alive: - c = self.port.read() - if c == b"\r": - sys.stdout.buffer.write(b"\n") - else: - sys.stdout.buffer.write(c) - sys.stdout.flush() - - if self.kernel_image is not None: - if self.serial_boot and self.detect_prompt(c): - self.answer_prompt() - if self.detect_magic(c): - self.answer_magic() - - except serial.SerialException: - self.reader_alive = False - raise - - def start_reader(self): - self.reader_alive = True - self.reader_thread = threading.Thread(target=self.reader) - self.reader_thread.setDaemon(True) - self.reader_thread.start() - - def stop_reader(self): - self.reader_alive = False - self.reader_thread.join() - - def writer(self): - try: - while self.writer_alive: - b = self.console.getkey() - if b == b"\x03": - self.stop() - elif b == b"\n": - self.port.write(b"\x0a") - else: - self.port.write(b) - except: - self.writer_alive = False - raise - - def start_writer(self): - self.writer_alive = True - self.writer_thread = threading.Thread(target=self.writer) - self.writer_thread.setDaemon(True) - self.writer_thread.start() - - def stop_writer(self): - self.writer_alive = False - self.writer_thread.join() - - def start(self): - print("[TERM] Starting....") - self.start_reader() - self.start_writer() - - def stop(self): - self.reader_alive = False - self.writer_alive = False - - def join(self, writer_only=False): - self.writer_thread.join() - if not writer_only: - self.reader_thread.join() - - -def _get_args(): - parser = argparse.ArgumentParser() - parser.add_argument("port", help="serial port") - parser.add_argument("--speed", default=115200, help="serial baudrate") - parser.add_argument("--serial-boot", default=False, action='store_true', - help="automatically initiate serial boot") - parser.add_argument("--kernel", default=None, help="kernel image") - parser.add_argument("--kernel-adr", type=lambda a: int(a, 0), default=0x40000000, help="kernel address") - return parser.parse_args() - - -def main(): - args = _get_args() - term = LiteXTerm(args.serial_boot, args.kernel, args.kernel_adr) - term.console.configure() - try: - term.open(args.port, args.speed) - term.start() - term.join(True) - except KeyboardInterrupt: - term.console.unconfigure() - finally: - term.console.unconfigure() - term.close() - -if __name__ == "__main__": - main() diff --git a/setup.py b/setup.py index ed0dd4d3..9039037c 100755 --- a/setup.py +++ b/setup.py @@ -36,11 +36,10 @@ setup( include_package_data=True, entry_points={ "console_scripts": [ - "mkmscimg=litex.soc.tools.mkmscimg:main", - "litex_term=litex.utils.litex_term:main", - "litex_server=litex.utils.litex_server:main", - "litex_sim=litex.utils.litex_sim:main", - "litex_read_verilog=litex.utils.litex_read_verilog:main", + "litex_term=litex.tools.litex_term:main", + "litex_server=litex.tools.litex_server:main", + "litex_sim=litex.tools.litex_sim:main", + "litex_read_verilog=litex.tools.litex_read_verilog:main", "litex_simple=litex.boards.targets.simple:main", ], },