The M0 SDK provides a way to load programs from external SPI flash rather than having the program reside in internal flash. This allows for larger programs beyond the size constraints of internal flash and offers more flexibility. Here is an overview of how to set up and use external SPI flash with M0 SDK:
SPI Flash Hardware
First, you need an external SPI flash chip connected to the M0 board. The SPI pins used are typically:
- MOSI – Master Out Slave In
- MISO – Master In Slave Out
- SCK – Serial Clock
- CS – Chip Select
For example, the SST26VF016B is a common 16MB SPI flash chip. Check the datasheet for your specific chip for details on pin connections and electrical characteristics.
Connect the SPI flash chip according to the pinout and make sure it shares a common ground with the M0 board. Some boards like the ARC EM Starter Kit have SPI flash already populated.
Enable SPI Driver
In your SDK project, enable the SPI flash driver in the board support package (BSP). This handles communication with the external flash chip.
In the bsp/board folder, edit the sys_config.h file and uncomment the following line: #define METAL_INTERNAL_SPI 0
This enables the SPI driver for the external flash chip, disabling the internal flash driver.
Configure SPI and Flash in BSP
Still in the bsp folder, open the spi_flash.c file. Set the following defines accordingly for your flash chip: #define SPI_FLASH_DEVICE_SST26VF016B #define SPI_FLASH_SECTOR_SIZE (4*1024) #define SPI_FLASH_PAGE_SIZE 256 #define SPI_FLASH_SUBSECTOR_SIZE (4*1024)
This configures the correct device parameters like page and sector sizes for operations.
Also set the GPIO pins used for the SPI connections. For example: struct spi_config_regs spi_regs = { .clk = METAL_SPI_2_SCK_PIN, .cs = METAL_SPI_2_CS_PIN, .mosi = METAL_SPI_2_MOSI_PIN, .miso = METAL_SPI_2_MISO_PIN };
Consult the board pinout and set the proper GPIO pins connected to your SPI flash chip.
Initialize SPI Driver
In your main.c file, initialize the SPI driver by calling: spi_flash_init(&spi_regs);
This sets up the SPI peripheral to communicate with the external flash device.
Copy Data to SPI Flash
To write data to the SPI flash, first define a buffer with your data: uint8_t buffer[256];
Then call the spi_flash_write_page() function: spi_flash_write_page(0x0000, buffer, 256);
This writes 256 bytes from the buffer to offset 0x0000 in the flash. Repeat as needed to write your full data.
Load Data from SPI Flash
To load data from the SPI flash into a buffer, use spi_flash_read_page(): uint8_t buffer[256]; spi_flash_read_page(0x0000, buffer, 256);
This reads 256 bytes from offset 0x0000 and stores them in the buffer.
Executing Code from SPI Flash
The M0 SDK initializes the vector table and program counter to execute from internal flash by default. To run a program from external SPI flash instead, you need to copy the vector table to SPI flash and remap the vector table base address.
Copy the vector table to the beginning of SPI flash: spi_flash_write_page(0x0000, vector_table, 256);
In your linker script, set the vector table base address to the SPI flash origin: PROVIDE (_memmap_flash_org = 0x00000000);
This makes the vector table get linked to the start of SPI flash. Then in your reset handler, set the VTOR register to this address to remap the vector table: void reset_handler() { __set_vtor((void*)_memmap_flash_org); /* rest of reset handling */ }
Now when reset occurs, the processor will start executing directly from the vector table in SPI flash.
To call functions in SPI flash, declare them as external: extern int spi_flash_func(void);
And link them at the desired flash offset: .spi_text : { *(.text*) } > _memmap_flash_org
This puts all text sections from your compiled code into the SPI flash area.
Bootloader Options
For more robust booting from external flash, consider using a bootloader. The SDK provides sample bootloaders that can copy a program from SPI flash to internal memory and execute it.
The spi_bootloader example initializes SPI flash, copies code to internal RAM, then jumps to it. Useful for testing: git clone https://github.com/foss-for-synopsys-dwc-arc-processors/embarc_osp cd embarc_osp/example/spi_bootloader make BOARD=emsk
For production firmware updates, the app_loader example demonstrates robustly booting from an application image in SPI flash. It performs checksum validation and can fall back to a golden image.
The bootloaders skip initialization to run from predefined flash areas. This helps avoid corrupting flash contents during development.
Summary
Loading programs from external SPI flash allows you to overcome the size limitations of internal flash on M0 processors. It requires configuring the SPI driver, initializing SPI flash, copying code to it, and remapping the vector table. With an optimized bootloader, you can achieve robust booting directly from SPI flash.