diff --git a/chipflow_digital_ip/io/i2c.py b/chipflow_digital_ip/io/i2c.py index 9df8132..279ac01 100644 --- a/chipflow_digital_ip/io/i2c.py +++ b/chipflow_digital_ip/io/i2c.py @@ -15,17 +15,47 @@ class I2CPeripheral(wiring.Component): + """ + A minimal I2C controller wrapping the Glasgow core + + Support for customisable clock frequency and byte-wise transfers. + """ + class Divider(csr.Register, access="rw"): - """I2C SCK clock divider, 1 = divide by 4""" + """Divider register. + + This :class:`Register` is used to configure the clock frequency of the I2C peripheral. + + The SCK frequency is the input clock frequency divided by 4 times the value in this register. + For example, for a SCK of 1/12 the system clock, this register would be set to 3. + """ val: csr.Field(csr.action.RW, unsigned(12)) class Action(csr.Register, access="w"): - """ - reset: reset the core, e.g. in case of a bus lockup - start: write 1 to trigger I2C start - stop: write 1 to trigger I2C stop - read_ack: write 1 to trigger I2C read and ACK - read_nack: write 1 to trigger I2C read and NACK + """Action register. + + This :class:`Register` is written to in order to perform various actions on the I2C bus. + Writing ``0b1`` to a field performs that action, it is only valid to set one field at a time. + + It has the following fields: + + .. bitfield:: + :bits: 8 + + [ + { "name": "reset", "bits": 1, "attr": "W" }, + { "name": "start", "bits": 1, "attr": "W" }, + { "name": "stop", "bits": 1, "attr": "W" }, + { "name": "read_ack", "bits": 1, "attr": "W" }, + { "name": "read_nack", "bits": 1, "attr": "W" }, + { "bits": 3, "attr": "ResR0W0" }, + ] + + - The ``reset`` field is used to reset the PHY in case of a lock-up (e.g. SCK stuck low) + - The ``start`` field sends an I2C start + - The ``stop`` field sends an I2C stop + - The ``read_ack`` field begins an I2C read, followed by an ACK + - The ``read_nack`` field begins an I2C read, followed by a NACK """ reset: csr.Field(csr.action.W, unsigned(1)) start: csr.Field(csr.action.W, unsigned(1)) @@ -35,20 +65,61 @@ class Action(csr.Register, access="w"): class SendData(csr.Register, access="w"): - """writes the given data onto the I2C bus when written to""" + """SendData register. + + Writing to this :class:`Register` sends a byte on the I2C bus. + + It has the following fields: + + .. bitfield:: + :bits: 8 + + [ + { "name": "val", "bits": 8, "attr": "W" }, + ] + + """ val: csr.Field(csr.action.W, unsigned(8)) class ReceiveData(csr.Register, access="r"): - """data received from the last read""" + """ReceiveData register. + + This :class:`Register` contains the result of the last read started using `read_ack` or `read_nack`. + + It has the following fields: + + .. bitfield:: + :bits: 8 + + [ + { "name": "val", "bits": 8, "attr": "R" }, + ] + + """ val: csr.Field(csr.action.R, unsigned(8)) class Status(csr.Register, access="r"): + """Status register. + + This :class:`Register` contains the status of the peripheral. + + It has the following fields: + + .. bitfield:: + :bits: 8 + + [ + { "name": "busy", "bits": 1, "attr": "R" }, + { "name": "ack", "bits": 1, "attr": "R" }, + { "bits": 6, "attr": "ResR0" }, + ] + + - The ``busy`` field is set when the PHY is currently performing an action, and unable to accept any requests. + - The ``ack`` field contains the ACK/NACK value of the last write. + """ busy: csr.Field(csr.action.R, unsigned(1)) ack: csr.Field(csr.action.R, unsigned(1)) - """ - A minimal I2C controller wrapping the Glasgow core - """ def __init__(self): regs = csr.Builder(addr_width=5, data_width=8) diff --git a/chipflow_digital_ip/io/spi.py b/chipflow_digital_ip/io/spi.py index e6a083e..8f32e7b 100644 --- a/chipflow_digital_ip/io/spi.py +++ b/chipflow_digital_ip/io/spi.py @@ -111,14 +111,36 @@ def elaborate(self, platform): class SPIPeripheral(wiring.Component): + """ + A custom, minimal SPI controller + + Transfers up to a width of 32 bits are supported, as are all SPI modes. + """ + class Config(csr.Register, access="rw"): - """ - sck_idle: idle state of sck, '1' to invert sck - sck_edge: - 1 to latch output on rising sck edge, read input on falling sck edge - 0 to read input on rising sck edge, latch output on falling sck edge - chip_select: write '1' to assert (bring low) chip select output - width: width of transfer, minus 1 + """Config register. + + This :class:`Register` is written to in order to configure the SPI mode and transaction. + + It has the following fields: + + .. bitfield:: + :bits: 8 + + [ + { "name": "sck_idle", "bits": 1, "attr": "RW" }, + { "name": "sck_edge", "bits": 1, "attr": "RW" }, + { "name": "chip_select", "bits": 1, "attr": "RW" }, + { "name": "width", "bits": 5, "attr": "RW" }, + ] + + - The ``sck_idle`` field is used to invert the SCK polarity, ``0b0`` for an idle low SCK, ``0b1`` for an idle high SCK + - The ``sck_edge`` field selects which edge is used for input and output data. + - ``0b0`` to read input on rising SCK edge, latch output on falling SCK edge + - ``0b1`` to latch output on rising SCK edge, read input on falling SCK edge + - The ``chip_select`` field controls the CS pin; setting it to ``0b1`` asserts (brings low) chip select. + - The ``width`` field configures the width of the transfer. It is set to the width of the transfer minus 1, + ``31`` gives the maximum width of 32. """ sck_idle: csr.Field(csr.action.RW, unsigned(1)) sck_edge: csr.Field(csr.action.RW, unsigned(1)) @@ -126,25 +148,71 @@ class Config(csr.Register, access="rw"): width: csr.Field(csr.action.RW, unsigned(5)) class Divider(csr.Register, access="rw"): - """SPI SCK clock divider, 1 = divide by 4""" + """Divider register. + + This :class:`Register` is used to configure the clock frequency of the I2C peripheral. + + The SCK frequency is the input clock frequency divided by 4 times the value in this register. + For example, for a SCK of 1/12 the system clock, this register would be set to 3. + """ val: csr.Field(csr.action.RW, unsigned(8)) class SendData(csr.Register, access="w"): - """data to transmit, must be left justified (bits [31..32-N] used)""" + """SendData register. + + Writing to this :class:`Register` starts a read/write transfer on the SPI bus, the width of which is configured in `Config`. + + It has the following fields: + + .. bitfield:: + :bits: 32 + + [ + { "name": "val", "bits": 32, "attr": "W" }, + ] + + - The `val` field must be left-justified, so for a transfer of ``N`` bits, bits ``[31..32-N]`` are used. + """ + val: csr.Field(csr.action.W, unsigned(32)) class ReceiveData(csr.Register, access="r"): - """data received, is right justified (bits [N-1..0] used)""" + """ReceiveData register. + + This :class:`Register` contains the read data of the last transfer started with a write to ``SendData``. + + It has the following fields: + + .. bitfield:: + :bits: 8 + + [ + { "name": "val", "bits": 32, "attr": "R" }, + ] + + - The `val` field is right-justified, so for a transfer of ``N`` bits, bits ``[N-1..0]`` are used. + """ val: csr.Field(csr.action.R, unsigned(32)) class Status(csr.Register, access="r"): - """recv_full is 1 when transfer has been completed. reset to zero by reading receive_data""" - recv_full: csr.Field(csr.action.R, unsigned(1)) + """Status register. + This :class:`Register` contains the status of the peripheral. + + It has the following fields: + + .. bitfield:: + :bits: 8 + + [ + { "name": "recv_full", "bits": 1, "attr": "R" }, + { "bits": 7, "attr": "ResR0" }, + ] + + - The ``recv_full`` field is set to ``0b1`` when a transfer is completed. It is reset to zero by reading ``ReceiveData``. + """ + recv_full: csr.Field(csr.action.R, unsigned(1)) - """ - A custom, minimal SPI controller - """ def __init__(self): regs = csr.Builder(addr_width=5, data_width=8) diff --git a/chipflow_digital_ip/io/uart.py b/chipflow_digital_ip/io/uart.py index 79dc5cf..b47f227 100644 --- a/chipflow_digital_ip/io/uart.py +++ b/chipflow_digital_ip/io/uart.py @@ -130,6 +130,8 @@ def __init__(self): """Wrapper for amaranth_soc RFC UART with PHY and chipflow_lib.IOSignature support + See rfc_uart.py for detailed register documentation. + Parameters ---------- addr_width : :class:`int` diff --git a/chipflow_digital_ip/memory/qspi_flash.py b/chipflow_digital_ip/memory/qspi_flash.py index 1eef84d..0f145ce 100644 --- a/chipflow_digital_ip/memory/qspi_flash.py +++ b/chipflow_digital_ip/memory/qspi_flash.py @@ -53,20 +53,84 @@ def elaborate(self, platform): return m class WishboneQSPIFlashController(wiring.Component): + """ + A memory-mapped, XIP-capable controller for SPI, DSPI and QSPI flash memory modes. + + The controller starts off in XIP, simple SPI mode, for maximum flash compatibility + before configuration to support cases where the processor reset vector is directly + in SPI flash. + + As many flash memories require commands to enable quad modes or perform other + configuration, a raw mode is also supported enabling custom byte-wise SPI + transfers. This mode could also be used to reprogram the SPI flash. + + As the Wishbone interface is disabled in raw mode, it is important that any code + performing raw accesses is running in memory outside of the SPI flash (e.g. copied + to SRAM). + """ class Config(csr.Register, access="rw"): + """Config register. + + This :class:`Register` is written to in order to configure the SPI mode and transaction. + + It has the following fields: + + .. bitfield:: + :bits: 8 + + [ + { "name": "raw_enable", "bits": 1, "attr": "RW" }, + { "name": "width", "bits": 2, "attr": "RW" }, + { "name": "dummy_bytes", "bits": 2, "attr": "RW" }, + ] + + - The ``raw_enable`` field is used to perform raw SPI accesses. The Wishbone bus is disabled in this mode. + - The ``width`` field configures the SPI access mode: + - ``0b00``: X1 IO, standard read (`0x03` command) + - ``0b01``: X1 IO, fast read (`0x0B` command) + - ``0b10``: X2 IO, fast read (`0xBB` command) + - ``0b11``: X4 IO, fast read (`0xEB` command) + - The ``dummy_bytes`` field configures the number of dummy bytes between outputing the address and reading back data. + """ raw_enable: csr.Field(csr.action.RW, 1) width: csr.Field(csr.action.RW, QSPIFlashWidth) dummy_bytes: csr.Field(csr.action.RW, 2) class RawControl(csr.Register, access="rw"): + """Raw control register. + + This :class:`Register` is used to control accesses in raw mode. + + It has the following fields: + + .. bitfield:: + :bits: 8 + + [ + { "name": "ready", "bits": 1, "attr": "R" }, + { "name": "deselect", "bits": 1, "attr": "W" }, + { "name": "dummy_bytes", "bits": 2, "attr": "RW" }, + ] + + - The ``ready`` field is ``0b1`` when the core is ready to perform a raw mode transfer. + - Writing ``0b1`` to the ``deselect`` field will deselect the SPI bus (CS high), used at the end of a command. + """ ready: csr.Field(csr.action.R, 1) deselect: csr.Field(csr.action.W, 1) class RawTxData(csr.Register, access="rw"): + """Transmit data register. + + Writing a byte this :class:`Register` starts a 1-byte read/write transfer on the SPI bus in raw mode. + """ data: csr.Field(_RawTxDataField, 8) class RawRxData(csr.Register, access="rw"): + """Receive data register. + + This data contains the result of the last byte transfer in raw mode, which was started with a write to ``RawTxData``. + """ data: csr.Field(csr.action.R, 8)