From 5d46cb3368182fa4430427a64e77d1f4b8aa4fbd Mon Sep 17 00:00:00 2001 From: Rob Taylor Date: Tue, 16 Sep 2025 14:58:43 +0100 Subject: [PATCH 1/4] Fix typo in gpio tests --- chipflow_digital_ip/io/_gpio.py | 8 ++++---- tests/test_gpio.py | 7 +++---- 2 files changed, 7 insertions(+), 8 deletions(-) diff --git a/chipflow_digital_ip/io/_gpio.py b/chipflow_digital_ip/io/_gpio.py index ae4ae94..7c34f2c 100644 --- a/chipflow_digital_ip/io/_gpio.py +++ b/chipflow_digital_ip/io/_gpio.py @@ -38,9 +38,9 @@ class GPIOPeripheral(wiring.Component): Raises ------ :exc:`TypeError` - If ``pin_count`` is not a positive integer. - :exc:`TypeError` - If ``input_stages`` is not a non-negative integer. + If ``pin_count`` is not an integer. + :exc:`ValueError` + If ``pin_count`` is not in the supported range """ def __init__(self, *, pin_count, addr_width=4, data_width=8, input_stages=2): @@ -49,7 +49,7 @@ def __init__(self, *, pin_count, addr_width=4, data_width=8, input_stages=2): if pin_count > 32 or pin_count <= 0: # TODO: why? - raise ValueError(f"Pin pin_count must be a postive integrer less than 32, not {pin_count}") + raise ValueError(f"Pin pin_count must be a positive integer 32 or less, not {pin_count}") self._gpio = gpio.Peripheral(pin_count=pin_count, addr_width=addr_width, diff --git a/tests/test_gpio.py b/tests/test_gpio.py index da87ab4..e43ca50 100644 --- a/tests/test_gpio.py +++ b/tests/test_gpio.py @@ -30,16 +30,15 @@ def test_init_wrong_pin_count(self): r"Pin count must be a positive integer, not foo"): GPIOPeripheral(pin_count="foo", addr_width=2, data_width=8) with self.assertRaisesRegex(ValueError, - r"Pin pin_count must be a postive integrer less than 32, not -1"): + r"Pin pin_count must be a positive integer 32 or less, not -1"): GPIOPeripheral(pin_count=-1, addr_width=2, data_width=8) with self.assertRaisesRegex(ValueError, - r"Pin pin_count must be a postive integrer less than 32, not 0"): + r"Pin pin_count must be a positive integer 32 or less, not 0"): GPIOPeripheral(pin_count=0, addr_width=2, data_width=8) with self.assertRaisesRegex(ValueError, - r"Pin pin_count must be a postive integrer less than 32, not 33"): + r"Pin pin_count must be a positive integer 32 or less, not 33"): GPIOPeripheral(pin_count=33, addr_width=2, data_width=8) - def test_init_wrong_input_stages(self): with self.assertRaisesRegex(TypeError, r"Input stages must be a non-negative integer, not 'foo'"): From 801659b6da2c0b0aaa967dab54aaaa7fb2289a02 Mon Sep 17 00:00:00 2001 From: Rob Taylor Date: Tue, 16 Sep 2025 17:38:55 +0100 Subject: [PATCH 2/4] ci: Fix checking out of chipflow-examples --- .github/workflows/main.yaml | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/.github/workflows/main.yaml b/.github/workflows/main.yaml index d698b4e..48a35cc 100644 --- a/.github/workflows/main.yaml +++ b/.github/workflows/main.yaml @@ -62,13 +62,21 @@ jobs: with: submodules: true - - name: Check out chipflow-examples + - name: Check out chipflow-examples ${{ github.head_ref || 'refs/heads/main' }} uses: actions/checkout@v4 + continue-on-error: true with: repository: ChipFlow/chipflow-examples path: chipflow-examples ref: ${{ github.head_ref || 'refs/heads/main' }} + - name: Check out chipflow-examples main + uses: actions/checkout@v4 + if: ${{ failure() && steps.step2.conclusion == 'failure' }} }} + with: + repository: ChipFlow/chipflow-examples + path: chipflow-examples + - name: Set up PDM uses: pdm-project/setup-pdm@v4 with: From d0adecf8a85149998613cde546c48111cd403e67 Mon Sep 17 00:00:00 2001 From: Rob Taylor Date: Fri, 25 Jul 2025 17:09:02 +0100 Subject: [PATCH 3/4] Move drivers from chipflow-lib to alongside the IP. This makes use of the new `amaranth.lib.wiring.Signature` `chipflow_lib.platform.DriverSignature` to annotate the driver information for the IP --- chipflow_digital_ip/base/_platform_timer.py | 20 +++-- chipflow_digital_ip/base/_soc_id.py | 13 +++- chipflow_digital_ip/base/drivers/plat_timer.c | 14 ++++ chipflow_digital_ip/base/drivers/plat_timer.h | 17 +++++ chipflow_digital_ip/base/drivers/soc_id.h | 12 +++ chipflow_digital_ip/io/_gpio.py | 19 +++-- chipflow_digital_ip/io/_i2c.py | 20 +++-- chipflow_digital_ip/io/_spi.py | 20 +++-- chipflow_digital_ip/io/_uart.py | 19 +++-- chipflow_digital_ip/io/drivers/gpio.h | 48 ++++++++++++ chipflow_digital_ip/io/drivers/i2c.c | 32 ++++++++ chipflow_digital_ip/io/drivers/i2c.h | 21 ++++++ chipflow_digital_ip/io/drivers/spi.c | 18 +++++ chipflow_digital_ip/io/drivers/spi.h | 19 +++++ chipflow_digital_ip/io/drivers/uart.c | 35 +++++++++ chipflow_digital_ip/io/drivers/uart.h | 26 +++++++ chipflow_digital_ip/memory/_qspi_flash.py | 26 +++++-- chipflow_digital_ip/memory/drivers/spiflash.S | 58 +++++++++++++++ chipflow_digital_ip/memory/drivers/spiflash.c | 74 +++++++++++++++++++ chipflow_digital_ip/memory/drivers/spiflash.h | 22 ++++++ 20 files changed, 496 insertions(+), 37 deletions(-) create mode 100644 chipflow_digital_ip/base/drivers/plat_timer.c create mode 100644 chipflow_digital_ip/base/drivers/plat_timer.h create mode 100644 chipflow_digital_ip/base/drivers/soc_id.h create mode 100644 chipflow_digital_ip/io/drivers/gpio.h create mode 100644 chipflow_digital_ip/io/drivers/i2c.c create mode 100644 chipflow_digital_ip/io/drivers/i2c.h create mode 100644 chipflow_digital_ip/io/drivers/spi.c create mode 100644 chipflow_digital_ip/io/drivers/spi.h create mode 100644 chipflow_digital_ip/io/drivers/uart.c create mode 100644 chipflow_digital_ip/io/drivers/uart.h create mode 100644 chipflow_digital_ip/memory/drivers/spiflash.S create mode 100644 chipflow_digital_ip/memory/drivers/spiflash.c create mode 100644 chipflow_digital_ip/memory/drivers/spiflash.h diff --git a/chipflow_digital_ip/base/_platform_timer.py b/chipflow_digital_ip/base/_platform_timer.py index 0c50d9a..b819870 100644 --- a/chipflow_digital_ip/base/_platform_timer.py +++ b/chipflow_digital_ip/base/_platform_timer.py @@ -1,9 +1,10 @@ from amaranth import * from amaranth.lib import wiring from amaranth.lib.wiring import In, Out, flipped, connect - from amaranth_soc import csr +from chipflow_lib.platforms import SoftwareDriverSignature + __all__ = ["PlatformTimer"] @@ -34,10 +35,19 @@ def __init__(self): self._bridge = csr.Bridge(regs.as_memory_map()) - super().__init__({ - "bus": In(csr.Signature(addr_width=regs.addr_width, data_width=regs.data_width)), - "irq": Out(unsigned(1)), - }) + super().__init__( + SoftwareDriverSignature( + members={ + "bus": In(csr.Signature(addr_width=regs.addr_width, data_width=regs.data_width)), + "irq": Out(unsigned(1)), + }, + component=self, + regs_struct='plat_timer_regs_t', + c_files=['drivers/plat_timer.c'], + h_files=['drivers/plat_timer.h'], + ) + ) + self.bus.memory_map = self._bridge.bus.memory_map def elaborate(self, platform): diff --git a/chipflow_digital_ip/base/_soc_id.py b/chipflow_digital_ip/base/_soc_id.py index c8b2c21..350e4b4 100644 --- a/chipflow_digital_ip/base/_soc_id.py +++ b/chipflow_digital_ip/base/_soc_id.py @@ -6,6 +6,7 @@ from amaranth_soc import csr +from chipflow_lib.platforms import SoftwareDriverSignature __all__ = ["SoCID"] @@ -31,10 +32,16 @@ def __init__(self, *, type_id=0xbadca77e): self._soc_version = regs.add("soc_version", self.Register(32), offset=0x4) self._bridge = csr.Bridge(regs.as_memory_map()) + super().__init__( + SoftwareDriverSignature( + members={ + "bus": In(csr.Signature(addr_width=regs.addr_width, data_width=regs.data_width)), + }, + component=self, + regs_struct='soc_id_regs_t', + h_files=['drivers/soc_id.h']) + ) - super().__init__({ - "bus": In(csr.Signature(addr_width=regs.addr_width, data_width=regs.data_width)), - }) self.bus.memory_map = self._bridge.bus.memory_map def elaborate(self, platform): diff --git a/chipflow_digital_ip/base/drivers/plat_timer.c b/chipflow_digital_ip/base/drivers/plat_timer.c new file mode 100644 index 0000000..899e98c --- /dev/null +++ b/chipflow_digital_ip/base/drivers/plat_timer.c @@ -0,0 +1,14 @@ +/* SPDX-License-Identifier: BSD-2-Clause */ +#include "plat_timer.h" + +uint64_t plat_timer_read(volatile plat_timer_regs_t *timer) { + uint32_t cnt_lo = timer->cnt_lo; + __asm__ volatile ("" : : : "memory"); + return (((uint64_t)timer->cnt_hi) << 32ULL) | cnt_lo; +} + +void plat_timer_schedule(volatile plat_timer_regs_t *timer, uint64_t val) { + timer->cmp_lo = val & 0xFFFFFFFFU; + __asm__ volatile ("" : : : "memory"); + timer->cmp_hi = (val >> 32U) & 0xFFFFFFFFU; +} diff --git a/chipflow_digital_ip/base/drivers/plat_timer.h b/chipflow_digital_ip/base/drivers/plat_timer.h new file mode 100644 index 0000000..0c4eb33 --- /dev/null +++ b/chipflow_digital_ip/base/drivers/plat_timer.h @@ -0,0 +1,17 @@ +/* SPDX-License-Identifier: BSD-2-Clause */ +#ifndef PLAT_TIMER_H +#define PLAT_TIMER_H + +#include + +typedef struct __attribute__((packed, aligned(4))) { + uint32_t cnt_lo; + uint32_t cnt_hi; + uint32_t cmp_lo; + uint32_t cmp_hi; +} plat_timer_regs_t; + +uint64_t plat_timer_read(volatile plat_timer_regs_t *timer); +void plat_timer_schedule(volatile plat_timer_regs_t *timer, uint64_t val); + +#endif diff --git a/chipflow_digital_ip/base/drivers/soc_id.h b/chipflow_digital_ip/base/drivers/soc_id.h new file mode 100644 index 0000000..c0936b6 --- /dev/null +++ b/chipflow_digital_ip/base/drivers/soc_id.h @@ -0,0 +1,12 @@ +/* SPDX-License-Identifier: BSD-2-Clause */ +#ifndef SOC_ID_H +#define SOC_ID_H + +#include + +typedef struct __attribute__((packed, aligned(4))) { + uint32_t type; + uint32_t version; +} soc_id_regs_t; + +#endif diff --git a/chipflow_digital_ip/io/_gpio.py b/chipflow_digital_ip/io/_gpio.py index 7c34f2c..79f0f22 100644 --- a/chipflow_digital_ip/io/_gpio.py +++ b/chipflow_digital_ip/io/_gpio.py @@ -5,7 +5,8 @@ from amaranth_soc import csr, gpio -from chipflow_lib.platforms import GPIOSignature +from chipflow_lib.platforms import GPIOSignature, SoftwareDriverSignature + __all__ = ["GPIOPeripheral"] @@ -55,12 +56,18 @@ def __init__(self, *, pin_count, addr_width=4, data_width=8, input_stages=2): addr_width=addr_width, data_width=data_width, input_stages=input_stages) + super().__init__( + SoftwareDriverSignature( + members={ + "bus": In(csr.Signature(addr_width=addr_width, data_width=data_width)), + "pins": Out(GPIOSignature(pin_count)), + "alt_mode": Out(unsigned(pin_count)), + }, + component=self, + regs_struct='gpio_regs_t', + h_files=['drivers/gpio.h']) + ) - super().__init__({ - "bus": In(csr.Signature(addr_width=addr_width, data_width=data_width)), - "pins": Out(GPIOSignature(pin_count)), - "alt_mode": Out(unsigned(pin_count)), - }) self.bus.memory_map = self._gpio.bus.memory_map def elaborate(self, platform): diff --git a/chipflow_digital_ip/io/_i2c.py b/chipflow_digital_ip/io/_i2c.py index fa02ea0..37b80c7 100644 --- a/chipflow_digital_ip/io/_i2c.py +++ b/chipflow_digital_ip/io/_i2c.py @@ -3,7 +3,7 @@ from amaranth.lib.wiring import In, Out, connect, flipped from amaranth_soc import csr -from chipflow_lib.platforms import I2CSignature +from chipflow_lib.platforms import I2CSignature, SoftwareDriverSignature from ._glasgow_i2c import I2CInitiator __all__ = ["I2CPeripheral"] @@ -55,10 +55,20 @@ def __init__(self): self._bridge = csr.Bridge(regs.as_memory_map()) - super().__init__({ - "i2c_pins": Out(I2CSignature()), - "bus": In(csr.Signature(addr_width=regs.addr_width, data_width=regs.data_width)), - }) + super().__init__( + SoftwareDriverSignature( + members={ + "i2c_pins": Out(I2CSignature()), + "bus": In(csr.Signature(addr_width=regs.addr_width, data_width=regs.data_width)), + }, + component=self, + regs_struct='i2c_regs_t', + c_files=['drivers/i2c.c'], + h_files=['drivers/i2c.h'], + ) + + ) + self.bus.memory_map = self._bridge.bus.memory_map def elaborate(self, platform): diff --git a/chipflow_digital_ip/io/_spi.py b/chipflow_digital_ip/io/_spi.py index 576f960..e0c890a 100644 --- a/chipflow_digital_ip/io/_spi.py +++ b/chipflow_digital_ip/io/_spi.py @@ -3,10 +3,12 @@ from amaranth.lib.wiring import In, Out, connect, flipped from amaranth_soc import csr -from chipflow_lib.platforms import SPISignature +from chipflow_lib.platforms import SPISignature, SoftwareDriverSignature + __all__ = ["SPIPeripheral"] + class SPIController(wiring.Component): def __init__(self): super().__init__({ @@ -149,10 +151,18 @@ def __init__(self): self._bridge = csr.Bridge(regs.as_memory_map()) - super().__init__({ - "spi_pins": Out(SPISignature()), - "bus": In(csr.Signature(addr_width=regs.addr_width, data_width=regs.data_width)), - }) + super().__init__( + SoftwareDriverSignature( + members={ + "spi_pins": Out(SPISignature()), + "bus": In(csr.Signature(addr_width=regs.addr_width, data_width=regs.data_width)), + }, + component=self, + regs_struct='spi_regs_t', + c_files=['drivers/spi.c'], + h_files=['drivers/spi.h']) + ) + self.bus.memory_map = self._bridge.bus.memory_map def elaborate(self, platform): diff --git a/chipflow_digital_ip/io/_uart.py b/chipflow_digital_ip/io/_uart.py index 17b001f..a02babe 100644 --- a/chipflow_digital_ip/io/_uart.py +++ b/chipflow_digital_ip/io/_uart.py @@ -5,7 +5,7 @@ from amaranth_soc import csr from amaranth_stdio.serial import AsyncSerialRX, AsyncSerialTX -from chipflow_lib.platforms import UARTSignature +from chipflow_lib.platforms import UARTSignature, SoftwareDriverSignature from . import _rfc_uart @@ -117,7 +117,6 @@ def elaborate(self, platform): return m - class UARTPeripheral(wiring.Component): """Wrapper for amaranth_soc RFC UART with PHY and chipflow_lib.IOSignature support @@ -149,10 +148,18 @@ def __init__(self, *, addr_width=5, data_width=8, init_divisor=0): phy_config_init=phy_config_shape.const({"divisor": init_divisor}), ) - super().__init__({ - "bus": In(csr.Signature(addr_width=addr_width, data_width=data_width)), - "pins": Out(UARTSignature()), - }) + super().__init__( + SoftwareDriverSignature( + members={ + "bus": In(csr.Signature(addr_width=addr_width, data_width=data_width)), + "pins": Out(UARTSignature()), + }, + component=self, + regs_struct='uart_regs_t', + c_files=['drivers/uart.c'], + h_files=['drivers/uart.h']) + ) + self.bus.memory_map = self._uart.bus.memory_map self._phy = UARTPhy(ports=self.pins, init_divisor=init_divisor) diff --git a/chipflow_digital_ip/io/drivers/gpio.h b/chipflow_digital_ip/io/drivers/gpio.h new file mode 100644 index 0000000..5ba953e --- /dev/null +++ b/chipflow_digital_ip/io/drivers/gpio.h @@ -0,0 +1,48 @@ +/* SPDX-License-Identifier: BSD-2-Clause */ +#ifndef GPIO_H +#define GPIO_H + +#include + +typedef struct __attribute__((packed, aligned(2))) { + uint16_t mode; + uint8_t input; + uint8_t output; + uint16_t setclr; +} gpio_regs_t; + +typedef enum { +#define _GPIO_PIN(n) \ + GPIO_PIN ## n ## _INPUT_ONLY = (0 << 2 * (n)), \ + GPIO_PIN ## n ## _PUSH_PULL = (1 << 2 * (n)), \ + GPIO_PIN ## n ## _OPEN_DRAIN = (2 << 2 * (n)), \ + GPIO_PIN ## n ## _ALTERNATE = (3 << 2 * (n)) + + _GPIO_PIN(0), + _GPIO_PIN(1), + _GPIO_PIN(2), + _GPIO_PIN(3), + _GPIO_PIN(4), + _GPIO_PIN(5), + _GPIO_PIN(6), + _GPIO_PIN(7), +#undef _GPIO_PIN +} gpio_mode_t; + +typedef enum { +#define _GPIO_PIN(n) \ + GPIO_PIN ## n ## _SET = (1 << 2 * (n)), \ + GPIO_PIN ## n ## _CLEAR = (2 << 2 * (n)) + + _GPIO_PIN(0), + _GPIO_PIN(1), + _GPIO_PIN(2), + _GPIO_PIN(3), + _GPIO_PIN(4), + _GPIO_PIN(5), + _GPIO_PIN(6), + _GPIO_PIN(7), +#undef _GPIO_PIN +} gpio_setclr_t; + +#endif diff --git a/chipflow_digital_ip/io/drivers/i2c.c b/chipflow_digital_ip/io/drivers/i2c.c new file mode 100644 index 0000000..2e1bd2e --- /dev/null +++ b/chipflow_digital_ip/io/drivers/i2c.c @@ -0,0 +1,32 @@ +/* SPDX-License-Identifier: BSD-2-Clause */ +#include "i2c.h" + +void i2c_init(volatile i2c_regs_t *i2c, uint32_t divider) { + i2c->divider = divider; +} + +void i2c_start(volatile i2c_regs_t *i2c) { + i2c->action = (1<<1); + while (i2c->status & 0x1) + ; +} + +int i2c_write(volatile i2c_regs_t *i2c, uint8_t data) { + i2c->send_data = data; + while (i2c->status & 0x1) + ; + return (i2c->status & 0x2) != 0; // check ACK +} + +uint8_t i2c_read(volatile i2c_regs_t *i2c) { + i2c->action = (1<<3); + while (i2c->status & 0x1) + ; + return i2c->receive_data; +} + +void i2c_stop(volatile i2c_regs_t *i2c) { + i2c->action = (1<<2); + while (i2c->status & 0x1) + ; +} diff --git a/chipflow_digital_ip/io/drivers/i2c.h b/chipflow_digital_ip/io/drivers/i2c.h new file mode 100644 index 0000000..3f70865 --- /dev/null +++ b/chipflow_digital_ip/io/drivers/i2c.h @@ -0,0 +1,21 @@ +/* SPDX-License-Identifier: BSD-2-Clause */ +#ifndef I2C_H +#define I2C_H + +#include + +typedef struct { + uint32_t divider; + uint32_t action; + uint32_t send_data; + uint32_t receive_data; + uint32_t status; +} i2c_regs_t; + +void i2c_init(volatile i2c_regs_t *i2c, uint32_t divider); +void i2c_start(volatile i2c_regs_t *i2c); +int i2c_write(volatile i2c_regs_t *i2c, uint8_t data); +uint8_t i2c_read(volatile i2c_regs_t *i2c); +void i2c_stop(volatile i2c_regs_t *i2c); + +#endif diff --git a/chipflow_digital_ip/io/drivers/spi.c b/chipflow_digital_ip/io/drivers/spi.c new file mode 100644 index 0000000..951bcc0 --- /dev/null +++ b/chipflow_digital_ip/io/drivers/spi.c @@ -0,0 +1,18 @@ +/* SPDX-License-Identifier: BSD-2-Clause */ +#include "spi.h" + +void spi_init(volatile spi_regs_t *spi, uint32_t divider) { + spi->divider = divider; + spi->config = 0x02; // CS=0, SCK_EDGE=1, SCK_IDLE=0 +} + +uint32_t spi_xfer(volatile spi_regs_t *spi, uint32_t data, uint32_t width, bool deselect) { + spi->config = ((width - 1) << 3) | 0x06; // CS=1, SCK_EDGE=1, SCK_IDLE=0 + spi->send_data = data << (32U - width); + while (!(spi->status & 0x1)) // wait for rx full + ; + if (deselect) { + spi->config = ((width - 1) << 3) | 0x02; // CS=0, SCK_EDGE=1, SCK_IDLE=0 + } + return spi->receive_data; +} diff --git a/chipflow_digital_ip/io/drivers/spi.h b/chipflow_digital_ip/io/drivers/spi.h new file mode 100644 index 0000000..a4f4836 --- /dev/null +++ b/chipflow_digital_ip/io/drivers/spi.h @@ -0,0 +1,19 @@ +/* SPDX-License-Identifier: BSD-2-Clause */ +#ifndef SPI_H +#define SPI_H + +#include +#include + +typedef struct { + uint32_t config; + uint32_t divider; + uint32_t send_data; + uint32_t receive_data; + uint32_t status; +} spi_regs_t; + +void spi_init(volatile spi_regs_t *spi, uint32_t divider); +uint32_t spi_xfer(volatile spi_regs_t *spi, uint32_t data, uint32_t width, bool deselect); + +#endif diff --git a/chipflow_digital_ip/io/drivers/uart.c b/chipflow_digital_ip/io/drivers/uart.c new file mode 100644 index 0000000..ec1422e --- /dev/null +++ b/chipflow_digital_ip/io/drivers/uart.c @@ -0,0 +1,35 @@ +/* SPDX-License-Identifier: BSD-2-Clause */ +#include "uart.h" + +void uart_init(volatile uart_regs_t *uart, uint32_t divisor) { + uart->tx.config = 0; + uart->tx.phy_config = divisor & 0x00FFFFFF; + uart->tx.config = 1; + uart->rx.config = 0; + uart->rx.phy_config = divisor & 0x00FFFFFF; + uart->rx.config = 1; +}; + +void uart_putc(volatile uart_regs_t *uart, char c) { + if (c == '\n') + uart_putc(uart, '\r'); + while (!(uart->tx.status & 0x1)) + ; + uart->tx.data = c; +} + +void uart_puts(volatile uart_regs_t *uart, const char *s) { + while (*s != 0) + uart_putc(uart, *s++); +} + + +void uart_puthex(volatile uart_regs_t *uart, uint32_t x) { + for (int i = 7; i >= 0; i--) { + uint8_t nib = (x >> (4 * i)) & 0xF; + if (nib <= 9) + uart_putc(uart, '0' + nib); + else + uart_putc(uart, 'A' + (nib - 10)); + } +} diff --git a/chipflow_digital_ip/io/drivers/uart.h b/chipflow_digital_ip/io/drivers/uart.h new file mode 100644 index 0000000..90ab00e --- /dev/null +++ b/chipflow_digital_ip/io/drivers/uart.h @@ -0,0 +1,26 @@ +/* SPDX-License-Identifier: BSD-2-Clause */ +#ifndef UART_H +#define UART_H + +#include + +typedef struct __attribute__((packed, aligned(4))) { + uint8_t config; + uint8_t padding_0[3]; + uint32_t phy_config; + uint8_t status; + uint8_t data; + uint8_t padding_1[6]; +} uart_mod_regs_t; + +typedef struct __attribute__((packed, aligned(4))) { + uart_mod_regs_t rx; + uart_mod_regs_t tx; +} uart_regs_t; + +void uart_init(volatile uart_regs_t *uart, uint32_t divisor); +void uart_putc(volatile uart_regs_t *uart, char c); +void uart_puts(volatile uart_regs_t *uart, const char *s); +void uart_puthex(volatile uart_regs_t *uart, uint32_t x); + +#endif diff --git a/chipflow_digital_ip/memory/_qspi_flash.py b/chipflow_digital_ip/memory/_qspi_flash.py index eae4a57..94b9b9a 100644 --- a/chipflow_digital_ip/memory/_qspi_flash.py +++ b/chipflow_digital_ip/memory/_qspi_flash.py @@ -9,7 +9,7 @@ from ..io._glasgow_iostream import PortGroup from ..memory._glasgow_qspi import QSPIMode, QSPIController -from chipflow_lib.platforms import QSPIFlashSignature +from chipflow_lib.platforms import QSPIFlashSignature, SoftwareDriverSignature class QSPIFlashCommand(enum.Enum, shape=8): @@ -20,12 +20,14 @@ class QSPIFlashCommand(enum.Enum, shape=8): FastReadDualInOut = 0xBB FastReadQuadInOut = 0xEB + class QSPIFlashWidth(enum.Enum, shape=2): X1 = 0x00 X1Fast = 0x01 X2 = 0x02 X4 = 0x03 + class _RawTxDataField(csr.FieldAction): """A field that is read/write and exposes a strobe""" def __init__(self, shape, *, init=0): @@ -51,6 +53,7 @@ def elaborate(self, platform): return m + class WishboneQSPIFlashController(wiring.Component): class Config(csr.Register, access="rw"): @@ -68,7 +71,6 @@ class RawTxData(csr.Register, access="rw"): class RawRxData(csr.Register, access="rw"): data: csr.Field(csr.action.R, 8) - def __init__(self, *, addr_width, data_width): super().__init__({ "csr_bus": In(csr.Signature(addr_width=4, data_width=8)), @@ -247,13 +249,23 @@ def elaborate(self, platform): m.d.comb += self._raw_control.f.ready.r_data.eq(fsm.ongoing("Raw-Wait")) return m + class QSPIFlash(wiring.Component): def __init__(self, *, addr_width, data_width): - super().__init__({ - "pins": Out(QSPIFlashSignature()), - "csr_bus": In(csr.Signature(addr_width=4, data_width=8)), - "wb_bus": In(wishbone.Signature(addr_width=addr_width, data_width=data_width, granularity=8)), - }) + super().__init__( + SoftwareDriverSignature( + members={ + "pins": Out(QSPIFlashSignature()), + "csr_bus": In(csr.Signature(addr_width=4, data_width=8)), + "wb_bus": In(wishbone.Signature(addr_width=addr_width, data_width=data_width, granularity=8)), + }, + component=self, + regs_struct='spiflash_regs_t', + regs_bus="csr_bus", + c_files=['drivers/spiflash.c', 'drivers/spiflash.S'], + h_files=['drivers/spiflash.h'], + ) + ) self._ctrl = WishboneQSPIFlashController(addr_width=addr_width, data_width=data_width) self.csr_bus.memory_map = self._ctrl.csr_bus.memory_map diff --git a/chipflow_digital_ip/memory/drivers/spiflash.S b/chipflow_digital_ip/memory/drivers/spiflash.S new file mode 100644 index 0000000..4026be6 --- /dev/null +++ b/chipflow_digital_ip/memory/drivers/spiflash.S @@ -0,0 +1,58 @@ +.global flashio_worker_begin +.global flashio_worker_end + +.balign 4 + +flashio_worker_begin: +# a0 ... flash base address +# a1 ... data pointer +# a2 ... data length +# a3 ... optional WREN cmd (0 = disable) + +mv t3, ra + +# address of SPI ctrl reg +li a0, 0xb0000000 +# enter bypass mode +lbu t1, 0(a0) +ori t1, t1, 0x1 +sb t1, 0(a0) +call flashio_wait_bypass_ready + +beqz a3, flashio_xfer + +sb a3, 8(a0) # send wren +call flashio_wait_bypass_ready +li t1, 2 # deselect +sb t1, 4(a0) +call flashio_wait_bypass_ready + +flashio_xfer: +beqz a2, flashio_done +lbu t1, 0(a1) +sb t1, 8(a0) # tx data +call flashio_wait_bypass_ready +lbu t1, 12(a0) # rx data +sb t1, 0(a1) +addi a1, a1, 1 +addi a2, a2, -1 +j flashio_xfer + +flashio_done: +# exit bypass mode +lbu t1, 0(a0) +andi t1, t1, 0xFE +sb t1, 0(a0) + +fence.i +mv ra, t3 +ret + +flashio_wait_bypass_ready: +lbu t1, 4(a0) +andi t1, t1, 0x1 +beqz t1, flashio_wait_bypass_ready +ret + +.balign 4 +flashio_worker_end: diff --git a/chipflow_digital_ip/memory/drivers/spiflash.c b/chipflow_digital_ip/memory/drivers/spiflash.c new file mode 100644 index 0000000..fbf7b10 --- /dev/null +++ b/chipflow_digital_ip/memory/drivers/spiflash.c @@ -0,0 +1,74 @@ +/* SPDX-License-Identifier: BSD-2-Clause */ +#include +#include "spiflash.h" + +extern uint32_t flashio_worker_begin; +extern uint32_t flashio_worker_end; + +void spiflash_io(volatile spiflash_regs_t *flash, uint8_t *data, int len, uint8_t wrencmd) { + // Flash can't be accessed during IO, so copy to RAM and run that + volatile uint32_t func[&flashio_worker_end - &flashio_worker_begin]; + + // Can't execute off flash while talking to it, so copy IO code to SRAM + uint32_t *src_ptr = &flashio_worker_begin; + volatile uint32_t *dst_ptr = func; + + while (src_ptr != &flashio_worker_end) + *(dst_ptr++) = *(src_ptr++); + + __asm__ volatile ("fence.i" : : : "memory"); + + ((void(*)(uint8_t*, uint8_t*, uint32_t, uint32_t))func)((uint8_t*)flash, data, len, wrencmd); +} + +uint32_t spiflash_read_id(volatile spiflash_regs_t *flash) { + uint8_t buffer[5] = { 0x9F, /* zeros */ }; + spiflash_io(flash, buffer, 5, 0); + + uint32_t id = 0; + for (int i = 1; i <= 4; i++) { + id = id << 8U; + id |= buffer[i]; + } + return id; +} + +bool spiflash_is_winbond(volatile spiflash_regs_t *flash) { + uint32_t id; + id = spiflash_read_id(flash); + if ((id & 0x00ff0000) == WINBOND_ID<<16) return true; + else return false; +} + +void spiflash_set_qspi_flag(volatile spiflash_regs_t *flash) { + uint8_t buffer[8]; + + //Check which device we have + if (spiflash_is_winbond(flash)) { + // Read Configuration Registers (RDCR1 35h) + buffer[0] = 0x35; + buffer[1] = 0x00; // rdata + spiflash_io(flash, buffer, 2, 0); + uint8_t sr2 = buffer[1]; + + // Write Enable Volatile (50h) + Write Status Register 2 (31h) + buffer[0] = 0x31; + buffer[1] = sr2 | 2; // Enable QSPI + spiflash_io(flash, buffer, 2, 0x50); + } else { + // Read Configuration Registers (RDCR1 05h) + buffer[0] = 0x05; + buffer[1] = 0x00; // rdata + spiflash_io(flash, buffer, 2, 0); + uint8_t sr2 = buffer[1]; + + // Write Enable Volatile (06h) + Write Status Register 2 (01h) + buffer[0] = 0x01; + buffer[1] = sr2 | 1<<6; // Enable QSPI + spiflash_io(flash, buffer, 2, 0x06); + } +} + +void spiflash_set_quad_mode(volatile spiflash_regs_t *flash) { + flash->config = (0x3U << 3U) | (0x03U << 1U); // 3 dummy byte, X4 mode +} diff --git a/chipflow_digital_ip/memory/drivers/spiflash.h b/chipflow_digital_ip/memory/drivers/spiflash.h new file mode 100644 index 0000000..a5587ba --- /dev/null +++ b/chipflow_digital_ip/memory/drivers/spiflash.h @@ -0,0 +1,22 @@ +/* SPDX-License-Identifier: BSD-2-Clause */ +#ifndef SPI_FLASH_H +#define SPI_FLASH_H + +#include + +#define WINBOND_ID 0x40 +#define ISSI_ID 0x60 + +typedef struct __attribute__((packed, aligned(4))) { + uint32_t config; + uint32_t raw_control; + uint32_t raw_tx_data; + uint32_t raw_rx_data; +} spiflash_regs_t; + +void spiflash_io(volatile spiflash_regs_t *flash, uint8_t *data, int len, uint8_t wrencmd); +uint32_t spiflash_read_id(volatile spiflash_regs_t *flash); +void spiflash_set_qspi_flag(volatile spiflash_regs_t *flash); +void spiflash_set_quad_mode(volatile spiflash_regs_t *flash); + +#endif From 769551a3add94da09247eb1889dfdceb76018bf6 Mon Sep 17 00:00:00 2001 From: Rob Taylor Date: Mon, 8 Sep 2025 23:56:34 +0100 Subject: [PATCH 4/4] Include drivers in sdist and wheel --- pyproject.toml | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 30beaab..a4912c9 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -28,7 +28,20 @@ requires = ["pdm-backend"] build-backend = "pdm.backend" [tool.pdm.build] -includes = ["**/*.py", "**/*.v", "**/*.yaml"] +includes = [ + "**/*.py", + "**/*.v", + "**/*.yaml" + ] +source-includes = [ + "**/drivers/*.{c,h,cpp,hpp}" + ] + +[tool.pdm.build.wheel-data] +purelib = [ + {path = "**/drivers/*", relative-to = "."}, +] + # Development workflow configuration