Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
95 changes: 83 additions & 12 deletions chipflow_digital_ip/io/i2c.py
Original file line number Diff line number Diff line change
Expand Up @@ -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))
Expand All @@ -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)

Expand Down
98 changes: 83 additions & 15 deletions chipflow_digital_ip/io/spi.py
Original file line number Diff line number Diff line change
Expand Up @@ -111,40 +111,108 @@ 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))
chip_select: csr.Field(csr.action.RW, unsigned(1))
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)

Expand Down
2 changes: 2 additions & 0 deletions chipflow_digital_ip/io/uart.py
Original file line number Diff line number Diff line change
Expand Up @@ -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`
Expand Down
64 changes: 64 additions & 0 deletions chipflow_digital_ip/memory/qspi_flash.py
Original file line number Diff line number Diff line change
Expand Up @@ -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)


Expand Down
Loading