Merge pull request #2353 from gebart/pr/nvram-api
drivers: Add NVRAM API
This commit is contained in:
commit
74e076d304
66
drivers/include/nvram-spi.h
Normal file
66
drivers/include/nvram-spi.h
Normal file
@ -0,0 +1,66 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2015 Eistec AB
|
||||||
|
*
|
||||||
|
* This file is subject to the terms and conditions of the GNU Lesser General
|
||||||
|
* Public License v2.1. See the file LICENSE in the top level directory for more
|
||||||
|
* details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @ingroup nvram
|
||||||
|
* @{
|
||||||
|
*
|
||||||
|
* @file
|
||||||
|
*
|
||||||
|
* @brief Device interface for various SPI connected NVRAM.
|
||||||
|
*
|
||||||
|
* Tested on:
|
||||||
|
* - Cypress/Ramtron FM25L04B.
|
||||||
|
*
|
||||||
|
* @author Joakim Gebart <joakim.gebart@eistec.se>
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef DRIVERS_NVRAM_SPI_H_
|
||||||
|
#define DRIVERS_NVRAM_SPI_H_
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
#include "nvram.h"
|
||||||
|
#include "periph/spi.h"
|
||||||
|
#include "periph/gpio.h"
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Bus parameters for SPI NVRAM.
|
||||||
|
*/
|
||||||
|
typedef struct nvram_spi_params {
|
||||||
|
/** @brief RIOT SPI device */
|
||||||
|
spi_t spi;
|
||||||
|
/** @brief Chip select pin */
|
||||||
|
gpio_t cs;
|
||||||
|
/** @brief Number of address bytes following each read/write command. */
|
||||||
|
uint8_t address_count;
|
||||||
|
} nvram_spi_params_t;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Initialize an nvram_t structure with SPI settings.
|
||||||
|
*
|
||||||
|
* This will also initialize the CS pin as a GPIO output, without pull resistors.
|
||||||
|
*
|
||||||
|
* @param[out] dev Pointer to NVRAM device descriptor
|
||||||
|
* @param[out] spi_params Pointer to SPI settings
|
||||||
|
* @param[in] size Device capacity
|
||||||
|
*
|
||||||
|
* @return 0 on success
|
||||||
|
* @return <0 on errors
|
||||||
|
*/
|
||||||
|
int nvram_spi_init(nvram_t *dev, nvram_spi_params_t *spi_params, size_t size);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif /* DRIVERS_NVRAM_SPI_H_ */
|
||||||
|
/** @} */
|
||||||
87
drivers/include/nvram.h
Normal file
87
drivers/include/nvram.h
Normal file
@ -0,0 +1,87 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2015 Eistec AB
|
||||||
|
*
|
||||||
|
* This file is subject to the terms and conditions of the GNU Lesser General
|
||||||
|
* Public License v2.1. See the file LICENSE in the top level directory for more
|
||||||
|
* details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @defgroup nvram Non-volatile RAM
|
||||||
|
* @ingroup drivers
|
||||||
|
* @brief Non-volatile RAM interface
|
||||||
|
*
|
||||||
|
* This API is designed around non-volatile memories which do not need blockwise
|
||||||
|
* erase, such as ferro-electric RAM (FRAM) or magneto-resistive RAM (MRAM).
|
||||||
|
*
|
||||||
|
* This interface is not suitable for flash memories.
|
||||||
|
*
|
||||||
|
* @{
|
||||||
|
*
|
||||||
|
* @file
|
||||||
|
*
|
||||||
|
* @brief Generic non-volatile RAM driver interface
|
||||||
|
* @author Joakim Gebart <joakim.gebart@eistec.se>
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef DRIVERS_NVRAM_H_
|
||||||
|
#define DRIVERS_NVRAM_H_
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stddef.h>
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* Forward declaration in order to declare function pointers which take this
|
||||||
|
* type as a parameter within the struct. */
|
||||||
|
struct nvram;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Device descriptor for generic NVRAM devices.
|
||||||
|
*/
|
||||||
|
typedef struct nvram {
|
||||||
|
/**
|
||||||
|
* @brief Pointer to device-specific read function
|
||||||
|
*
|
||||||
|
* Copy data from system memory to NVRAM.
|
||||||
|
*
|
||||||
|
* @param[in] dev Pointer to NVRAM device descriptor
|
||||||
|
* @param[out] dst Pointer to the first byte in the system memory address space
|
||||||
|
* @param[in] src Starting address in the NVRAM device address space
|
||||||
|
* @param[in] len Number of bytes to copy
|
||||||
|
*
|
||||||
|
* @return Number of bytes read on success
|
||||||
|
* @return <0 on errors
|
||||||
|
*/
|
||||||
|
int (*read)(struct nvram *dev, uint8_t *dst, uint32_t src, size_t size);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Pointer to device-specific write function
|
||||||
|
*
|
||||||
|
* Copy data from NVRAM to system memory.
|
||||||
|
*
|
||||||
|
* @param[in] dev Pointer to NVRAM device descriptor
|
||||||
|
* @param[in] src Pointer to the first byte in the system memory address space
|
||||||
|
* @param[in] dst Starting address in the NVRAM device address space
|
||||||
|
* @param[in] len Number of bytes to copy
|
||||||
|
*
|
||||||
|
* @return Number of bytes written on success
|
||||||
|
* @return <0 on errors
|
||||||
|
*/
|
||||||
|
int (*write)(struct nvram *dev, uint8_t *src, uint32_t dst, size_t size);
|
||||||
|
|
||||||
|
/** @brief Device capacity */
|
||||||
|
size_t size;
|
||||||
|
|
||||||
|
/** @brief Device-specific parameters, if any. */
|
||||||
|
void *extra;
|
||||||
|
} nvram_t;
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif /* DRIVERS_NVRAM_H_ */
|
||||||
|
/** @} */
|
||||||
3
drivers/nvram_spi/Makefile
Normal file
3
drivers/nvram_spi/Makefile
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
MODULE = nvram_spi
|
||||||
|
|
||||||
|
include $(RIOTBASE)/Makefile.base
|
||||||
283
drivers/nvram_spi/nvram-spi.c
Normal file
283
drivers/nvram_spi/nvram-spi.c
Normal file
@ -0,0 +1,283 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2015 Eistec AB
|
||||||
|
*
|
||||||
|
* This file is subject to the terms and conditions of the GNU Lesser General
|
||||||
|
* Public License v2.1. See the file LICENSE in the top level directory for more
|
||||||
|
* details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stddef.h>
|
||||||
|
#include "nvram.h"
|
||||||
|
#include "nvram-spi.h"
|
||||||
|
#include "byteorder.h"
|
||||||
|
#include "periph/spi.h"
|
||||||
|
#include "periph/gpio.h"
|
||||||
|
#include "hwtimer.h"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @ingroup nvram
|
||||||
|
* @{
|
||||||
|
*
|
||||||
|
* @file
|
||||||
|
*
|
||||||
|
* @brief Device interface for various SPI connected NVRAM.
|
||||||
|
*
|
||||||
|
* Tested on:
|
||||||
|
* - Cypress/Ramtron FM25L04B.
|
||||||
|
*
|
||||||
|
* @author Joakim Gebart <joakim.gebart@eistec.se>
|
||||||
|
*/
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
/** WRITE command byte, 0b0000 0010 */
|
||||||
|
NVRAM_SPI_CMD_WRITE = 0x02,
|
||||||
|
/** READ command byte, 0b0000 0011 */
|
||||||
|
NVRAM_SPI_CMD_READ = 0x03,
|
||||||
|
/** WREN command byte, 0b0000 0110 */
|
||||||
|
NVRAM_SPI_CMD_WREN = 0x06,
|
||||||
|
} nvram_spi_commands_t;
|
||||||
|
|
||||||
|
/** @brief Delay to wait between toggling CS pin, on most chips this can probably be
|
||||||
|
* removed. */
|
||||||
|
#define NVRAM_SPI_CS_TOGGLE_TICKS 1
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Copy data from system memory to NVRAM.
|
||||||
|
*
|
||||||
|
* @param[in] dev Pointer to NVRAM device descriptor
|
||||||
|
* @param[in] src Pointer to the first byte in the system memory address space
|
||||||
|
* @param[in] dst Starting address in the NVRAM device address space
|
||||||
|
* @param[in] len Number of bytes to copy
|
||||||
|
*
|
||||||
|
* @return Number of bytes written on success
|
||||||
|
* @return <0 on errors
|
||||||
|
*/
|
||||||
|
static int nvram_spi_write(nvram_t *dev, uint8_t *src, uint32_t dst, size_t len);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Copy data from NVRAM to system memory.
|
||||||
|
*
|
||||||
|
* @param[in] dev Pointer to NVRAM device descriptor
|
||||||
|
* @param[out] dst Pointer to the first byte in the system memory address space
|
||||||
|
* @param[in] src Starting address in the NVRAM device address space
|
||||||
|
* @param[in] len Number of bytes to copy
|
||||||
|
*
|
||||||
|
* @return Number of bytes read on success
|
||||||
|
* @return <0 on errors
|
||||||
|
*/
|
||||||
|
static int nvram_spi_read(nvram_t *dev, uint8_t *dst, uint32_t src, size_t len);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Copy data from system memory to NVRAM.
|
||||||
|
*
|
||||||
|
* This is a special form of the WRITE command used by some Ramtron/Cypress
|
||||||
|
* 4Kbit FRAM devices which puts the 9th address bit inside the command byte to
|
||||||
|
* be able to use one byte for addressing instead of two.
|
||||||
|
*
|
||||||
|
* @param[in] dev Pointer to NVRAM device descriptor
|
||||||
|
* @param[in] src Pointer to the first byte in the system memory address space
|
||||||
|
* @param[in] dst Starting address in the NVRAM device address space
|
||||||
|
* @param[in] len Number of bytes to copy
|
||||||
|
*
|
||||||
|
* @return Number of bytes written on success
|
||||||
|
* @return <0 on errors
|
||||||
|
*/
|
||||||
|
static int nvram_spi_write_9bit_addr(nvram_t *dev, uint8_t *src, uint32_t dst, size_t len);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Copy data from NVRAM to system memory.
|
||||||
|
*
|
||||||
|
* This is a special form of the READ command used by some Ramtron/Cypress 4Kbit
|
||||||
|
* FRAM devices which puts the 9th address bit inside the command byte to be
|
||||||
|
* able to use one byte for addressing instead of two.
|
||||||
|
*
|
||||||
|
* @param[in] dev Pointer to NVRAM device descriptor
|
||||||
|
* @param[out] dst Pointer to the first byte in the system memory address space
|
||||||
|
* @param[in] src Starting address in the NVRAM device address space
|
||||||
|
* @param[in] len Number of bytes to copy
|
||||||
|
*
|
||||||
|
* @return Number of bytes read on success
|
||||||
|
* @return <0 on errors
|
||||||
|
*/
|
||||||
|
static int nvram_spi_read_9bit_addr(nvram_t *dev, uint8_t *dst, uint32_t src, size_t len);
|
||||||
|
|
||||||
|
int nvram_spi_init(nvram_t *dev, nvram_spi_params_t *spi_params, size_t size)
|
||||||
|
{
|
||||||
|
dev->size = size;
|
||||||
|
if (size > 0x100 && spi_params->address_count == 1) {
|
||||||
|
dev->write = nvram_spi_write_9bit_addr;
|
||||||
|
dev->read = nvram_spi_read_9bit_addr;
|
||||||
|
} else {
|
||||||
|
dev->write = nvram_spi_write;
|
||||||
|
dev->read = nvram_spi_read;
|
||||||
|
}
|
||||||
|
dev->extra = spi_params;
|
||||||
|
|
||||||
|
gpio_init_out(spi_params->cs, GPIO_NOPULL);
|
||||||
|
gpio_set(spi_params->cs);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int nvram_spi_write(nvram_t *dev, uint8_t *src, uint32_t dst, size_t len)
|
||||||
|
{
|
||||||
|
nvram_spi_params_t *spi_dev = (nvram_spi_params_t *) dev->extra;
|
||||||
|
int status;
|
||||||
|
union {
|
||||||
|
uint32_t u32;
|
||||||
|
char c[4];
|
||||||
|
} addr;
|
||||||
|
/* Address is expected by the device as big-endian, i.e. network byte order,
|
||||||
|
* we utilize the network byte order macros here. */
|
||||||
|
addr.u32 = HTONL(dst);
|
||||||
|
/* Acquire exclusive bus access */
|
||||||
|
spi_acquire(spi_dev->spi);
|
||||||
|
/* Assert CS */
|
||||||
|
gpio_clear(spi_dev->cs);
|
||||||
|
/* Enable writes */
|
||||||
|
status = spi_transfer_byte(spi_dev->spi, NVRAM_SPI_CMD_WREN, NULL);
|
||||||
|
if (status < 0)
|
||||||
|
{
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
/* Release CS */
|
||||||
|
gpio_set(spi_dev->cs);
|
||||||
|
hwtimer_spin(NVRAM_SPI_CS_TOGGLE_TICKS);
|
||||||
|
/* Re-assert CS */
|
||||||
|
gpio_clear(spi_dev->cs);
|
||||||
|
/* Write command and address */
|
||||||
|
status = spi_transfer_regs(spi_dev->spi, NVRAM_SPI_CMD_WRITE,
|
||||||
|
&addr.c[sizeof(addr.c) - spi_dev->address_count], NULL,
|
||||||
|
spi_dev->address_count);
|
||||||
|
if (status < 0)
|
||||||
|
{
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
/* Keep holding CS and write data */
|
||||||
|
status = spi_transfer_bytes(spi_dev->spi, (char *)src, NULL, len);
|
||||||
|
if (status < 0)
|
||||||
|
{
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
/* Release CS */
|
||||||
|
gpio_set(spi_dev->cs);
|
||||||
|
/* Release exclusive bus access */
|
||||||
|
spi_release(spi_dev->spi);
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int nvram_spi_read(nvram_t *dev, uint8_t *dst, uint32_t src, size_t len)
|
||||||
|
{
|
||||||
|
nvram_spi_params_t *spi_dev = (nvram_spi_params_t *) dev->extra;
|
||||||
|
int status;
|
||||||
|
union {
|
||||||
|
uint32_t u32;
|
||||||
|
char c[4];
|
||||||
|
} addr;
|
||||||
|
/* Address is expected by the device as big-endian, i.e. network byte order,
|
||||||
|
* we utilize the network byte order macros here. */
|
||||||
|
addr.u32 = HTONL(src);
|
||||||
|
/* Acquire exclusive bus access */
|
||||||
|
spi_acquire(spi_dev->spi);
|
||||||
|
/* Assert CS */
|
||||||
|
gpio_clear(spi_dev->cs);
|
||||||
|
/* Write command and address */
|
||||||
|
status = spi_transfer_regs(spi_dev->spi, NVRAM_SPI_CMD_READ,
|
||||||
|
&addr.c[sizeof(addr.c) - spi_dev->address_count],
|
||||||
|
NULL, spi_dev->address_count);
|
||||||
|
if (status < 0)
|
||||||
|
{
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
/* Keep holding CS and read data */
|
||||||
|
status = spi_transfer_bytes(spi_dev->spi, NULL, (char *)dst, len);
|
||||||
|
if (status < 0)
|
||||||
|
{
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
/* Release CS */
|
||||||
|
gpio_set(spi_dev->cs);
|
||||||
|
/* Release exclusive bus access */
|
||||||
|
spi_release(spi_dev->spi);
|
||||||
|
/* status contains the number of bytes actually read from the SPI bus. */
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static int nvram_spi_write_9bit_addr(nvram_t *dev, uint8_t *src, uint32_t dst, size_t len)
|
||||||
|
{
|
||||||
|
nvram_spi_params_t *spi_dev = (nvram_spi_params_t *) dev->extra;
|
||||||
|
int status;
|
||||||
|
uint8_t cmd;
|
||||||
|
uint8_t addr;
|
||||||
|
cmd = NVRAM_SPI_CMD_WRITE;
|
||||||
|
/* The upper address bit is mixed into the command byte on certain devices,
|
||||||
|
* probably just to save a byte in the SPI transfer protocol. */
|
||||||
|
if (dst > 0xff) {
|
||||||
|
cmd |= 0x08;
|
||||||
|
}
|
||||||
|
/* LSB of address */
|
||||||
|
addr = (dst & 0xff);
|
||||||
|
spi_acquire(spi_dev->spi);
|
||||||
|
gpio_clear(spi_dev->cs);
|
||||||
|
/* Enable writes */
|
||||||
|
status = spi_transfer_byte(spi_dev->spi, NVRAM_SPI_CMD_WREN, NULL);
|
||||||
|
if (status < 0)
|
||||||
|
{
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
gpio_set(spi_dev->cs);
|
||||||
|
hwtimer_spin(NVRAM_SPI_CS_TOGGLE_TICKS);
|
||||||
|
gpio_clear(spi_dev->cs);
|
||||||
|
/* Write command and address */
|
||||||
|
status = spi_transfer_reg(spi_dev->spi, cmd, addr, NULL);
|
||||||
|
if (status < 0)
|
||||||
|
{
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
/* Keep holding CS and write data */
|
||||||
|
status = spi_transfer_bytes(spi_dev->spi, (char *)src, NULL, len);
|
||||||
|
if (status < 0)
|
||||||
|
{
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
gpio_set(spi_dev->cs);
|
||||||
|
spi_release(spi_dev->spi);
|
||||||
|
/* status contains the number of bytes actually written to the SPI bus. */
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int nvram_spi_read_9bit_addr(nvram_t *dev, uint8_t *dst, uint32_t src, size_t len)
|
||||||
|
{
|
||||||
|
nvram_spi_params_t *spi_dev = (nvram_spi_params_t *) dev->extra;
|
||||||
|
int status;
|
||||||
|
uint8_t cmd;
|
||||||
|
uint8_t addr;
|
||||||
|
cmd = NVRAM_SPI_CMD_READ;
|
||||||
|
/* The upper address bit is mixed into the command byte on certain devices,
|
||||||
|
* probably just to save a byte in the SPI transfer protocol. */
|
||||||
|
if (src > 0xff) {
|
||||||
|
cmd |= 0x08;
|
||||||
|
}
|
||||||
|
/* LSB of address */
|
||||||
|
addr = (src & 0xff);
|
||||||
|
spi_acquire(spi_dev->spi);
|
||||||
|
gpio_clear(spi_dev->cs);
|
||||||
|
/* Write command and address */
|
||||||
|
status = spi_transfer_reg(spi_dev->spi, (char)cmd, addr, NULL);
|
||||||
|
if (status < 0)
|
||||||
|
{
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
/* Keep holding CS and read data */
|
||||||
|
status = spi_transfer_bytes(spi_dev->spi, NULL, (char *)dst, len);
|
||||||
|
if (status < 0)
|
||||||
|
{
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
gpio_set(spi_dev->cs);
|
||||||
|
spi_release(spi_dev->spi);
|
||||||
|
/* status contains the number of bytes actually read from the SPI bus. */
|
||||||
|
return status;
|
||||||
|
}
|
||||||
34
tests/driver_nvram_spi/Makefile
Normal file
34
tests/driver_nvram_spi/Makefile
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
APPLICATION = driver_nvram_spi
|
||||||
|
include ../Makefile.tests_common
|
||||||
|
|
||||||
|
FEATURES_REQUIRED = periph_spi periph_gpio
|
||||||
|
|
||||||
|
USEMODULE += nvram_spi
|
||||||
|
USEMODULE += vtimer
|
||||||
|
|
||||||
|
ifneq (,$(TEST_NVRAM_SPI_DEV))
|
||||||
|
CFLAGS += -DTEST_NVRAM_SPI_DEV=$(TEST_NVRAM_SPI_DEV)
|
||||||
|
else
|
||||||
|
# set arbitrary default
|
||||||
|
CFLAGS += -DTEST_NVRAM_SPI_DEV=SPI_0
|
||||||
|
endif
|
||||||
|
ifneq (,$(TEST_NVRAM_SPI_CS))
|
||||||
|
CFLAGS += -DTEST_NVRAM_SPI_CS=$(TEST_NVRAM_SPI_CS)
|
||||||
|
else
|
||||||
|
# set arbitrary default
|
||||||
|
CFLAGS += -DTEST_NVRAM_SPI_CS=GPIO_0
|
||||||
|
endif
|
||||||
|
ifneq (,$(TEST_NVRAM_SPI_SIZE))
|
||||||
|
CFLAGS += -DTEST_NVRAM_SPI_SIZE=$(TEST_NVRAM_SPI_SIZE)
|
||||||
|
else
|
||||||
|
# set tiny arbitrary default
|
||||||
|
CFLAGS += -DTEST_NVRAM_SPI_SIZE=64
|
||||||
|
endif
|
||||||
|
ifneq (,$(TEST_NVRAM_SPI_ADDRESS_COUNT))
|
||||||
|
CFLAGS += -DTEST_NVRAM_SPI_ADDRESS_COUNT=$(TEST_NVRAM_SPI_ADDRESS_COUNT)
|
||||||
|
else
|
||||||
|
# set 1 address byte by default, increase if using a larger module for test.
|
||||||
|
CFLAGS += -DTEST_NVRAM_SPI_ADDRESS_COUNT=1
|
||||||
|
endif
|
||||||
|
|
||||||
|
include $(RIOTBASE)/Makefile.include
|
||||||
12
tests/driver_nvram_spi/README.md
Normal file
12
tests/driver_nvram_spi/README.md
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
# About
|
||||||
|
This is a manual test application for the SPI NVRAM driver.
|
||||||
|
|
||||||
|
# Usage
|
||||||
|
This test application will initialize the SPI bus and NVRAM device with the
|
||||||
|
following parameters:
|
||||||
|
|
||||||
|
- Baudrate: 10 MHz (overridable by setting TEST_NVRAM_SPI_SPEED)
|
||||||
|
- SPI config: SPI_CONF_FIRST_RISING (overridable by setting TEST_NVRAM_SPI_CONF)
|
||||||
|
|
||||||
|
The memory will be overwritten by the test application. The original contents
|
||||||
|
will not be restored after the test.
|
||||||
257
tests/driver_nvram_spi/main.c
Normal file
257
tests/driver_nvram_spi/main.c
Normal file
@ -0,0 +1,257 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2015 Eistec AB
|
||||||
|
*
|
||||||
|
* This file is subject to the terms and conditions of the GNU Lesser
|
||||||
|
* General Public License v2.1. See the file LICENSE in the top level
|
||||||
|
* directory for more details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @ingroup tests
|
||||||
|
* @{
|
||||||
|
*
|
||||||
|
* @file
|
||||||
|
* @brief Test application for the SPI NVRAM driver
|
||||||
|
*
|
||||||
|
* @author Joakim Gebart <joakim.gebart@eistec.se
|
||||||
|
*
|
||||||
|
* @}
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <ctype.h>
|
||||||
|
|
||||||
|
#include "board.h"
|
||||||
|
#include "vtimer.h"
|
||||||
|
#include "periph/spi.h"
|
||||||
|
#include "nvram-spi.h"
|
||||||
|
|
||||||
|
#ifndef TEST_NVRAM_SPI_DEV
|
||||||
|
#error "TEST_NVRAM_SPI_DEV not defined"
|
||||||
|
#endif
|
||||||
|
#ifndef TEST_NVRAM_SPI_CS
|
||||||
|
#error "TEST_NVRAM_SPI_CS not defined"
|
||||||
|
#endif
|
||||||
|
#ifndef TEST_NVRAM_SPI_SIZE
|
||||||
|
#error "TEST_NVRAM_SPI_SIZE not defined"
|
||||||
|
#endif
|
||||||
|
#ifndef TEST_NVRAM_SPI_ADDRESS_COUNT
|
||||||
|
#error "TEST_NVRAM_SPI_ADDRESS_COUNT not defined"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef TEST_NVRAM_SPI_CONF
|
||||||
|
#define SPI_CONF (TEST_NVRAM_SPI_CONF)
|
||||||
|
#else
|
||||||
|
#define SPI_CONF (SPI_CONF_FIRST_RISING)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef TEST_NVRAM_SPI_SPEED
|
||||||
|
#define SPI_SPEED (TEST_NVRAM_SPI_SPEED)
|
||||||
|
#else
|
||||||
|
#define SPI_SPEED (SPI_SPEED_10MHZ)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* This will only work on small memories. Modify if you need to test NVRAM
|
||||||
|
* memories which do not fit inside free RAM */
|
||||||
|
static uint8_t buf_out[TEST_NVRAM_SPI_SIZE];
|
||||||
|
static uint8_t buf_in[TEST_NVRAM_SPI_SIZE];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief xxd-like printing of a binary buffer
|
||||||
|
*/
|
||||||
|
static void print_buffer(const uint8_t * buf, size_t length) {
|
||||||
|
static const unsigned int bytes_per_line = 16;
|
||||||
|
static const unsigned int bytes_per_group = 2;
|
||||||
|
unsigned long i = 0;
|
||||||
|
while (i < length) {
|
||||||
|
unsigned int col;
|
||||||
|
for (col = 0; col < bytes_per_line; ++col) {
|
||||||
|
/* Print hex data */
|
||||||
|
if (col == 0) {
|
||||||
|
printf("\n%08lx: ", i);
|
||||||
|
}
|
||||||
|
else if ((col % bytes_per_group) == 0) {
|
||||||
|
putchar(' ');
|
||||||
|
}
|
||||||
|
if ((i + col) < length) {
|
||||||
|
printf("%02hhx", buf[i + col]);
|
||||||
|
} else {
|
||||||
|
putchar(' ');
|
||||||
|
putchar(' ');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
putchar(' ');
|
||||||
|
for (col = 0; col < bytes_per_line; ++col) {
|
||||||
|
if ((i + col) < length) {
|
||||||
|
/* Echo only printable chars */
|
||||||
|
if (isprint(buf[i + col])) {
|
||||||
|
putchar(buf[i + col]);
|
||||||
|
} else {
|
||||||
|
putchar('.');
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
putchar(' ');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
i += bytes_per_line;
|
||||||
|
}
|
||||||
|
/* end with a newline */
|
||||||
|
puts("");
|
||||||
|
}
|
||||||
|
|
||||||
|
/* weak PRNG for generating "random" test data */
|
||||||
|
static uint8_t lcg_rand8(void) {
|
||||||
|
static const uint32_t a = 1103515245;
|
||||||
|
static const uint32_t c = 12345;
|
||||||
|
static uint32_t val = 123456; /* seed value */
|
||||||
|
val = val * a + c;
|
||||||
|
return (val >> 16) & 0xff;
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(void)
|
||||||
|
{
|
||||||
|
uint32_t i;
|
||||||
|
nvram_spi_params_t spi_params = {
|
||||||
|
.spi = TEST_NVRAM_SPI_DEV,
|
||||||
|
.cs = TEST_NVRAM_SPI_CS,
|
||||||
|
.address_count = TEST_NVRAM_SPI_ADDRESS_COUNT,
|
||||||
|
};
|
||||||
|
nvram_t dev;
|
||||||
|
timex_t start_delay = {
|
||||||
|
.seconds = 10,
|
||||||
|
.microseconds = 0,
|
||||||
|
};
|
||||||
|
|
||||||
|
puts("NVRAM SPI test application starting...");
|
||||||
|
printf("Initializing SPI_%i... ", TEST_NVRAM_SPI_DEV);
|
||||||
|
if (spi_init_master(TEST_NVRAM_SPI_DEV, SPI_CONF, SPI_SPEED_10MHZ) == 0) {
|
||||||
|
puts("[OK]");
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
puts("[Failed]\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
puts("Initializing NVRAM SPI device descriptor... ");
|
||||||
|
if (nvram_spi_init(&dev, &spi_params, TEST_NVRAM_SPI_SIZE) == 0) {
|
||||||
|
puts("[OK]");
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
puts("[Failed]\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
puts("NVRAM SPI init done.\n");
|
||||||
|
|
||||||
|
puts("!!! This test will erase everything on the NVRAM !!!");
|
||||||
|
puts("!!! Unplug/reset/halt device now if this is not acceptable !!!");
|
||||||
|
puts("Waiting for 10 seconds before continuing...");
|
||||||
|
vtimer_sleep(start_delay);
|
||||||
|
|
||||||
|
puts("Reading current memory contents...");
|
||||||
|
for (i = 0; i < TEST_NVRAM_SPI_SIZE; ++i) {
|
||||||
|
if (dev.read(&dev, &buf_in[i], i, 1) != 1) {
|
||||||
|
puts("[Failed]\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
puts("[OK]");
|
||||||
|
puts("NVRAM contents before test:");
|
||||||
|
print_buffer(buf_in, sizeof(buf_in));
|
||||||
|
|
||||||
|
puts("Writing bytewise 0xFF to device");
|
||||||
|
|
||||||
|
memset(buf_out, 0xff, sizeof(buf_out));
|
||||||
|
for (i = 0; i < TEST_NVRAM_SPI_SIZE; ++i) {
|
||||||
|
if (dev.write(&dev, &buf_out[i], i, 1) != 1) {
|
||||||
|
puts("[Failed]\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
if (buf_out[i] != 0xff) {
|
||||||
|
puts("nvram_spi_write modified *src!");
|
||||||
|
printf(" i = %08lx\n", (unsigned long) i);
|
||||||
|
puts("[Failed]\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
puts("Reading back blockwise");
|
||||||
|
memset(buf_in, 0x00, sizeof(buf_in));
|
||||||
|
if (dev.read(&dev, buf_in, 0, TEST_NVRAM_SPI_SIZE) != TEST_NVRAM_SPI_SIZE) {
|
||||||
|
puts("[Failed]\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
puts("[OK]");
|
||||||
|
puts("Verifying contents...");
|
||||||
|
if (memcmp(buf_in, buf_out, TEST_NVRAM_SPI_SIZE) != 0) {
|
||||||
|
puts("[Failed]\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
puts("[OK]");
|
||||||
|
|
||||||
|
puts("Writing blockwise address complement to device");
|
||||||
|
for (i = 0; i < TEST_NVRAM_SPI_SIZE; ++i) {
|
||||||
|
buf_out[i] = (~(i)) & 0xff;
|
||||||
|
}
|
||||||
|
if (dev.write(&dev, buf_out, 0, TEST_NVRAM_SPI_SIZE) != TEST_NVRAM_SPI_SIZE) {
|
||||||
|
puts("[Failed]\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
puts("[OK]");
|
||||||
|
puts("buf_out:");
|
||||||
|
print_buffer(buf_out, sizeof(buf_out));
|
||||||
|
puts("Reading back blockwise");
|
||||||
|
memset(buf_in, 0x00, sizeof(buf_in));
|
||||||
|
if (dev.read(&dev, buf_in, 0, TEST_NVRAM_SPI_SIZE) != TEST_NVRAM_SPI_SIZE) {
|
||||||
|
puts("[Failed]\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
puts("[OK]");
|
||||||
|
puts("Verifying contents...");
|
||||||
|
if (memcmp(buf_in, buf_out, TEST_NVRAM_SPI_SIZE) != 0) {
|
||||||
|
puts("buf_in:");
|
||||||
|
print_buffer(buf_in, sizeof(buf_in));
|
||||||
|
puts("[Failed]\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
puts("[OK]");
|
||||||
|
|
||||||
|
puts("Generating pseudo-random test data...");
|
||||||
|
|
||||||
|
for (i = 0; i < TEST_NVRAM_SPI_SIZE; ++i) {
|
||||||
|
buf_out[i] = lcg_rand8();
|
||||||
|
}
|
||||||
|
|
||||||
|
puts("buf_out:");
|
||||||
|
print_buffer(buf_out, sizeof(buf_out));
|
||||||
|
|
||||||
|
puts("Writing blockwise data to device");
|
||||||
|
if (dev.write(&dev, buf_out, 0, TEST_NVRAM_SPI_SIZE) != TEST_NVRAM_SPI_SIZE) {
|
||||||
|
puts("[Failed]\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
puts("[OK]");
|
||||||
|
|
||||||
|
puts("Reading back blockwise");
|
||||||
|
memset(buf_in, 0x00, sizeof(buf_in));
|
||||||
|
if (dev.read(&dev, buf_in, 0, TEST_NVRAM_SPI_SIZE) != TEST_NVRAM_SPI_SIZE) {
|
||||||
|
puts("[Failed]\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
puts("[OK]");
|
||||||
|
puts("Verifying contents...");
|
||||||
|
if (memcmp(buf_in, buf_out, TEST_NVRAM_SPI_SIZE) != 0) {
|
||||||
|
puts("buf_in:");
|
||||||
|
print_buffer(buf_in, sizeof(buf_in));
|
||||||
|
puts("[Failed]\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
puts("[OK]");
|
||||||
|
|
||||||
|
puts("All tests passed!");
|
||||||
|
|
||||||
|
while(1);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user