#
# This file is part of LUNA.
#
# Copyright (c) 2020-2025 Great Scott Gadgets <info@greatscottgadgets.com>
# SPDX-License-Identifier: BSD-3-Clause
from amaranth import *
from amaranth.lib import wiring
from amaranth.lib.wiring import In, Out, flipped, connect
from amaranth_soc import csr
from amaranth_stdio.serial import AsyncSerialRX, AsyncSerialTX
__all__ = ["PinSignature", "Peripheral"]
[docs]
class PinSignature(wiring.Signature):
"""UART pin signature.
Interface attributes
--------------------
tx : :class:`Signal`
Output.
rx : :class:`Signal`
Input.
"""
def __init__(self):
super().__init__({
"tx": Out(
wiring.Signature({
"o" : Out(unsigned(1)),
"oe" : Out(unsigned(1)),
})
),
"rx": In(unsigned(1)),
})
[docs]
class Peripheral(wiring.Component):
[docs]
class TxData(csr.Register, access="w"):
"""valid to write to when tx_rdy is high, will trigger a transmit"""
data: csr.Field(csr.action.W, unsigned(8))
[docs]
class RxData(csr.Register, access="r"):
"""valid to read from when rx_avail is high, last received byte"""
data: csr.Field(csr.action.R, unsigned(8))
[docs]
class TxReady(csr.Register, access="r"):
"""is '1' when 1-byte transmit buffer is empty"""
txe: csr.Field(csr.action.R, unsigned(1))
[docs]
class RxAvail(csr.Register, access="r"):
"""is '1' when 1-byte receive buffer is full; reset by a read from rx_data"""
rxe: csr.Field(csr.action.R, unsigned(1))
[docs]
class BaudRate(csr.Register, access="rw"):
"""baud rate divider, defaults to init"""
def __init__(self, init):
super().__init__({
"div": csr.Field(csr.action.RW, unsigned(24), init=init),
})
"""A minimal UART."""
def __init__(self, *, divisor):
self._init_divisor = divisor
regs = csr.Builder(addr_width=5, data_width=8)
self._tx_data = regs.add("tx_data", self.TxData(), offset=0x00)
self._rx_data = regs.add("rx_data", self.RxData(), offset=0x04)
self._tx_ready = regs.add("tx_ready", self.TxReady(), offset=0x08)
self._rx_avail = regs.add("rx_avail", self.RxAvail(), offset=0x0c)
self._divisor = regs.add("divisor", self.BaudRate(init=self._init_divisor), offset=0x10)
# bridge
self._bridge = csr.Bridge(regs.as_memory_map())
super().__init__({
"bus": In(csr.Signature(addr_width=regs.addr_width, data_width=regs.data_width)),
"pins": Out(PinSignature()),
})
self.bus.memory_map = self._bridge.bus.memory_map
[docs]
def elaborate(self, platform):
m = Module()
m.submodules.bridge = self._bridge
connect(m, flipped(self.bus), self._bridge.bus)
m.submodules.tx = tx = AsyncSerialTX(divisor=self._init_divisor, divisor_bits=24)
m.d.comb += [
self.pins.tx.oe.eq(~self._tx_ready.f.txe.r_data),
self.pins.tx.o.eq(tx.o),
tx.data.eq(self._tx_data.f.data.w_data),
tx.ack.eq(self._tx_data.f.data.w_stb),
self._tx_ready.f.txe.r_data.eq(tx.rdy),
tx.divisor.eq(self._divisor.f.div.data)
]
rx_buf = Signal(unsigned(8))
rx_avail = Signal()
m.submodules.rx = rx = AsyncSerialRX(divisor=self._init_divisor, divisor_bits=24)
with m.If(self._rx_data.f.data.r_stb):
m.d.sync += rx_avail.eq(0)
with m.If(rx.rdy):
m.d.sync += [
rx_buf.eq(rx.data),
rx_avail.eq(1)
]
m.d.comb += [
rx.i.eq(self.pins.rx),
rx.ack.eq(~rx_avail),
rx.divisor.eq(self._divisor.f.div.data),
self._rx_data.f.data.r_data.eq(rx_buf),
self._rx_avail.f.rxe.r_data.eq(rx_avail)
]
return m