3. QSPI Memory Driver

Using a custom QSPI memory requires the creation of a driver for the custom device and subsequently making said driver visible to the firmware loader tools and to the firmware itself. For products of the DA1469x family, the process of the driver creation has been simplified into the making of a simple c header file. The driver header file consists of a configuration object of structure qspi_flash_config_t as well as a set of callback and helper functions. When creating a new driver it is advised to use qspi_XXX_template.h as a basis, which can be found along with the other QSPI Memory Drivers under {SDK_root}/sdk/bsp/memory/include folder.

3.1. Configuration Structure

The qspi_flash_config_t structure is defined in qspi_common.h header file which can be found under {SDK_root}/sdk/bsp/memory/include folder. Fill in the required fields after consulting the datasheet of the QSPI Memory device used.

The fields of the configuration structure could be divided into three different sections.

  1. Common: These fields are mandatory for both QSPI Flash drivers AND QSPI PSRAM drivers.

  2. QSPI Flash only: These fields should only be initialized when the device targetted by the driver, is a QSPI Flash.

  3. QSPI PSRAM only: These fields should only be initialized when the device targetted by the driver, is a QSPI PSRAM.

3.1.1. Common Fields

Table 1 qspi_flash_config_t Common Fields

Field

Type

Description

.manufacturer_id

uint8_t

This corresponds to the first byte expected to be returned by the read_jedec_id command (9Fh).

.device_type

uint8_t

This corresponds to the second byte expected to be returned by the read_jedec_id command (9Fh).

.device_density

uint8_t

This corresponds to the third byte expected to be returned by the read_jedec_id command (9Fh).

.memory_size

uint32_t

This represents the capacity of the storage device in bits.

.address_size

HW_QSPI_ADDR_SIZE

Desired Addressing Mode. Either 24-bit or 32-bit addressing will be used when accessing the device.
Please note that not all devices support both addressing modes.

.is_ram

bool

Set to true if the device is a PSRAM device connected to QSPI Controller 2 or false if it is a Flash Memory.

.initialize

initialize_cb_t

This is the QSPI Memory Device initialization function. It is called by the system to perform all the necessary steps that will set the QSPI Memory device operable.

Typically for QSPI Flash devices, in this callback, the Quad Enable bit of the Status Register is set, making eXecution In Place more efficient.

Occasionally, when certain devices cannot operate in high enough frequency, it might be necessary to setup a QSPIC clock divider depending on the system clock used.

.sys_clk_cfg

sys_clk_cfg_cb_t

This callback function is called right before a system clock change is going to take place when switching to PLL96 or after the clock change when switching from PLL96 to account for any configurations that might require change when switching operating frequency.

This is typically the place where the number of dummy cycles expected by the QSPI Flash device and the dummy bytes sent by the QSPI Controller have to be set

Additionally, this is where a QSPIC clock divider will be set if such a divider is necessary for the new operating frequency.

(e.g. For Macronix R series the maximum operating frequency is 80 MHz. Whenever PLL96 is chosen as the system clock for this QSPi Flash, a QSPIC clock divider of 2 has to be used.)

.is_suspended

is_suspended_cb_t

This callback function is called to check if the Flash is in suspended state.

Normally, DA1469x SDK ensures that whenever a Read instruction occurs, Erase or Program instructions are suspended so eXecution In Place can fetch instructions to the cache without delays.

.get_dummy_bytes

get_dummy_bytes_cb_t

This function’s purpose is to return the number of dummy bytes that need to be sent after the extra byte, before the QSPI Storage device can switch SIO0 to SIO3 from input to output. The number might vary, depending on the vendor, model or even QSPIC clock frequency.

Important Note: This API is only called by the SDK upon initialization.
In order to address a change in the number of dummy bytes that are expected to be sent by the QSPI Controller at any point after initialization, make sure to call API hw_qspi_set_dummy_bytes_count() found in hw_qspi.h header file. (e.g. during a call of the sys_clk_cfg callback function.)

.fast_read_opcode

uint8_t

This is the opcode that should be used to perform read operations.

.extra_byte

uint8_t

This field represents the byte that needs to be sent right after the addressing phase which activates performance mode and could vary from vendor to vendor.
For every subsequent read, this byte needs to be repeated in order to stay in performance mode.

.send_once

uint8_t

This field goes along with the extra byte.
If performance mode is enabled, its value should be 1 as to only send the read instruction once while the QSPI Storage device remains in this mode.
If it is desired to send the command every time there is a need to read from the device, this field should be set to 0 instead.

.break_seq_size

HW_QSPI_BREAK_SEQ_SIZE

When it is desired to exit performance mode, a new empty read command is issued only this time 0xFF is sent instead of the extra byte.
This field defines how many bytes are required to exit performance mode.

HW_QSPI_BREAK_SEQ_SIZE_1B: Value for QSPI Memory devices which use 24-bit addressing only.
HW_QSPI_BREAK_SEQ_SIZE_2B: Value for QSPI Memory devices that support 32-bit addressing as well.

Warning

if Device Autodetect is used, the firmware will first issue a READ_JEDEC_ID (9Fh) command to the device to retrieve three bytes that uniquely identify the QSPI Memory. These three bytes that were returned are then checked against every configuration object of every driver that is part of Device Autodetect. If they match with .manufacturer_id, .device_type and .device_density fields of any configuration object, the initialization of the QSPI Memory occurs according to that configuration object’s fields.

Tool uartboot loader which is responsible for downloading firmware to the Flash or RAM, uses dg_configFLASH_AUTODETECT by default.

3.1.2. QSPI Flash only Fields

Table 2 qspi_flash_config_t QSPI Flash only Fields

Field

Type

Description

(QSPI Flash only)
.ucode_wakeup

qspi_ucode_t

OBSOLETE - Currently not used in DA1469x products. The .ucode_wakeup field can be ommited in the driver. - OBSOLETE

When the system powers on or when it recovers from sleep, the Flash requires some commands to be issued before it can start/return to a state that the CPU can start/continue executing code from. This is taken care by the QSPI controller through Microcode.

The microcode value is dependent on how the Flash is handled during sleep or if the device sleeps at all. The value of this field should be a pointer to the start of micrcode array and its size.

In the default drivers under {SDK_root}/sdk/bsp/memory/include folder, it is possible to find code that calculates the proper values that should be passed through microcode. This code is able to calculate the values of the microcode of any driver.

For further information, please check DA1469x Datasheet chapter 19.2.2 page 176/743.

(QSPI Flash only)
.power_up_delay

uint16_t

Every QSPI Flash device needs some time from the moment that it is supplied with power for the first time, until it is able to accept all the SPI commands it supports. This delay in microseconds should be the value of this field.

The minimum value supported is 3 μsec.

(QSPI Flash only)
.power_down_delay

uint16_t

Similar to power_up_delay, this field represents the delay in microseconds between sending Enter Deep Power Down Mode command (B9h) until it takes effect.

The minimum value supported is 3 μsec.

(QSPI Flash only)
.release_power_down_delay

uint16_t

Exiting Deep Power Down mode has a short delay as well. This field’s value should reflect this delay in microseconds as well.

The minimum value supported is 3 μsec.

(QSPI Flash only)
.page_program_opcode

uint8_t

This byte value corresponds to the opcode of the Quad Page Program command.

(QSPI Flash only)
.page_qpi_program_opcode

uint8_t

Optional
This byte value corresponds to the opcode of QPI Page Program.

(QSPI Flash only)
.quad_page_program_address

bool

This field sets the QSPI bus to the proper mode during the addressing phase for Page Program.

0 - The address is transfered in Single Mode.

1 - The address is transfered in Quad Mode.

(QSPI Flash only)
.erase_opcode

uint8_t

This byte value corresponds to the opcode of sector(4KB) erase.

(QSPI Flash only)
.erase_suspend_opcode

uint8_t

This byte value corresponds to the opcode of erase suspend.

(QSPI Flash only)
.erase_resume_opcode

uint8_t

This byte value corresponds to the opcode of erase resume.

(QSPI Flash only)
.read_erase_progress_opcode

uint8_t

This field usually takes the value of Read Status Register command so it can read the WIP bit.

(QSPI Flash only)
.erase_in_progress_bit

uint8_t

This corresponds to the bit position [0-7] of the WIP bit in the Status Register. (usually position 0)

(QSPI Flash only)
.erase_in_progress_bit_high_level

bool

This field’s value represents the value of WIP bit when the QSPI Flash is busy.

false - Flash is busy when WIP bit has the value 0.
true - Flash is busy when WIP bit has the value 1.

3.1.3. QSPI PSRAM only Fields

Table 3 qspi_flash_config_t QSPI PSRAM only Fields

Field

Type

Description

(QSPI PSRAM only)
.burst_len

HW_QSPI2_MEMBLEN

The length of the wrapping burst that the external memory device is capable to implement is held in this field.

(QSPI PSRAM only)
.cs_high_t_en

bool

Some QSPI Memory devices only allow up to a maximum amount of time that the Chip Select (CE#) can stay active-low during burst read operations.

false - There is no upper limit to the time that CE# can stay active
true - CE# can stay active up to a specified time (see cs_high_t field).

If a burst would take longer, the QSPI Controller will fragment the access to more than one Read Bursts to respect the constraint.

(QSPI PSRAM only)
.cs_high_t

uint8_t

The maximum allowed time that the Chip Select(CE#) can stay active, in microseconds.

This value is ignored if cs_high_t_en field is set to false.

3.2. Extra Driver Components

Other than the configuration object, a driver should contain the definitions of the callback functions as well as functions that provide non-standard functionality and which could also be called by the callback functions.Such functions’ range of operations could vary from performing optional to essential tasks to the proper operation of the device.

One such example could be writing directly to the second byte of status register or driving the output signal. In most cases they are not mandatory for operating the device however they are helpful.

Another example could be reading or writing to a register which is non accessible through Write Status Register command (01h).