gateware: add UART to CSR

This commit is contained in:
David Lenfesty 2023-03-24 17:42:27 -06:00
parent 32fb8383d1
commit 96dabe013a
5 changed files with 46 additions and 21 deletions

View File

@ -41,7 +41,7 @@ class I2C(Elaboratable):
# [3]: read - read a byte from the bus
# [4]: read_ack - ACK value that gets written out during a read operation
# [5]: read_ack_en - Hacky solution to determine if we want to save read_ack
self.CR = Element(6, Element.Access.W, name="CR")
self.CR = Element(6, Element.Access.W, name="I2C_CR")
# Status register
#
@ -49,27 +49,28 @@ class I2C(Elaboratable):
# [0]: busy - bus is busy operating
# [1]: ack - an ACK has been received from a bus slave
# [2]: read_ack - a convenience read field to see value of CR->read_ack
self.SR = Element(3, Element.Access.R, name="SR")
self.SR = Element(3, Element.Access.R, name="I2C_SR")
# Data write register
#
# Latches in data to be written when write signal is applied.
self.DWR = Element(8, Element.Access.W, name="DWR")
self.DWR = Element(8, Element.Access.W, name="I2C_DWR")
# Data read register
#
# Only presents valid data after 'read' has started, and once 'busy' is no longer asserted.
self.DRR = Element(8, Element.Access.R, name="DRR")
self.DRR = Element(8, Element.Access.R, name="I2C_DRR")
# Set up CSR bus
addr_width = ceil(log2(64)) # Support up to 64 registers just because
data_width = 8 # 32 bit bus
self._csr_mux = Multiplexer(addr_width=addr_width, data_width=data_width)
# TODO export the addresses of these somehow
self._csr_mux.add(self.CR)
self._csr_mux.add(self.SR)
self._csr_mux.add(self.DWR)
self._csr_mux.add(self.DRR)
# TODO export these addresses into some config file
cr_start, _stop = self._csr_mux.add(self.CR)
sr_start, _stop = self._csr_mux.add(self.SR)
dwr_start, _stop = self._csr_mux.add(self.DWR)
drr_start, _stop = self._csr_mux.add(self.DRR)
print(f"I2C added. CR 0x{cr_start:x}, SR 0x{sr_start:x}, DWR 0x{dwr_start:x}, DRR 0x{drr_start:x}")
self.bus = self._csr_mux.bus
# Set up I2C initiator submodule

View File

@ -146,7 +146,6 @@ class Core(Elaboratable):
print(f"CSR bus added at 0x{start:08x}")
# I2C (connected to DAC for VCO and ADC?)
Signal()
if platform is not None:
i2c_pads = platform.request("i2c")
else:
@ -158,7 +157,20 @@ class Core(Elaboratable):
m.d.comb += i2c_pads.scl.i.eq(1)
self.i2c = i2c.I2C(50e6, 100e3, i2c_pads)
m.submodules.i2c = self.i2c
self.csr.add(self.i2c.bus)
i2c_start, _stop, _step = self.csr.add(self.i2c.bus)
print(f"LED added to CSR at 0x{i2c_start}")
if platform is not None:
uart_pads = platform.request("uart")
else:
uart_pads = type('UARTPads', (), {})
uart_pads.tx = Signal()
uart_pads.rx = Signal()
# TODO spread sysclk freq through design
self.uart = uart.UART(50e6, 1152_000)
m.submodules.uart = self.uart
uart_start, _stop, _step = self.csr.add(self.uart.bus)
print(f"UART added to CSR at 0x{uart_start:x}")
self.csr_bridge = WishboneCSRBridge(self.csr.bus, data_width=32, name="CSR")
m.submodules.csr_bridge = self.csr_bridge

View File

@ -1 +1 @@
from colorlight_i9 import *
from .colorlight_i9 import *

View File

@ -25,8 +25,18 @@ class Colorlight_i9_Platform(LatticeECP5Platform):
# attrs=Attrs(IO_TYPE="LVCMOS33", PULLMODE="UP")),
UARTResource(0,
tx="J17",
rx="H18",
tx="E17",
rx="D18",
attrs=Attrs(IO_TYPE="LVCMOS33")
),
UARTResource(1,
tx="P16",
rx="L5",
attrs=Attrs(IO_TYPE="LVCMOS33")
),
UARTResource(2,
tx="J18",
rx="J16",
attrs=Attrs(IO_TYPE="LVCMOS33")
),

View File

@ -38,7 +38,7 @@ class UART(Elaboratable):
#
# Sets input/output baudrate to system clock / divisor. Resets to value
# that provides 115200 baud rate. Writes to this register clear FIFOs.
self.DIVISOR = Element(16, Element.Access.RW, name="DIVISOR")
self.DIVISOR = Element(16, Element.Access.RW, name="UART_DIVISOR")
# Status register.
#
@ -47,22 +47,23 @@ class UART(Elaboratable):
# [1]: txfifo_empty
# [2]: rxfifo_full
# [3]: rxfifo_empty
self.SR = Element(4, Element.Access.R, name="SR")
self.SR = Element(4, Element.Access.R, name="UART_SR")
# Data register.
#
# Writes push data into TX FIFO, and are discarded if full, reads pull
# data from RX FIFO, and are invalid if it is empty. Incoming bytes are discarded
# if the RX FIFO is full.
self.DR = Element(8, Element.Access.RW, name="DR")
self.DR = Element(8, Element.Access.RW, name="UART_DR")
# Set up CSR bus
addr_width = ceil(log2(64))
data_width = 32
data_width = 8
self._csr_mux = Multiplexer(addr_width=addr_width, data_width=data_width)
self._csr_mux.add(self.DIVISOR)
self._csr_mux.add(self.SR)
self._csr_mux.add(self.DR)
div_start, _stop = self._csr_mux.add(self.DIVISOR)
sr_start, _stop = self._csr_mux.add(self.SR)
dr_start, _stop = self._csr_mux.add(self.DR)
print(f"UART added. DIVISOR 0x{div_start:x}, SR 0x{sr_start:x}, DR 0x{dr_start:x}")
self.bus = self._csr_mux.bus
# Actual business logic
@ -94,6 +95,7 @@ class UART(Elaboratable):
m.submodules.csr_mux = self._csr_mux
# Hook up divisor to register.
# TODO do some validation and write a known good value if a dumb value was provided
m.d.comb += self.DIVISOR.r_data.eq(self._serial.divisor)
with m.If(self.DIVISOR.w_stb):
m.d.sync += self._serial.divisor.eq(self.DIVISOR.w_data)