move init'ing UARTInterface from config to classmethod
This commit is contained in:
parent
75a0fe46ff
commit
a1fddf555e
|
|
@ -63,7 +63,7 @@ class Manta(Elaboratable):
|
||||||
|
|
||||||
def get_interface(self):
|
def get_interface(self):
|
||||||
if "uart" in self.config:
|
if "uart" in self.config:
|
||||||
return UARTInterface(self.config["uart"])
|
return UARTInterface.from_config(self.config["uart"])
|
||||||
|
|
||||||
elif "ethernet" in self.config:
|
elif "ethernet" in self.config:
|
||||||
return EthernetInterface(self.config["ethernet"])
|
return EthernetInterface(self.config["ethernet"])
|
||||||
|
|
|
||||||
|
|
@ -8,26 +8,31 @@ from serial import Serial
|
||||||
|
|
||||||
|
|
||||||
class UARTInterface(Elaboratable):
|
class UARTInterface(Elaboratable):
|
||||||
def __init__(self, config):
|
def __init__(self, port, baudrate, clock_freq, chunk_size=256):
|
||||||
self.config = config
|
self._port = port
|
||||||
self.check_config(self.config)
|
self._baudrate = baudrate
|
||||||
|
self._clock_freq = clock_freq
|
||||||
self.port = config["port"]
|
self._chunk_size = chunk_size
|
||||||
self.clock_freq = config["clock_freq"]
|
self._clocks_per_baud = int(self._clock_freq // self._baudrate)
|
||||||
self.baudrate = config["baudrate"]
|
self._check_config()
|
||||||
self.clocks_per_baud = int(self.clock_freq // self.baudrate)
|
|
||||||
|
|
||||||
self.define_signals()
|
|
||||||
|
|
||||||
# Set chunk_size, which is the max amount of bytes that the core will
|
# Set chunk_size, which is the max amount of bytes that the core will
|
||||||
# dump to the OS driver at a time. Since the FPGA will return bytes
|
# dump to the OS driver at a time. Since the FPGA will return bytes
|
||||||
# almost instantaneously, this prevents the OS's input buffer from
|
# almost instantaneously, this prevents the OS's input buffer from
|
||||||
# overflowing, and dropping bytes.
|
# overflowing, and dropping bytes.
|
||||||
self.chunk_size = 256 # in bytes
|
|
||||||
if "chunk_size" in config:
|
|
||||||
self.chunk_size = config["chunk_size"]
|
|
||||||
|
|
||||||
def check_config(self, config):
|
self.rx = Signal()
|
||||||
|
self.tx = Signal()
|
||||||
|
|
||||||
|
self.bus_o = Signal(InternalBus())
|
||||||
|
self.bus_i = Signal(InternalBus())
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def from_config(cls, config):
|
||||||
|
port = config.get("port")
|
||||||
|
clock_freq = config.get("clock_freq")
|
||||||
|
baudrate = config.get("baudrate")
|
||||||
|
|
||||||
# Warn if unrecognized options have been given
|
# Warn if unrecognized options have been given
|
||||||
recognized_options = ["port", "clock_freq", "baudrate", "chunk_size"]
|
recognized_options = ["port", "clock_freq", "baudrate", "chunk_size"]
|
||||||
for option in config:
|
for option in config:
|
||||||
|
|
@ -36,49 +41,53 @@ class UARTInterface(Elaboratable):
|
||||||
f"Ignoring unrecognized option '{option}' in UART interface config."
|
f"Ignoring unrecognized option '{option}' in UART interface config."
|
||||||
)
|
)
|
||||||
|
|
||||||
|
if "chunk_size" in config:
|
||||||
|
return cls(port, baudrate, clock_freq, config["chunk_size"])
|
||||||
|
|
||||||
|
else:
|
||||||
|
return cls(port, baudrate, clock_freq)
|
||||||
|
|
||||||
|
def _check_config(self):
|
||||||
# Ensure a serial port has been given
|
# Ensure a serial port has been given
|
||||||
if "port" not in config:
|
if self._port is None:
|
||||||
raise ValueError("No serial port provided to UART interface.")
|
raise ValueError("No serial port provided to UART interface.")
|
||||||
|
|
||||||
# Ensure clock frequency is provided and positive
|
# Ensure clock frequency is provided and positive
|
||||||
if "clock_freq" not in config:
|
if self._clock_freq is None:
|
||||||
raise ValueError("No clock frequency provided to UART interface.")
|
raise ValueError("No clock frequency provided to UART interface.")
|
||||||
|
|
||||||
if config["clock_freq"] <= 0:
|
if self._clock_freq <= 0:
|
||||||
raise ValueError("Non-positive clock frequency provided to UART interface.")
|
raise ValueError("Non-positive clock frequency provided to UART interface.")
|
||||||
|
|
||||||
# Check that baudrate is provided and positive
|
# Check that baudrate is provided and positive
|
||||||
if "baudrate" not in config:
|
if self._baudrate is None:
|
||||||
raise ValueError("No baudrate provided to UART interface.")
|
raise ValueError("No baudrate provided to UART interface.")
|
||||||
|
|
||||||
if config["baudrate"] <= 0:
|
if self._baudrate <= 0:
|
||||||
raise ValueError("Non-positive baudrate provided to UART interface.")
|
raise ValueError("Non-positive baudrate provided to UART interface.")
|
||||||
|
|
||||||
# Confirm the actual baudrate is within 5% of the target baudrate
|
# Confirm the actual baudrate is within 5% of the target baudrate
|
||||||
clock_freq = config["clock_freq"]
|
actual_baudrate = self._clock_freq / self._clocks_per_baud
|
||||||
baudrate = config["baudrate"]
|
error = 100 * abs(actual_baudrate - self._baudrate) / self._baudrate
|
||||||
clocks_per_baud = clock_freq // baudrate
|
|
||||||
actual_baudrate = clock_freq / clocks_per_baud
|
|
||||||
error = 100 * abs(actual_baudrate - baudrate) / baudrate
|
|
||||||
|
|
||||||
if error > 5:
|
if error > 5:
|
||||||
raise ValueError(
|
raise ValueError(
|
||||||
"UART interface is unable to match targeted baudrate with specified clock frequency."
|
"UART interface is unable to match targeted baudrate with specified clock frequency."
|
||||||
)
|
)
|
||||||
|
|
||||||
def get_serial_device(self):
|
def _get_serial_device(self):
|
||||||
"""
|
"""
|
||||||
Return an open PySerial serial device if one exists, otherwise, open one.
|
Return an open PySerial serial device if one exists, otherwise, open one.
|
||||||
"""
|
"""
|
||||||
if hasattr(self, "serial_device"):
|
|
||||||
return self.serial_device
|
|
||||||
|
|
||||||
else:
|
# Check if we've already opened a device
|
||||||
if self.port != "auto":
|
if hasattr(self, "_serial_device"):
|
||||||
self.serial_device = Serial(self.port, self.baudrate, timeout=1)
|
return self._serial_device
|
||||||
return self.serial_device
|
|
||||||
|
if self._port != "auto":
|
||||||
|
self._serial_device = Serial(self._port, self._baudrate, timeout=1)
|
||||||
|
return self._serial_device
|
||||||
|
|
||||||
else:
|
|
||||||
# Try to autodetect which port to use based on the PID/VID of the device attached.
|
# Try to autodetect which port to use based on the PID/VID of the device attached.
|
||||||
# This looks for the PID/VID of the FT2232, the primary chip used on the icestick
|
# This looks for the PID/VID of the FT2232, the primary chip used on the icestick
|
||||||
# and Digilent dev boards. However, folks will likely want to connect other things
|
# and Digilent dev boards. However, folks will likely want to connect other things
|
||||||
|
|
@ -112,8 +121,8 @@ class UARTInterface(Elaboratable):
|
||||||
else:
|
else:
|
||||||
chosen_port = ports[1].device
|
chosen_port = ports[1].device
|
||||||
|
|
||||||
self.serial_device = Serial(chosen_port, self.baudrate, timeout=1)
|
self._serial_device = Serial(chosen_port, self._baudrate, timeout=1)
|
||||||
return self.serial_device
|
return self._serial_device
|
||||||
|
|
||||||
def get_top_level_ports(self):
|
def get_top_level_ports(self):
|
||||||
return [self.rx, self.tx]
|
return [self.rx, self.tx]
|
||||||
|
|
@ -133,8 +142,8 @@ class UARTInterface(Elaboratable):
|
||||||
raise ValueError("Read address must be an integer or list of integers.")
|
raise ValueError("Read address must be an integer or list of integers.")
|
||||||
|
|
||||||
# Send read requests, and get responses
|
# Send read requests, and get responses
|
||||||
ser = self.get_serial_device()
|
ser = self._get_serial_device()
|
||||||
addr_chunks = split_into_chunks(addrs, self.chunk_size)
|
addr_chunks = split_into_chunks(addrs, self._chunk_size)
|
||||||
datas = []
|
datas = []
|
||||||
|
|
||||||
for addr_chunk in addr_chunks:
|
for addr_chunk in addr_chunks:
|
||||||
|
|
@ -152,7 +161,7 @@ class UARTInterface(Elaboratable):
|
||||||
|
|
||||||
# Split received bytes into individual responses and decode
|
# Split received bytes into individual responses and decode
|
||||||
responses = split_into_chunks(bytes_in, 7)
|
responses = split_into_chunks(bytes_in, 7)
|
||||||
data_chunk = [self.decode_read_response(r) for r in responses]
|
data_chunk = [self._decode_read_response(r) for r in responses]
|
||||||
datas += data_chunk
|
datas += data_chunk
|
||||||
|
|
||||||
return datas
|
return datas
|
||||||
|
|
@ -186,10 +195,10 @@ class UARTInterface(Elaboratable):
|
||||||
# Encode addrs and datas into write requests
|
# Encode addrs and datas into write requests
|
||||||
bytes_out = "".join([f"W{a:04X}{d:04X}\r\n" for a, d in zip(addrs, datas)])
|
bytes_out = "".join([f"W{a:04X}{d:04X}\r\n" for a, d in zip(addrs, datas)])
|
||||||
bytes_out = bytes_out.encode("ascii")
|
bytes_out = bytes_out.encode("ascii")
|
||||||
ser = self.get_serial_device()
|
ser = self._get_serial_device()
|
||||||
ser.write(bytes_out)
|
ser.write(bytes_out)
|
||||||
|
|
||||||
def decode_read_response(self, response_bytes):
|
def _decode_read_response(self, response_bytes):
|
||||||
"""
|
"""
|
||||||
Check that read response is formatted properly, and extract the encoded data if so.
|
Check that read response is formatted properly, and extract the encoded data if so.
|
||||||
"""
|
"""
|
||||||
|
|
@ -221,21 +230,14 @@ class UARTInterface(Elaboratable):
|
||||||
|
|
||||||
return int(response_ascii[1:5], 16)
|
return int(response_ascii[1:5], 16)
|
||||||
|
|
||||||
def define_signals(self):
|
|
||||||
self.rx = Signal()
|
|
||||||
self.tx = Signal()
|
|
||||||
|
|
||||||
self.bus_o = Signal(InternalBus())
|
|
||||||
self.bus_i = Signal(InternalBus())
|
|
||||||
|
|
||||||
def elaborate(self, platform):
|
def elaborate(self, platform):
|
||||||
# fancy submoduling and such goes in here
|
# fancy submoduling and such goes in here
|
||||||
m = Module()
|
m = Module()
|
||||||
|
|
||||||
m.submodules.uart_rx = uart_rx = UARTReceiver(self.clocks_per_baud)
|
m.submodules.uart_rx = uart_rx = UARTReceiver(self._clocks_per_baud)
|
||||||
m.submodules.bridge_rx = bridge_rx = ReceiveBridge()
|
m.submodules.bridge_rx = bridge_rx = ReceiveBridge()
|
||||||
m.submodules.bridge_tx = bridge_tx = TransmitBridge()
|
m.submodules.bridge_tx = bridge_tx = TransmitBridge()
|
||||||
m.submodules.uart_tx = uart_tx = UARTTransmitter(self.clocks_per_baud)
|
m.submodules.uart_tx = uart_tx = UARTTransmitter(self._clocks_per_baud)
|
||||||
|
|
||||||
m.d.comb += [
|
m.d.comb += [
|
||||||
# UART RX -> Internal Bus
|
# UART RX -> Internal Bus
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue