Getting Started#

Use of the qspi_flash_fast_read peripheral is split into 2 sections below, hardware setup and software use including typical use scenarios.

This document will walk through a use case with the XK-VOICE-L71 board with Winbond W25Q64JW flash part so some steps may need to be adjusted for the particular development board or flash part being used.

Hardware Setup#

To setup the hardware to use this library the flash part must be configured to a drive strength that best suits the applications expected SCLK speed. For the XK-VOICE-L71 1V0A board, the Winbond W25Q64JW flash part the drive strength register must be configured to 50%. Per the datasheet, this can be set by writing the value 0x40 to the status register 3, 0x11.

In the thruput example application a CMake custom target flash_config_drive_str_50_example_ff_thruput_eval has been added to wrap the appropriate XFLASH calls. Refer to the XTC Tools Documentation for more information on sending flash commands to the QSPI flash part via XFLASH and a connected XCORE device.

Note

Not all flash parts require this step. For instance, the XCORE_AI_EXPLORER board default drive strength does not need to be changed.

After configuring the flash part, the next step is to flash the appropriate calibration pattern. The calibration pattern is supplied as a binary file, and the user can place this wherever they wish in flash, provided that the address is known when qspi_flash_fast_read_calibrate() is called.

In the thruput example application a CMake custom target flash_calibration_example_ff_thruput_eval_XK_VOICE_L71 has been added to wrap the appropriate XFLASH call to store the pattern at address 0x00000000. Refer to the XTC Tools Documentation for more information on storing in the data partition.

General Use#

The runtime setup of the library consists of initializing the peripheral driver with the XCORE resources to use. Additionally, the user has the ability to setup the driver to populate the data buffer with read data nibble swapped or not. This is an option since depending on how data is stored in flash, the user may need to nibble swap it to be read as expected.

The user then must set up the resources and calibrate. See Calibration for more information on the calibration process. After successful calibration, the user may perform reads indefinitely, or until qspi_flash_fast_read_shutdown() is called. There is no internal mutual exclusion mechanism and it is up to the user to handle concurrent peripheral use.

#include "qspi_flash_fast_read.h"

#define CALIBRATION_PATTERN_ADDR  0x0 // Application specific

...
qspi_fast_flash_read_ctx_t qspi_fast_flash_read_ctx;
qspi_fast_flash_read_ctx_t *ctx = &qspi_fast_flash_read_ctx;

qspi_flash_fast_read_init(ctx,
                          XS1_CLKBLK_1,
                          XS1_PORT_1B,
                          XS1_PORT_1C,
                          XS1_PORT_4B,
                          qspi_fast_flash_read_transfer_raw,
                          3
);

qspi_flash_fast_read_setup_resources(ctx);

uint32_t scratch_buf[QFFR_DEFAULT_CAL_PATTERN_BUF_SIZE_WORDS];
if (qspi_flash_fast_read_calibrate(
        ctx,
        CALIBRATION_PATTERN_ADDR,
        qspi_flash_fast_read_pattern_expect_default,
        scratch_buf,
        QFFR_DEFAULT_CAL_PATTERN_BUF_SIZE_WORDS)
  != 0) {
      printf("Fast flash calibration failed\n");
      /* Error handle here */
}

qspi_flash_fast_read(ctx, /* Some address */, /* buffer to populate */, /* bytes to read */);

qspi_flash_fast_read_shutdown(ctx);

Reuse Resources#

The library supports shutting down of the peripheral and a minimal method to reinstantiate flash access. This enables the user to potentially reuse ports or clock blocks when flash access is not required. After shutdown, the resources are free to be used elsewhere. The application is responsible for preventing reads from happening, as there is no internal protection. When flash is needed again the resources can be setup again and calibration re-applied.

qspi_flash_fast_read_shutdown(ctx);

/* Re-use XCORE resources here */
{
  ;
}

/* Setup flash fast read */
qspi_flash_fast_read_setup_resources(ctx);
qspi_flash_fast_read_apply_calibration(ctx);

Warning

If reusing the ports be aware that there is still a QSPI flash part connected.

Nibble Swapping#

The transfer mode is set during initialization but may be changed during runtime when an operation is not in progress. Here is an example of how to perform one read in nibble swapped mode before returning back to raw.

qspi_flash_fast_read_mode_set(ctx, qspi_fast_flash_read_transfer_nibble_swap);
qspi_flash_fast_read(ctx, /* Some address */, /* buffer to populate */, /* bytes to read */);
qspi_flash_fast_read_mode_set(ctx, qspi_fast_flash_read_transfer_raw);

Multiple Peripherals#

It is possible to use this library with multiple flash parts. Below is an example of instantiating 2 drivers with separate XCORE resources.

qspi_fast_flash_read_ctx_t qspi_fast_flash_read_ctx;
qspi_fast_flash_read_ctx_t *ctx = &qspi_fast_flash_read_ctx;

qspi_fast_flash_read_ctx_t qspi_fast_flash_read_second_ctx;
qspi_fast_flash_read_ctx_t *ctx_2 = &qspi_fast_flash_read_second_ctx;

qspi_flash_fast_read_init(ctx,
                          XS1_CLKBLK_1,
                          XS1_PORT_1B,
                          XS1_PORT_1C,
                          XS1_PORT_4B,
                          qspi_fast_flash_read_transfer_raw,
                          3
);

qspi_flash_fast_read_setup_resources(ctx);

uint32_t scratch_buf[QFFR_DEFAULT_CAL_PATTERN_BUF_SIZE_WORDS];
if (qspi_flash_fast_read_calibrate(
        ctx,
        CALIBRATION_PATTERN_ADDR,
        qspi_flash_fast_read_pattern_expect_default,
        scratch_buf,
        QFFR_DEFAULT_CAL_PATTERN_BUF_SIZE_WORDS)
  != 0) {
      printf("Fast flash calibration failed\n");
      /* Error handle here */
}

qspi_flash_fast_read_init(ctx_2,
                          XS1_CLKBLK_2,
                          XS1_PORT_1E,
                          XS1_PORT_1F,
                          XS1_PORT_4C,
                          qspi_fast_flash_read_transfer_raw,
                          3
);

qspi_flash_fast_read_setup_resources(ctx_2);

uint32_t scratch_buf[QFFR_DEFAULT_CAL_PATTERN_BUF_SIZE_WORDS];
if (qspi_flash_fast_read_calibrate(
        ctx_2,
        CALIBRATION_PATTERN_ADDR2,
        qspi_flash_fast_read_pattern_expect_default,
        scratch_buf,
        QFFR_DEFAULT_CAL_PATTERN_BUF_SIZE_WORDS)
  != 0) {
      printf("Fast flash calibration failed\n");
      /* Error handle here */
}

Note

It may be possible to share SCLK and SIO pins and only use different CS and clock blocks to save on 1-bit ports. This configuration would be heavily dependent on the flash parts being used and their pin states when CS is not asserted and the length of traces run. It would be up to the application programmer to handle mutual exclusion of ports.