91 lines
3.2 KiB
Python
91 lines
3.2 KiB
Python
from nmigen import *
|
|
from nmigen_stdio import serial
|
|
from nmigen_soc import csr
|
|
from nmigen.utils import *
|
|
|
|
|
|
__all__ = ["UARTCore"]
|
|
|
|
|
|
class UARTCore(Elaboratable):
|
|
def __init__(self, uart_pins, *, sys_clk_freq, bus_data_width,
|
|
baudrate=115200, divisor_bits=32, bus_granularity=None):
|
|
self.uart_pins = uart_pins
|
|
if divisor_bits > 32:
|
|
raise NotImplementedError("Currently only supports divisor values "
|
|
"not wider than 32 bits")
|
|
self.div_bits = divisor_bits
|
|
self.div = int(sys_clk_freq/baudrate) - 1
|
|
|
|
bus_dw = bus_data_width
|
|
if bus_granularity is None:
|
|
bus_granularity = bus_data_width
|
|
bus_gr = bus_granularity
|
|
|
|
with csr.Bank(name="uart", addr_width=log2_int(4*bus_dw//bus_gr),
|
|
data_width=bus_gr) as self.csr:
|
|
self.csr.r += [
|
|
csr.Register(
|
|
"control", "rw", width=bus_dw,
|
|
fields=[csr.Field("divisor", "rw", width=self.div_bits, reset_value=self.div)]
|
|
),
|
|
csr.Register(
|
|
"flags", "rw", width=bus_dw,
|
|
fields=[csr.Field("rx_ready", "r", startbit=0, reset_value=0),
|
|
csr.Field("tx_ready", "r", startbit=4, reset_value=1),
|
|
csr.Field("rx_enable", "rw", startbit=8)]
|
|
),
|
|
csr.Register(
|
|
"txdata", "w", width=bus_dw, fields=[csr.Field("value", width=8)]
|
|
),
|
|
csr.Register(
|
|
"rxdata", "r", width=bus_dw, fields=[csr.Field("value", width=8)]
|
|
)
|
|
]
|
|
self.wb2csr = csr.WishboneCSRBridge(self.csr.dec.bus, data_width=bus_data_width)
|
|
self.csr_bus = self.wb2csr.wb_bus
|
|
|
|
def elaborate(self, platform):
|
|
m = Module()
|
|
|
|
m.submodules.rxtx = rxtx = serial.AsyncSerial(
|
|
divisor=self.div, divisor_bits=self.div_bits, pins=self.uart_pins
|
|
)
|
|
rx = rxtx.rx
|
|
tx = rxtx.tx
|
|
|
|
m.submodules += self.csr, self.wb2csr
|
|
|
|
# CSR - Divisor
|
|
m.d.comb += rxtx.divisor.eq(self.csr.r.control.f.divisor.s)
|
|
|
|
# CSR - RX/TX
|
|
tx_ack_now = Signal()
|
|
m.d.sync += tx_ack_now.eq(self.csr.r.txdata.bus.w_stb)
|
|
|
|
# RX Ready Flag is read-once-and-clear
|
|
with m.If(self.csr.r.flags.bus.r_stb):
|
|
m.d.comb += [
|
|
self.csr.r.flags.f.rx_ready.set_stb.eq(1),
|
|
self.csr.r.flags.f.rx_ready.set_val.eq(0)
|
|
]
|
|
with m.Elif(rx.r_rdy):
|
|
m.d.comb += [
|
|
self.csr.r.flags.f.rx_ready.set_stb.eq(1),
|
|
self.csr.r.flags.f.rx_ready.set_val.eq(1)
|
|
]
|
|
|
|
m.d.comb += [
|
|
# TX
|
|
self.csr.r.flags.f.tx_ready.set_stb.eq(1),
|
|
self.csr.r.flags.f.tx_ready.set_val.eq(~tx.busy),
|
|
tx.ack.eq(tx_ack_now),
|
|
tx.data.eq(self.csr.r.txdata.f.value.s),
|
|
# RX
|
|
self.csr.r.rxdata.f.value.set_stb.eq(1),
|
|
self.csr.r.rxdata.f.value.set_val.eq(rx.data),
|
|
rx.ack.eq(self.csr.r.flags.f.rx_enable.s)
|
|
]
|
|
|
|
return m
|