API

The data API is centered around lftl_ctx_t.

Return codes

LFTL_SUCCESS 0x00

Success.

LFTL_ERROR_VERSION_COLLISION 0x01

Internal error / corruption: area contains two or more slots with the same version.

LFTL_ERROR_NO_VALID_VERSION 0x02

Internal error / corruption: area does not contains any valid slot.

LFTL_ERROR_FIRST_NOT_IN_DATA 0x03

Error: base address is not within the valid data address range for this area.

LFTL_ERROR_LAST_NOT_IN_DATA 0x04

Error: last address is not within the valid data address range for this area.

LFTL_ERROR_BASE_MISALIGNED 0x05

Error: base address is not aligned.

LFTL_ERROR_SIZE_MISALIGNED 0x06

Error: size is not aligned.

LFTL_ERROR_TRANSACTION_ONGOING 0x07

Error: a basic write is attempted during a transaction.

LFTL_ERROR_NO_TRANSACTION 0x08

Error: a transaction API call is attempted outside of a transaction.

LFTL_ERROR_TRANSACTION_OVERWRITE 0x09

Error: the same address is written twice during a transaction.

LFTL_ERROR_WU_SIZE_TOO_LARGE 0x0A

Error: the write unit size is too large, max is defined by LFTL_WU_MAX_SIZE.

LFTL_ERROR_LOW_LEVEL_ERASE 0x0100

Base value for errors reported by the erase function. Bits 0 to 7 may give more details.

LFTL_ERROR_LOW_LEVEL_WRITE 0x0200

Base value for error reported by the write function. Bits 0 to 7 may give more details.

LFTL_ERROR_LOW_LEVEL_READ 0x0300

Base value for error reported by the read function. Bits 0 to 7 may give more details.

LFTL_INTERNAL_ERROR -1

Most likely a bug in LFTL or a hardware level issue.

Meta information API

Functions in this group can be called at any time.

const char *lftl_version()

Return lean-ftl library version as a string.

lean-ftl follows https://semver.org/

Release versions have the format vX.Y.Z where X is MAJOR, Y is MINOR and Z is PATCH

return:

the version as a null terminated string

uint64_t lftl_version_timestamp()

Return lean-ftl library version timestamp.

lean-ftl version timestamp is the Unix epoch of the commit in git repository.

When a build is “dirty” or with “untracked” files the version timestamp is the Unix epoch of the build

return:

the version timestamp

const char *lftl_build_type()

Return lean-ftl library build type.

lean-ftl can be built for different goals. Currently the following build types are supported:

  • debug

  • minSizeRel

return:

the build type as a null terminated string

Initialization API

void lftl_init_lib()

Initialize LFTL library.

This un-register any previously registered areas. Call this before any other function (except the Meta information API group).

void lftl_register_area(lftl_ctx_t *ctx)

Register an LFTL area.

Register and LFTL area. This allows to be able to copy data from the LFTL area into another by using the write functions, i.e., without intermediate buffering. Call this for each area, after lftl_init_lib and before any other function (except the Meta information API group).

param ctx:

Context of the LFTL area to register

void lftl_format(lftl_ctx_t *ctx)

Format an LFTL area.

This call is not covered by anti-tearing, this is the only one. In order to erase data with anti-tearing, use lftl_erase_all or keep track of operations using a variable in another LFTL area.

param ctx:

Context of the target LFTL area

Main API

All functions in this group are covered by anti-tearing.

They shall be called with a valid context on a properly formated LFTL area. See lftl_format for the initial formating of an LFTL area. See lftl_init_lib and lftl_register_area for the initialization of contexts.

lftl_ctx_t *lftl_get_ctx(const void *const addr)

Retrieve the LFTL context correponding to an address, if any.

param addr:

Address to search

return:

a valid LFTL context or LFTL_INVALID_POINTER

void lftl_erase_all(lftl_ctx_t *ctx)

Erase all the data of an LFTL area.

Note that the erasure is ‘logical’. At physical level, previous version of the data may still remain. See lftl_format for erasing all data at physical level.

param ctx:

Context of the target LFTL area

void lftl_transaction_start(lftl_ctx_t *ctx, void *const transaction_tracker)

Start a transaction.

transaction_tracker is used from this call until a call to lftl_transaction_commit or lftl_transaction_abort.

Performance considerations: this function erase one slot.

param ctx:

Context of the target LFTL area

param transaction_tracker:

Volatile buffer, see LFTL_TRANSACTION_TRACKER_SIZE

void lftl_transaction_commit(lftl_ctx_t *ctx)

Commit a transaction.

After this call, the transaction_tracker buffer can be discarded.

Performance considerations: this function writes at most one slot + meta data.

param ctx:

Context of the target LFTL area

void lftl_transaction_abort(lftl_ctx_t *ctx)

Abort a transaction.

After this call, the transaction_tracker buffer can be discarded.

param ctx:

Context of the target LFTL area

void lftl_write(lftl_ctx_t *ctx, void *dst_nvm_addr, const void *const src, uintptr_t size)

Write aligned data to NVM.

This function detects if the write is part of a transaction or not.

param ctx:

Context of the target LFTL area

param dst_nvm_addr:

Destination address, it MUST be within the target LFTL area, MUST be aligned on a write unit (LFTL_WU_SIZE)

param src:

Source address, if it is in NVM, it MUST be in the same LFTL area as ctx or outside of any LFTL area

param size:

Size in bytes, MUST be multiple of write unit size (LFTL_WU_SIZE)

void lftl_read(lftl_ctx_t *ctx, void *dst, const void *const src_nvm_addr, uintptr_t size)

Read data from LFTL area.

When a transaction is on-going, read the current data, i.e., the data as it was before the start of the transaction.

param ctx:

Context of the target LFTL area

param dst:

Destination address, it shall be in a volatile memory

param src_nvm_addr:

Source address, it shall be within the target LFTL area

param size:

Size in bytes

void lftl_read_newer(lftl_ctx_t *ctx, void *dst, const void *const src_nvm_addr, uintptr_t size)

Read data from LFTL area.

When a transaction is on-going, read the new data, i.e., the data written but not commited yet. Keep it mind that the transaction may be aborded, so the data read by that function may not be available anymore.

param ctx:

Context of the target LFTL area

param dst:

Destination address, it shall be in a volatile memory

param src_nvm_addr:

Source address, it shall be within the target LFTL area

param size:

Size in bytes

void lftl_memread(void *dst, const void *const src, uintptr_t size)

Read data from an LFTL area, a regular NVM area or regular memory.

When a transaction is on-going, read the current data, i.e., the data as it was before the start of the transaction.

param dst:

Destination address, it shall be in a volatile memory

param src_nvm_addr:

Source address, it shall be within the target LFTL area

param size:

Size in bytes

void lftl_memread_newer(void *dst, const void *const src, uintptr_t size)

Read data from an LFTL area, a regular NVM area or regular memory.

When a transaction is on-going, read the new data, i.e., the data written but not commited yet. Keep it mind that the transaction may be aborded, so the data read by that function may not be available anymore.

param dst:

Destination address, it shall be in a volatile memory

param src_nvm_addr:

Source address, it shall be within the target LFTL area

param size:

Size in bytes

Low level API

Prefere using functions from the Main API unless you know what you are doing.

All functions in this group are covered by anti-tearing. They shall be called with a valid context on a properly formated LFTL area. See lftl_format for the initial formating of an LFTL area.

void lftl_basic_write(lftl_ctx_t *ctx, void *dst_nvm_addr, const void *const src, uintptr_t size)

Write data to NVM in an LFTL area.

Update partially or totally an LFTL area. This function is not allowed when a transaction is on-going.

Performance considerations: Each call performs a full erase and write of one slot and meta-data. The only exception is if size is 0, then it just returns without touching the NVM at all.

param ctx:

LFTL area context

param dst_nvm_addr:

Destination address, it MUST be within the target LFTL area

param src:

Source address, if it is in NVM, it MUST be in the same LFTL area as ctx or outside of any LFTL area

param size:

Size in bytes

void lftl_transaction_write(lftl_ctx_t *ctx, void *dst_nvm_addr, const void *const src, uintptr_t size)

Write aligned data to NVM in an LFTL area.

Update partially or totally an LFTL area. This function is allowed only when a transaction is on-going.

Performance considerations: Each call writes the specified number of bytes to NVM.

param ctx:

LFTL area context

param dst_nvm_addr:

Destination address, it MUST be within the target LFTL area, MUST be aligned on a write unit (LFTL_WU_SIZE)

param src:

Source address, if it is in NVM, it MUST be in the same LFTL area as ctx or outside of any LFTL area

param size:

Size in bytes, MUST be multiple of write unit size (LFTL_WU_SIZE)

void lftl_transaction_read(lftl_ctx_t *ctx, void *dst, const void *const src_nvm_addr, uintptr_t size)

Read data from NVM.

This function is allowed only when a transaction is on-going, It reads the new data, i.e., the data written but not commited yet. Keep it mind that the transaction may be aborded, so the data read by that function may not be available anymore.

param ctx:

Context of the target LFTL area

param dst:

Destination address, it MUST be in a volatile memory

param src_nvm_addr:

Source address, it MUST be within the target LFTL area

param size:

Size in bytes

Foot-guns API

Functions in this group are useful but can break transaction mechanism if used incorrectly.

They remove restrictions on data alignement and size of lftl_write and lftl_transaction_write. They work well as long as you do not target the same write unit twice during a transaction. If it happens, the behavior depends on the target platform. It can be:

  • hardware fault raised during the second write

  • hardware fault raise when reading back

  • no hardware fault but incorrect read value

All functions in this group are covered by anti-tearing. They shall be called with a valid context on a properly formated LFTL area. See lftl_format for the initial formating of an LFTL area.

void lftl_write_any(lftl_ctx_t *ctx, void *dst_nvm_addr, const void *const src, uintptr_t size)

Write aligned or unaligned data to NVM.

This function detects if the write is part of a transaction or not.

param ctx:

Context of the target LFTL area

param dst_nvm_addr:

Destination address, it MUST be within the target LFTL area

param src:

Source address, if it is in NVM, it MUST be in the same LFTL area as ctx or outside of any LFTL area

param size:

Size in bytes

void lftl_transaction_write_any(lftl_ctx_t *ctx, void *dst_nvm_addr, const void *const src, uintptr_t size)

Write aligned or unaligned data to NVM in an LFTL area.

Update partially or totally an LFTL area. This function is allowed only when a transaction is on-going.

Performance considerations: Each call writes the specified number of bytes to NVM.

param ctx:

LFTL area context

param dst_nvm_addr:

Destination address, it MUST be within the target LFTL area

param src:

Source address, if it is in NVM, it MUST be in the same LFTL area as ctx or outside of any LFTL area

param size:

Size in bytes

Defines

LFTL_INVALID_POINTER ((void*)-1)

Value used for invalid pointers.

LFTL_WU_MAX_SIZE 128
LFTL_TRANSACTION_TRACKER_SIZE_LL(data_size, write_size)

(\

((((data_size)+(write_size)-1)/(write_size))+7)\

/8)


Compute the size required for transaction_tracker.

See lftl_transaction_start.

Parameters:
  • data_size – Size of the data in the target LFTL area, in bytes

  • write_size – Size of the minimum write unit in the NVM, in bytes

LFTL_TRANSACTION_TRACKER_SIZE(ctx) LFTL_TRANSACTION_TRACKER_SIZE_LL((ctx)->data_size,(ctx)->nvm_props->write_size)

Compute the size required for transaction_tracker.

See lftl_transaction_start.

Parameters:
  • ctx – Pointer to lftl_ctx_t

LFTL_META_N_ITEMS 3
SIZE64(size) (((size)+7)/8)

Convert a size in bytes into the minimum number of uint64_t.

BITS_PER_BYTE 8

Number of bits in one byte.

LFTL_DIV_CEIL(d, q) (((d)+(q)-1)/(q))

Division with rounding to ceiling.

STR_INNER(x) # x
STR(x) STR_INNER(x)
CONCAT_INNER(x, y) x ## y
CONCAT(x, y) CONCAT_INNER(x,y)
BYTES_TO_BITS_1 8
BYTES_TO_BITS_2 16
BYTES_TO_BITS_4 32
BYTES_TO_BITS_8 64
LFTL_DAT_TYPE_SIZE LFTL_WU_SIZE
LFTL_DAT_TYPE_WIDTH CONCAT(BYTES_TO_BITS_ , LFTL_DAT_TYPE_SIZE)
DAT_PER_WU 1
SIZE_LFTL_DAT(size) (((size)+LFTL_DAT_TYPE_SIZE-1)/LFTL_DAT_TYPE_SIZE)
LFTL_DAT_TYPE_BUILDER(VAL) uint ## VAL ## _t
LFTL_DAT_TYPE_BUILDER2(VAL) LFTL_DAT_TYPE_BUILDER(VAL)
LFTL_DAT_TYPE LFTL_DAT_TYPE_BUILDER2(LFTL_DAT_TYPE_WIDTH)
LFTL_ROUND_UP(val, unit) (LFTL_DIV_CEIL(val,unit)*(unit))

Round a value to the minimum number of multiples of a unit.

LFTL_WU64(size) (SIZE64(LFTL_ROUND_UP(size,LFTL_WU_SIZE)))

Convert a size into the minimum number of write units and then convert to number of uint64_t.

LFTL_WU_DAT(size) (SIZE_LFTL_DAT(LFTL_ROUND_UP(size,LFTL_WU_SIZE)))

Convert a size into the minimum number of write units and then convert to number of lftl_dat_t.

LFTL_PAGES(size) (LFTL_DIV_CEIL(size,LFTL_PAGE_SIZE))
LFTL_AREA(name, area_content, wear_leveling_factor)

union {\

flash_sw_page_t name##_pages[

LFTL_PAGES(sizeof(struct {area_content})+LFTL_META_N_ITEMS

*LFTL_WU_SIZE)*(wear_leveling_factor)];\

struct {area_content} name##_data;\

struct {area_content};\

struct {area_content} _##name##_data;\

};


Declare an LFTL area.

Parameters:
  • name – Name of the LFTL area.

  • area_content – Members of the LFTL area.

  • wear_leveling_factor – Only integers are supported, minimum is 2.

LFTL_WEAR_LEVELING_FACTOR(x) x

Macro to declare the wear leveling factor of an area.

LFTL_DATA(name, size) lftl_dat_t name[LFTL_WU_DAT(size)]

Legacy.

LFTL_RAW_DATA(name, size)   union {\   lftl_dat_t name##_phy[LFTL_WU_DAT

(size)];\

uint8_t name[size];\

};


Declare raw data within an LFTL area.

LFTL_VAR(type, name)   union {\   lftl_dat_t name##_phy[LFTL_WU_DAT

(sizeof(type))];\

type name;\

};


Declare a variable within an LFTL area.

LFTL_ARRAY(type, name, n_elem)   union {\   lftl_dat_t name##_phy[LFTL_WU_DAT

(sizeof(type))*n_elem];\

type name[n_elem];\

};


Declare an array within an LFTL area.

LFTL_COMPACT_ARRAY(type, name, n_elem)   union {\   lftl_dat_t name##_phy[LFTL_WU_DAT

(sizeof(type)*n_elem)];\

type name[n_elem];\

};


Declare compact within an LFTL area.

LFTL_READ_WHOLE_DATA(dst, area)

do{\

uint8_t*__dst8 = (uint8_t*)dst;\

const size_t __size = sizeof(nvm. _## area ## _data);\

lftl_read

(&area,__dst8,&nvm. _## area ## _data,__size);\

dst = __dst8+__size;\

}while(0)


Read an entire LFTL area and increment destination buffer.

LFTL_WRITE_WHOLE_DATA(area, src)

do{\

uint8_t*__src8 = (uint8_t*)src;\

const size_t __size = sizeof(nvm. _## area ## _data);\

lftl_write

(&area,&nvm. _## area ## _data,__src8,__size);\

src = __src8+__size;\

}while(0)


Write an entire LFTL area and increment destination buffer.

LFTL_MEMSET_WHOLE_AREA(area, value)

do{ \

uint8_t transaction_tracker[

LFTL_TRANSACTION_TRACKER_SIZE(&area)]; \   lftl_transaction_start

(&area, transaction_tracker); \

flash_sw_page_t buf; \

memset(buf,value,sizeof(buf)); \

const unsigned int n_loops = area.data_size / sizeof(buf); \

uint32_t remaining = area.data_size; \

flash_sw_page_t*wr_addr = area.data; \

for(unsigned int i=0;i<n_loops;i++){ \

lftl_transaction_write

(&area,wr_addr,buf,sizeof(buf)); \

remaining -= sizeof(buf); \

wr_addr++; \

} \

lftl_transaction_write(&area,wr_addr,buf,remaining); \   lftl_transaction_commit

(&area); \

}while(0)


Set the same byte value on an entire LFTL area.

Typedefs

typedef struct lftl_nvm_props_struct lftl_nvm_props_t
typedef uint8_t (*nvm_erase_t)(void *base_address, unsigned int n_pages)

Callback to erase physical NVM.

It erases one or more pages. If n_pages = 0 it shall immediately return 0.

Param base_address:

The start address of the range to erase

Param n_pages:

The number of physical pages to erase

Return:

0 or an error code

typedef uint8_t (*nvm_write_t)(void *dst_nvm_addr, const void *const src, uintptr_t size)

Callback to write physical NVM.

It writes one or more bytes.

If size = 0 it shall immediately return 0.

The destination range may cross multiple physical page boundaries.

The source may be within the NVM but is guaranteed to not overlap with the destination range.

Param dst_nvm_addr:

The start address of the destination. It is always aligned on LFTL_WU_SIZE.

Param src:

The start address of the source. It may be misaligned if top level API are called with misaligned source address.

Param size:

The size in bytes of the data to write. It is always a multiple of LFTL_WU_SIZE.

Return:

0 or an error code

typedef uint8_t (*nvm_read_t)(void *dst, const void *const src_nvm_addr, uintptr_t size)

Callback to read physical NVM.

It reads one or more bytes.

If size = 0 it shall immediately return 0.

The source range may cross multiple physical page boundaries.

The destination is in a regular RAM.

Param dst:

The start address of the destination

Param src_nvm_addr:

The start address of the source

Param size:

The size in bytes of the data to write

Return:

0 or an error code

typedef void (*error_handler_t)(uint32_t err_code)

Callback for error handling.

It is called to handle fatal errors, it shall not return.

A typical implementation will longjump.

Param err_code:

The error code

typedef struct lftl_ctx_struct lftl_ctx_t
typedef LFTL_DAT_TYPE lftl_dat_t
typedef lftl_dat_t __attribute__((aligned(LFTL_PAGE_SIZE))) flash_sw_page_t[SIZE_LFTL_DAT(LFTL_PAGE_SIZE)]

Functions

struct lftl_wu_struct __attribute__((aligned(sizeof( lftl_dat_t ))))

Variables

lftl_dat_t dat[DAT_PER_WU]
struct lftl_nvm_props_struct

Properties of the physical NVM.

Public Members

void *base

the base address of the entire NVM

uintptr_t size

the size of the entire NVM (used to select between direct access and nvm_read_t)

uint32_t write_size

at least what the NVM is supporting, or a multiple of it

uint32_t erase_size

at least what the NVM is supporting, or a multiple of it

struct lftl_ctx_struct

Structure defining the context for an LFTL area.

User shall initialize each member before calling any LFTL function.

Public Members

lftl_nvm_props_t *nvm_props

Properties of the targeted NVM.

void *area

Base address of the LFTL area.

uintptr_t area_size

Total size in bytes of the LFTL area.

void *data

Initialize it to LFTL_INVALID_POINTER.

uintptr_t data_size

Size in bytes of the data in this LFTL area.

nvm_erase_t erase

Erase function for this area.

nvm_write_t write

Write function for this area.

nvm_read_t read

Read function for this area.

error_handler_t error_handler

Error handler function for this area.

void *transaction_tracker

Initialize it LFTL_INVALID_POINTER.

void *next

Initialize it LFTL_INVALID_POINTER.

struct lftl_wu_struct

Struct which has the same size as a NVM write unit.

It is used to defined the type lftl_wu_t.

lftl_wu_t has the same size and the same alignement as a NVM write unit.

Public Members

lftl_dat_t dat[DAT_PER_WU]