Skip to content

Commit

Permalink
Merge pull request #299 from aabadie/qspi
Browse files Browse the repository at this point in the history
bsp: add support for qspi peripheral + sample application
  • Loading branch information
aabadie authored May 31, 2024
2 parents 8999f75 + b26ddce commit a30343e
Show file tree
Hide file tree
Showing 10 changed files with 445 additions and 4 deletions.
9 changes: 6 additions & 3 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ ifeq (nrf5340dk-app,$(BUILD_TARGET))
01bsp_motors \
01bsp_nvmc \
01bsp_qdec \
01bsp_qspi \
01bsp_radio_txrx \
01bsp_radio_txrx_lr \
01bsp_rgbled \
Expand All @@ -28,8 +29,10 @@ ifeq (nrf5340dk-app,$(BUILD_TARGET))
01bsp_uart \
01drv_lis2mdl \
01drv_lis3mdl \
01drv_lz4 \
01drv_move \
01drv_pid \
01drv_uzlib \
03app_dotbot \
03app_dotbot_gateway \
03app_dotbot_gateway_lr \
Expand Down Expand Up @@ -81,7 +84,7 @@ endif

# remove incompatible apps (nrf5340, sailbot gateway) for dotbot (v1, v2) builds
ifneq (,$(filter dotbot-v1,$(BUILD_TARGET)))
PROJECTS := $(filter-out 01bsp_qdec 01drv_lis3mdl 01drv_move 03app_dotbot_gateway 03app_dotbot_gateway_lr 03app_sailbot 03app_nrf5340_% 03app_freebot 03app_xgo,$(PROJECTS))
PROJECTS := $(filter-out 01bsp_qdec 01bsp_qspi 01drv_lis3mdl 01drv_move 03app_dotbot_gateway 03app_dotbot_gateway_lr 03app_sailbot 03app_nrf5340_% 03app_freebot 03app_xgo,$(PROJECTS))
ARTIFACT_PROJECTS := 03app_dotbot
endif

Expand All @@ -92,12 +95,12 @@ endif

# remove incompatible apps (nrf5340, dotbot, gateway) for sailbot-v1 build
ifeq (sailbot-v1,$(BUILD_TARGET))
PROJECTS := $(filter-out 01bsp_qdec 01drv_lis3mdl 01drv_move 03app_dotbot_gateway 03app_dotbot_gateway_lr 03app_dotbot 03app_nrf5340_% 03app_freebot 03app_xgo,$(PROJECTS))
PROJECTS := $(filter-out 01bsp_qdec 01bsp_qspi 01drv_lis3mdl 01drv_move 03app_dotbot_gateway 03app_dotbot_gateway_lr 03app_dotbot 03app_nrf5340_% 03app_freebot 03app_xgo,$(PROJECTS))
ARTIFACT_PROJECTS := 03app_sailbot
endif

ifneq (,$(filter nrf52833dk,$(BUILD_TARGET)))
PROJECTS := $(filter-out 01crypto_%,$(PROJECTS))
PROJECTS := $(filter-out 01crypto_% 01bsp_qspi,$(PROJECTS))
# Bootloader not supported on nrf52833dk
BOOTLOADER :=
endif
Expand Down
9 changes: 9 additions & 0 deletions bsp/bsp.emProject
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,15 @@
<file file_name="nrf/qdec.c" />
<file file_name="qdec.h" />
</project>
<project Name="00bsp_qspi">
<configuration
Name="Common"
project_dependencies="00bsp_gpio(bsp);00bsp_clock(bsp)"
project_directory="."
project_type="Library" />
<file file_name="nrf/qspi.c" />
<file file_name="qspi.h" />
</project>
<project Name="00bsp_radio">
<configuration
Name="Common"
Expand Down
203 changes: 203 additions & 0 deletions bsp/nrf/qspi.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,203 @@
/**
* @file
* @ingroup bsp_qspi
*
* @brief Definitions of the "qspi" bsp module.
*
* @author Alexandre Abadie <[email protected]>
*
* @copyright Inria, 2024-present
*/

#include <assert.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <nrf.h>

#include "clock.h"
#include "gpio.h"
#include "qspi.h"

//=========================== defines ==========================================

#if defined(NRF5340_XXAA)
#if defined(NRF_APPLICATION)
#define DB_QSPI (NRF_QSPI_S) ///< QSPI peripheral used
#elif defined(NRF_NETWORK)
#define DB_QSPI (NRF_QSPI_NS) ///< QSPI peripheral used
#endif
#else
#define DB_QSPI (NRF_QSPI) ///< QSPI peripheral
#endif
#define DB_QSPI_IRQ (QSPI_IRQn) ///< SPIM IRQ
#define DB_QSPI_IRQ_HANDLER (QSPI_IRQHandler) ///< SPIM IRQ handler function

#define DB_QSPI_STATUS_QE_BIT_Pos (6)

typedef struct {
bool running; ///< whether bytes are being sent/received
} qspi_vars_t;

//=========================== variables ========================================

static qspi_vars_t _qspi_vars;

static void _setup_quad_mode(bool enable) {
uint8_t status_reg = (uint8_t)(DB_QSPI->STATUS >> QSPI_STATUS_SREG_Pos);
if (enable) {
status_reg |= (1 << DB_QSPI_STATUS_QE_BIT_Pos);
} else {
status_reg &= ~(1 << DB_QSPI_STATUS_QE_BIT_Pos);
}
DB_QSPI->CINSTRDAT0 = (uint32_t)status_reg;

uint8_t opcode = 0x01; // Write status register
uint8_t length = QSPI_CINSTRCONF_LENGTH_3B;
DB_QSPI->EVENTS_READY = 0;
_qspi_vars.running = true;
DB_QSPI->CINSTRCONF = (opcode << QSPI_CINSTRCONF_OPCODE_Pos) |
(length << QSPI_CINSTRCONF_LENGTH_Pos) |
(1 << QSPI_CINSTRCONF_LIO2_Pos) |
(1 << QSPI_CINSTRCONF_LIO3_Pos) |
(QSPI_CINSTRCONF_WIPWAIT_Disable << QSPI_CINSTRCONF_WIPWAIT_Pos) |
(QSPI_CINSTRCONF_WREN_Enable << QSPI_CINSTRCONF_WREN_Pos);
while (_qspi_vars.running) {
__WFE();
}
}

//=========================== public ===========================================

void db_qspi_init(const db_qspi_conf_t *conf) {
_qspi_vars.running = false;
db_hfclk_init();

// configure SPIM pins
db_gpio_init(conf->io0, DB_GPIO_IN);
db_gpio_init(conf->io1, DB_GPIO_IN);
db_gpio_init(conf->io2, DB_GPIO_IN);
db_gpio_init(conf->io3, DB_GPIO_IN);
db_gpio_init(conf->cs, DB_GPIO_OUT);
db_gpio_init(conf->sck, DB_GPIO_OUT);

nrf_port[conf->io0->port]->PIN_CNF[conf->io0->pin] |= (GPIO_PIN_CNF_DRIVE_H0H1 << GPIO_PIN_CNF_DRIVE_Pos);
nrf_port[conf->io1->port]->PIN_CNF[conf->io0->pin] |= (GPIO_PIN_CNF_DRIVE_H0H1 << GPIO_PIN_CNF_DRIVE_Pos);
nrf_port[conf->io2->port]->PIN_CNF[conf->io0->pin] |= (GPIO_PIN_CNF_DRIVE_H0H1 << GPIO_PIN_CNF_DRIVE_Pos);
nrf_port[conf->io3->port]->PIN_CNF[conf->io0->pin] |= (GPIO_PIN_CNF_DRIVE_H0H1 << GPIO_PIN_CNF_DRIVE_Pos);
nrf_port[conf->cs->port]->PIN_CNF[conf->cs->pin] |= GPIO_PIN_CNF_DRIVE_H0H1 << GPIO_PIN_CNF_DRIVE_Pos;
nrf_port[conf->sck->port]->PIN_CNF[conf->sck->pin] |= GPIO_PIN_CNF_DRIVE_H0H1 << GPIO_PIN_CNF_DRIVE_Pos;

DB_QSPI->PSEL.IO0 = (conf->io0->port << QSPI_PSEL_IO0_PORT_Pos) |
(conf->io0->pin << QSPI_PSEL_IO0_PIN_Pos) |
(QSPI_PSEL_IO0_CONNECT_Connected << QSPI_PSEL_IO0_CONNECT_Pos);
DB_QSPI->PSEL.IO1 = (conf->io1->port << QSPI_PSEL_IO1_PORT_Pos) |
(conf->io1->pin << QSPI_PSEL_IO1_PIN_Pos) |
(QSPI_PSEL_IO1_CONNECT_Connected << QSPI_PSEL_IO1_CONNECT_Pos);
DB_QSPI->PSEL.IO2 = (conf->io2->port << QSPI_PSEL_IO2_PORT_Pos) |
(conf->io2->pin << QSPI_PSEL_IO2_PIN_Pos) |
(QSPI_PSEL_IO2_CONNECT_Connected << QSPI_PSEL_IO2_CONNECT_Pos);
DB_QSPI->PSEL.IO3 = (conf->io3->port << QSPI_PSEL_IO3_PORT_Pos) |
(conf->io3->pin << QSPI_PSEL_IO3_PIN_Pos) |
(QSPI_PSEL_IO3_CONNECT_Connected << QSPI_PSEL_IO3_CONNECT_Pos);
DB_QSPI->PSEL.CSN = (conf->cs->port << QSPI_PSEL_CSN_PORT_Pos) |
(conf->cs->pin << QSPI_PSEL_CSN_PIN_Pos) |
(QSPI_PSEL_CSN_CONNECT_Connected << QSPI_PSEL_CSN_CONNECT_Pos);
DB_QSPI->PSEL.SCK = (conf->sck->port << QSPI_PSEL_SCK_PORT_Pos) |
(conf->sck->pin << QSPI_PSEL_SCK_PIN_Pos) |
(QSPI_PSEL_SCK_CONNECT_Connected << QSPI_PSEL_SCK_CONNECT_Pos);

// Configure XIP offset to 0
DB_QSPI->XIPOFFSET = 0;

// Configure IFCONFIG0
uint32_t rw_opcode = (QSPI_IFCONFIG0_READOC_FASTREAD << QSPI_IFCONFIG0_READOC_Pos) |
(QSPI_IFCONFIG0_WRITEOC_PP << QSPI_IFCONFIG0_WRITEOC_Pos);
if (conf->enable_quad) {
rw_opcode = (QSPI_IFCONFIG0_READOC_READ4O << QSPI_IFCONFIG0_READOC_Pos) |
(QSPI_IFCONFIG0_WRITEOC_PP4O << QSPI_IFCONFIG0_WRITEOC_Pos);
}

DB_QSPI->IFCONFIG0 = rw_opcode |
(QSPI_IFCONFIG0_ADDRMODE_24BIT << QSPI_IFCONFIG0_ADDRMODE_Pos) |
(QSPI_IFCONFIG0_DPMENABLE_Disable << QSPI_IFCONFIG0_DPMENABLE_Pos) |
(QSPI_IFCONFIG0_PPSIZE_256Bytes << QSPI_IFCONFIG0_PPSIZE_Pos);

// Configure IFCONFIG1
DB_QSPI->IFCONFIG1 = (10 << QSPI_IFCONFIG1_SCKDELAY_Pos) |
(QSPI_IFCONFIG1_DPMEN_Exit << QSPI_IFCONFIG1_DPMEN_Pos) |
(QSPI_IFCONFIG1_SPIMODE_MODE0 << QSPI_IFCONFIG1_SPIMODE_Pos) |
(conf->sckfreq << QSPI_IFCONFIG1_SCKFREQ_Pos);

DB_QSPI->INTENSET = QSPI_INTENSET_READY_Enabled << QSPI_INTENSET_READY_Pos;
NVIC_EnableIRQ(DB_QSPI_IRQ);

// Enable QSPI peripheral
DB_QSPI->ENABLE = QSPI_ENABLE_ENABLE_Enabled << QSPI_ENABLE_ENABLE_Pos;

// Clear events and activate
DB_QSPI->EVENTS_READY = 0;
_qspi_vars.running = true;
DB_QSPI->TASKS_ACTIVATE = QSPI_TASKS_ACTIVATE_TASKS_ACTIVATE_Trigger << QSPI_TASKS_ACTIVATE_TASKS_ACTIVATE_Pos;
while (_qspi_vars.running) {
__WFE();
}

_setup_quad_mode(conf->enable_quad);
}

void db_qspi_read(const uint32_t addr, void *in, size_t len) {
DB_QSPI->READ.SRC = addr;
DB_QSPI->READ.DST = (uint32_t)in;
DB_QSPI->READ.CNT = (uint32_t)len;
DB_QSPI->EVENTS_READY = 0;
_qspi_vars.running = true;
DB_QSPI->TASKS_READSTART = QSPI_TASKS_READSTART_TASKS_READSTART_Trigger << QSPI_TASKS_READSTART_TASKS_READSTART_Pos;
while (_qspi_vars.running) {
__WFE();
}
}

void db_qspi_program(const uint32_t addr, const void *out, size_t len) {
DB_QSPI->WRITE.DST = addr;
DB_QSPI->WRITE.SRC = (uint32_t)out;
DB_QSPI->WRITE.CNT = (uint32_t)len;
DB_QSPI->EVENTS_READY = 0;
_qspi_vars.running = true;
DB_QSPI->TASKS_WRITESTART = QSPI_TASKS_WRITESTART_TASKS_WRITESTART_Trigger << QSPI_TASKS_WRITESTART_TASKS_WRITESTART_Pos;
while (_qspi_vars.running) {
__WFE();
}
}

void _erase(const uint32_t addr, uint32_t len) {
DB_QSPI->ERASE.PTR = addr;
DB_QSPI->ERASE.LEN = len;
DB_QSPI->EVENTS_READY = 0;
_qspi_vars.running = true;
DB_QSPI->TASKS_ERASESTART = QSPI_TASKS_ERASESTART_TASKS_ERASESTART_Trigger << QSPI_TASKS_ERASESTART_TASKS_ERASESTART_Pos;
while (_qspi_vars.running) {
__WFE();
}

// Wait for the status register to be ready
while (!(DB_QSPI->STATUS & (1 << QSPI_STATUS_READY_Pos))) {
__WFE();
}
}

void db_qspi_block_erase(const uint32_t addr) {
_erase(addr, QSPI_ERASE_LEN_LEN_64KB << QSPI_ERASE_LEN_LEN_Pos);
}

void db_qspi_bulk_erase(void) {
_erase(0, QSPI_ERASE_LEN_LEN_All << QSPI_ERASE_LEN_LEN_Pos);
}

void DB_QSPI_IRQ_HANDLER(void) {
if (DB_QSPI->EVENTS_READY) {
DB_QSPI->EVENTS_READY = 0;
_qspi_vars.running = false;
}
}
86 changes: 86 additions & 0 deletions bsp/qspi.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
#ifndef __QSPI_H
#define __QSPI_H

/**
* @defgroup bsp_qspi QSPI flash
* @ingroup bsp
* @brief Control the QSPI flash peripheral
*
* @{
* @file
* @author Alexandre Abadie <[email protected]>
* @copyright Inria, 2024-present
* @}
*/

#include <stdbool.h>
#include <stdint.h>
#include <stdlib.h>
#include <nrf.h>
#include "gpio.h"

#define DB_QSPI_SCKFREQ_32MHZ (0)
#define DB_QSPI_SCKFREQ_16MHZ (1)
#define DB_QSPI_SCKFREQ_8MHZ (3)
#define DB_QSPI_SCKFREQ_4MHZ (7)
#define DB_QSPI_SCKFREQ_2MHZ (15)
#define DB_QSPI_SCKFREQ_1MHZ (31)

/// SPIM pin configuration
typedef struct {
const gpio_t *io0; ///< IO0
const gpio_t *io1; ///< IO1
const gpio_t *io2; ///< IO2
const gpio_t *io3; ///< IO3
const gpio_t *cs; ///< Chip select
const gpio_t *sck; ///< Clock
uint8_t sckfreq; ///< QSPI clock frequency. 32MHz / (val + 1)
bool enable_quad; ///< enable Quad serial mode
} db_qspi_conf_t;

/// SPIM mode
typedef enum {
DB_SPIM_MODE_0 = 0, ///< CPOL = 0 (Active High), CPHA = 0 (Leading)
DB_SPIM_MODE_1, ///< CPOL = 0 (Active High), CPHA = 1 (Trailing)
DB_SPIM_MODE_2, ///< CPOL = 1 (Active Low), CPHA = 0 (Leading)
DB_SPIM_MODE_3 ///< CPOL = 1 (Active Low), CPHA = 1 (Trailing)
} db_qspi_mode_t;

/**
* @brief Initialize the QSPI peripheral
*
* @param[in] conf pointer to configuration struct
*/
void db_qspi_init(const db_qspi_conf_t *conf);

/**
* @brief Read bytes from flash
*
* @param[in] addr 24bit address
* @param[in] in Pointer to the incoming bytes
* @param[in] len Length of the bytes to send
*/
void db_qspi_read(const uint32_t addr, void *in, size_t len);

/**
* @brief Program bytes on flash
*
* @param[in] addr 24bit address
* @param[in] out Pointer to the output bytes
* @param[in] len Length of the bytes to receive
*/
void db_qspi_program(const uint32_t addr, const void *out, size_t len);

/**
* @brief Erase page/blocks of flash
*
* @param[in] addr 24bit address
*/
void db_qspi_block_erase(const uint32_t addr);

/**
* @brief Erase all flash
*/
void db_qspi_bulk_erase(void);

#endif
2 changes: 1 addition & 1 deletion doc/doxygen/Doxyfile
Original file line number Diff line number Diff line change
Expand Up @@ -357,7 +357,7 @@ DIAFILE_DIRS =
PLANTUML_JAR_PATH =
PLANTUML_CFG_FILE =
PLANTUML_INCLUDE_PATH =
DOT_GRAPH_MAX_NODES = 50
DOT_GRAPH_MAX_NODES = 100
MAX_DOT_GRAPH_DEPTH = 0
DOT_TRANSPARENT = NO
DOT_MULTI_TARGETS = NO
Expand Down
1 change: 1 addition & 0 deletions doc/sphinx/bsp.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ _api/bsp_nvmc
_api/bsp_partition
_api/bsp_pwm
_api/bsp_qdec
_api/bsp_qspi
_api/bsp_radio
_api/bsp_rgbled
_api/bsp_rng
Expand Down
1 change: 1 addition & 0 deletions doc/sphinx/examples.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ _examples/01bsp_lighthouse
_examples/01bsp_motors
_examples/01bsp_nvmc
_examples/01bsp_qdec
_examples/01bsp_qspi
_examples/01bsp_radio_txrx_lr
_examples/01bsp_radio_txrx
_examples/01bsp_rgbled
Expand Down
5 changes: 5 additions & 0 deletions projects/01bsp_qspi/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# QSPI bsp sample application

This application erases the first block of flash on the DK embedded flash memory.
Then it reads some content, write a buffer and verify that the buffer was written
correctly.
Loading

0 comments on commit a30343e

Please sign in to comment.