Loading files
This file is pinned to the selected release. Download the original to open it in your design tool.
2e0eaead3This file is pinned to the selected release. Download the original to open it in your design tool.
#
# This file is part of LiteX.
#
# Copyright (c) 2015-2020 Florent Kermarrec <florent@enjoy-digital.fr>
# SPDX-License-Identifier: BSD-2-Clause
import serial
import struct
from litex.tools.remote.csr_builder import CSRBuilder
# Constants ----------------------------------------------------------------------------------------
CMD_WRITE_BURST_INCR = 0x01
CMD_READ_BURST_INCR = 0x02
CMD_WRITE_BURST_FIXED = 0x03
CMD_READ_BURST_FIXED = 0x04
UARTBONE_MAX_BURST_LENGTH = 255
UARTBONE_WRITE_TIMEOUT = 0.100
UARTBONE_WRITE_TIMEOUT_MARGIN = 0.5
# CommUART -----------------------------------------------------------------------------------------
class CommUART(CSRBuilder):
def __init__(self, port, baudrate=115200, csr_csv=None, debug=False, addr_width=32):
CSRBuilder.__init__(self, comm=self, csr_csv=csr_csv)
self.baudrate = int(float(baudrate))
self.debug = debug
self.addr_bytes = addr_width // 8
self.max_write_burst_length = self._get_max_write_burst_length(
baudrate = self.baudrate,
addr_bytes = self.addr_bytes,
)
self.port = serial.serial_for_url(port, self.baudrate)
@staticmethod
def _get_max_write_burst_length(baudrate, addr_bytes):
if baudrate <= 0:
raise ValueError("baudrate must be greater than 0.")
# UARTBone's gateware timeout is 100ms while outside RECEIVE-CMD.
# Keep half of that budget for serial transmission and leave the rest
# for Wishbone accesses and host/USB adapter jitter.
timeout = UARTBONE_WRITE_TIMEOUT*UARTBONE_WRITE_TIMEOUT_MARGIN
bytes_time = 10/baudrate # 1 start bit + 8 data bits + 1 stop bit.
max_bytes = int(timeout/bytes_time)
max_words = (max_bytes - (2 + addr_bytes)) // 4
return max(1, min(UARTBONE_MAX_BURST_LENGTH, max_words))
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, burst="incr"):
self._flush()
data = []
length_int = 1 if length is None else length
cmd = {
"incr" : CMD_READ_BURST_INCR,
"fixed": CMD_READ_BURST_FIXED,
}[burst]
self._write([cmd, length_int])
self._write(list((addr//4).to_bytes(self.addr_bytes, byteorder="big")))
raw = self._read(4*length_int)
for i in range(length_int):
value = int.from_bytes(raw[4*i:4*(i + 1)], "big")
if self.debug:
print("read 0x{:08x} @ 0x{:08x}".format(value, addr + 4*i))
if length is None:
return value
data.append(value)
return data
def write(self, addr, data, burst="incr"):
self._flush()
data = data if isinstance(data, list) else [data]
length = len(data)
offset = 0
incr = burst == "incr"
cmd = {
"incr" : CMD_WRITE_BURST_INCR,
"fixed": CMD_WRITE_BURST_FIXED,
}[burst]
while length:
size = min(length, self.max_write_burst_length)
burst_addr = addr//4 + incr*offset
self._write([cmd, size])
self._write(list((burst_addr).to_bytes(self.addr_bytes, byteorder="big")))
payload = bytearray()
for i, value in enumerate(data[offset:offset+size]):
payload += value.to_bytes(4, byteorder="big")
if self.debug:
debug_addr = addr + 4*(offset + i) if incr else addr
print("write 0x{:08x} @ 0x{:08x}".format(value, debug_addr))
self._write(payload)
offset += size
length -= size