A cross-platform NOR flash driver library designed for embedded devices. The driver is hardware-agnostic — all platform I/O is supplied by the application via function pointers, making it portable across any MCU or operating system. The library also ships a Linux-hosted flash simulator, enabling full driver testing on a development machine without any hardware.
This library is designed in a layered manner:
+-------------------+
| application | - Embedded application using NOR flash as a storage medium
+-------------------+
^
|
+-------------------+
| file system | - Optional: littlefs glue layer (fs/littlefs/)
+-------------------+
^
|
+-------------------+
| driver | - NOR flash driver: constant, cross-platform (driver/)
+-------------------+
^
|
+-------------------+
| port | - Platform port: user configurable, depends on hardware (port/)
+-------------------+
The driver layer provides a unified API that remains identical across all supported platforms. All hardware interaction is abstracted behind six function pointers held in a struct norfx_device:
| Callback | Description |
|---|---|
spi_chip_select |
Assert chip-select (CS low) |
spi_chip_deselect |
De-assert chip-select (CS high) |
spi_write |
Transmit bytes over SPI |
spi_read |
Receive bytes over SPI |
get_tick_ms |
Return a millisecond tick counter |
delay_ms |
Block for N milliseconds |
The port layer connects a specific platform to the driver by implementing those six callbacks. The optional file system layer provides a ready-made littlefs integration on top of the driver.
enum norfx_status norfx_reset(struct norfx_device *dev);
enum norfx_status norfx_read_id(struct norfx_device *dev,
enum norfx_id_kind id,
uint32_t *id_val);
enum norfx_status norfx_read(struct norfx_device *dev,
uint32_t start_page, uint8_t offset,
uint32_t size, uint8_t *rx_buf);
enum norfx_status norfx_fast_read(struct norfx_device *dev,
uint32_t start_page, uint8_t offset,
uint32_t size, uint8_t *rx_buf);
enum norfx_status norfx_erase_sector(struct norfx_device *dev,
uint16_t num_sector);
enum norfx_status norfx_page_program(struct norfx_device *dev,
uint32_t page, uint16_t offset,
uint32_t size, uint8_t *data);
enum norfx_status norfx_write(struct norfx_device *dev,
uint32_t page, uint16_t offset,
uint32_t size, uint8_t *data,
uint8_t *scratch_buf);Note:
norfx_writeperforms an automatic read-modify-write cycle and is intended for raw flash usage. Do not use it with a file system such as littlefs — passnorfx_page_programandnorfx_erase_sectordirectly to the littlefs block device callbacks instead.
Create a .c / .h pair under port/<platform>/ and implement the six callbacks. Rules:
- No platform headers in
.h— usevoid *for HAL handle types. Cast inside the.cfile only. - Implement a
struct norfx_<platform>_ctxto carry the hardware handles passed throughvoid *context.
A complete STM32F4 port (HAL SPI) is located at port/stm32/f4/.
A Linux simulation port (file-backed, no hardware needed) is located at port/native_sim/linux/.
Minimal port skeleton:
/* port_myplatform.h */
#include "nor_fx.h"
struct norfx_myplatform_ctx {
void *hspi; /* SPI handle — void* to avoid HAL header in .h */
void *cs_port; /* GPIO port — void* for the same reason */
uint16_t cs_pin;
};
enum norfx_status norfx_myplatform_cs_select(void *context);
enum norfx_status norfx_myplatform_cs_deselect(void *context);
enum norfx_status norfx_myplatform_spi_write(void *context, uint8_t *data, uint16_t size);
enum norfx_status norfx_myplatform_spi_read(void *context, uint8_t *data, uint16_t size);
uint32_t norfx_myplatform_get_tick_ms(void *context);
void norfx_myplatform_delay_ms(void *context, uint32_t delay);/* port_myplatform.c */
#include "platform_hal.h" /* HAL header lives here only */
#include "port_myplatform.h"
enum norfx_status norfx_myplatform_spi_write(void *context,
uint8_t *data,
uint16_t size)
{
struct norfx_myplatform_ctx *ctx = context;
/* cast void* back to real HAL type here */
...
}The library includes a file-backed NOR flash simulator for host testing (port/native_sim/linux/). It models correct NOR flash behaviour:
- Read — returns flash content directly.
- Page program — ANDs incoming bytes with existing content (bits can only go
1→0, not0→1without an erase). - Sector erase — resets 4 KB to
0xFF. - WIP / WEL bits — simulated in Status Register 1.
- JEDEC ID — returns Winbond W25Q128JV values (
0xEF 0x70 0x18). - Persistent state — backed by a file on disk; state survives across runs.
Basic usage:
#include "nor_fx.h"
#include "port_linux.h"
norfx_sim_ctx_t ctx;
norfx_sim_init(&ctx, "flash.bin", NORFX_SIM_FLASH_SIZE);
struct norfx_device dev = {
.context = &ctx,
.spi_chip_select = norfx_sim_cs_select,
.spi_chip_deselect = norfx_sim_cs_deselect,
.spi_write = norfx_sim_spi_write,
.spi_read = norfx_sim_spi_read,
.get_tick_ms = norfx_sim_get_tick_ms,
.delay_ms = norfx_sim_delay_ms,
};
norfx_reset(&dev);
/* ... use driver normally ... */
norfx_sim_deinit(&ctx);The library uses explicit CMake options for optional components.
Default behavior:
- standalone (
PROJECT_IS_TOP_LEVEL=TRUE): builds the Linux simulation port,norfx_lfs, and tests, - submodule / parent-project mode: builds only
norfx_driverunless the parent explicitly enables other components.
Important options:
NORFX_BUILD_LFSNORFX_BUILD_PORT_NATIVE_LINUXNORFX_BUILD_PORT_STM32_F4NORFX_BUILD_TESTS
git submodule update --init --recursive # pulls Unity test framework
cmake -B build -S . -DCMAKE_BUILD_TYPE=Debug
cmake --build build
ctest --test-dir build --output-on-failureExpected output (depending on whether NORFX_BUILD_LFS is enabled):
100% tests passed, 0 tests failed out of 6
# or
100% tests passed, 0 tests failed out of 7
When consumed as a git submodule, the parent project explicitly selects the optional NORFX components it needs:
# Parent project CMakeLists.txt
set(NORFX_BUILD_PORT_STM32_F4 ON CACHE BOOL "" FORCE)
add_subdirectory(libraries/nor_fx)
# Provide STM32 HAL headers/includes to the actual STM32 port target.
target_link_libraries(norfx_port_stm32 PRIVATE stm32cubemx)
target_link_libraries(my_firmware PRIVATE
norfx_driver
norfx_port_stm32_f4
)Compatibility notes:
norfx_port_stm32remains the concrete target used to attach STM32 HAL dependencies.norfx_port_stm32_f4is provided as a clearer alias for linking from firmware targets.norfx_port_native_linuxis provided as a clearer alias for the Linux simulator target.
See ARCHITECTURE.md for a full description of the CMake target graph and design decisions.
A complete demo of the nor_fx library running on STM32 can be found in the following repository.
This project is licensed under the BSD 3-Clause License — see the LICENSE file for details.