Import Mbed OS hard-float snapshot

This commit is contained in:
Beslan
2026-06-01 20:15:04 +03:00
commit d3738e2f89
16278 changed files with 10628036 additions and 0 deletions

View File

@@ -0,0 +1,194 @@
/* mbed Microcontroller Library
* Copyright (c) 2018 ARM Limited
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "greentea-client/test_env.h"
#include "unity.h"
#include "utest.h"
#include "BufferedBlockDevice.h"
#include "HeapBlockDevice.h"
#include <stdlib.h>
using namespace utest::v1;
static const bd_size_t heap_erase_size = 512;
static const bd_size_t num_blocks = 4;
typedef struct {
bd_size_t read_size;
bd_size_t prog_size;
} sizes_t;
static const int num_tests = 4;
sizes_t sizes[num_tests] = {
{1, 1},
{1, 128},
{4, 256},
{256, 512}
};
void functionality_test()
{
for (int i = 0; i < num_tests; i++) {
bd_size_t heap_read_size = sizes[i].read_size;
bd_size_t heap_prog_size = sizes[i].prog_size;
printf("Testing read size of %lld, prog size of %lld\n", heap_read_size, heap_prog_size);
uint8_t *read_buf, *write_buf;
read_buf = new (std::nothrow) uint8_t[heap_erase_size];
TEST_SKIP_UNLESS_MESSAGE(read_buf, "Not enough memory for test");
write_buf = new (std::nothrow) uint8_t[heap_erase_size];
TEST_SKIP_UNLESS_MESSAGE(write_buf, "Not enough memory for test");
uint8_t *dummy = new (std::nothrow) uint8_t[num_blocks * heap_erase_size + heap_prog_size + heap_read_size];
TEST_SKIP_UNLESS_MESSAGE(dummy, "Not enough memory for test");
delete[] dummy;
HeapBlockDevice *heap_bd = new HeapBlockDevice(num_blocks * heap_erase_size, heap_read_size, heap_prog_size, heap_erase_size);
BufferedBlockDevice *bd = new BufferedBlockDevice(heap_bd);
int err = bd->init();
TEST_ASSERT_EQUAL(0, err);
TEST_ASSERT_EQUAL(1, bd->get_read_size());
TEST_ASSERT_EQUAL(1, bd->get_program_size());
TEST_ASSERT_EQUAL(heap_erase_size, bd->get_erase_size());
for (bd_size_t i = 0; i < num_blocks; i++) {
memset(write_buf, i, heap_erase_size);
err = heap_bd->program(write_buf, i * heap_erase_size, heap_erase_size);
// Heap BD allocates memory on each program, so failure here indicates
// lack of memory - just skip test.
TEST_SKIP_UNLESS_MESSAGE(!err, "Not enough memory for test");
}
err = bd->read(read_buf, heap_erase_size + heap_erase_size / 2, 1);
TEST_ASSERT_EQUAL(0, err);
TEST_ASSERT_EQUAL(1, read_buf[0]);
err = bd->read(read_buf, 2 * heap_erase_size + heap_erase_size / 2, 4);
TEST_ASSERT_EQUAL(0, err);
memset(write_buf, 2, 4);
TEST_ASSERT_EQUAL_UINT8_ARRAY(write_buf, read_buf, 4);
memset(write_buf, 1, heap_erase_size);
memset(write_buf + 64, 0x5A, 8);
memset(write_buf + 72, 0xA5, 8);
err = bd->program(write_buf + 64, heap_erase_size + 64, 8);
TEST_ASSERT_EQUAL(0, err);
err = bd->program(write_buf + 72, heap_erase_size + 72, 8);
TEST_ASSERT_EQUAL(0, err);
err = bd->read(read_buf, heap_erase_size, heap_erase_size);
TEST_ASSERT_EQUAL(0, err);
TEST_ASSERT_EQUAL_UINT8_ARRAY(write_buf, read_buf, heap_erase_size);
memset(write_buf, 1, heap_erase_size);
// Underlying BD should not be updated before sync
err = heap_bd->read(read_buf, heap_erase_size, heap_erase_size);
TEST_ASSERT_EQUAL(0, err);
if (heap_prog_size > 1) {
TEST_ASSERT_EQUAL_UINT8_ARRAY(write_buf, read_buf, heap_erase_size);
}
err = bd->sync();
TEST_ASSERT_EQUAL(0, err);
memset(write_buf + 64, 0x5A, 8);
memset(write_buf + 72, 0xA5, 8);
// Should be updated now
err = bd->read(read_buf, heap_erase_size, heap_erase_size);
TEST_ASSERT_EQUAL(0, err);
TEST_ASSERT_EQUAL_UINT8_ARRAY(write_buf, read_buf, heap_erase_size);
err = heap_bd->read(read_buf, heap_erase_size, heap_erase_size);
TEST_ASSERT_EQUAL(0, err);
TEST_ASSERT_EQUAL_UINT8_ARRAY(write_buf, read_buf, heap_erase_size);
memset(write_buf, 0xAA, 16);
memset(write_buf + 16, 0xBB, 16);
err = bd->program(write_buf, 3 * heap_erase_size - 16, 32);
TEST_ASSERT_EQUAL(0, err);
// First block should sync, but second still shouldn't
memset(write_buf, 2, heap_erase_size - 16);
memset(write_buf + heap_erase_size - 16, 0xAA, 16);
err = heap_bd->read(read_buf, 2 * heap_erase_size, heap_erase_size);
TEST_ASSERT_EQUAL(0, err);
TEST_ASSERT_EQUAL_UINT8_ARRAY(write_buf, read_buf, heap_erase_size);
memset(write_buf, 3, heap_erase_size);
err = heap_bd->read(read_buf, 3 * heap_erase_size, heap_erase_size);
TEST_ASSERT_EQUAL(0, err);
if (heap_prog_size > 1) {
TEST_ASSERT_EQUAL_UINT8_ARRAY(write_buf, read_buf, heap_erase_size);
}
memset(write_buf, 0xBB, 16);
err = bd->read(read_buf, 3 * heap_erase_size, heap_erase_size);
TEST_ASSERT_EQUAL(0, err);
TEST_ASSERT_EQUAL_UINT8_ARRAY(write_buf, read_buf, heap_erase_size);
// Writing to another block should automatically sync
err = bd->program(write_buf, 15, 1);
TEST_ASSERT_EQUAL(0, err);
err = heap_bd->read(read_buf, 3 * heap_erase_size, heap_erase_size);
TEST_ASSERT_EQUAL(0, err);
TEST_ASSERT_EQUAL_UINT8_ARRAY(write_buf, read_buf, heap_erase_size);
err = bd->read(read_buf, 3 * heap_erase_size, heap_erase_size);
TEST_ASSERT_EQUAL(0, err);
TEST_ASSERT_EQUAL_UINT8_ARRAY(write_buf, read_buf, heap_erase_size);
// Unaligned reads and writes
memset(write_buf, 2, 41);
memset(write_buf + 41, 0x39, 21);
err = bd->program(write_buf + 41, 2 * heap_erase_size + 41, 21);
TEST_ASSERT_EQUAL(0, err);
err = heap_bd->read(read_buf, 2 * heap_erase_size, heap_erase_size);
TEST_ASSERT_EQUAL(0, err);
if (heap_prog_size > 1) {
TEST_ASSERT_EQUAL_UINT8_ARRAY(write_buf, read_buf, 41);
TEST_ASSERT_EQUAL_UINT8_ARRAY(write_buf, read_buf + 41, 21);
}
err = bd->read(read_buf, 2 * heap_erase_size + 4, 41 + 21 - 4);
TEST_ASSERT_EQUAL(0, err);
TEST_ASSERT_EQUAL_UINT8_ARRAY(write_buf + 4, read_buf, 41 + 21 - 4);
bd->sync();
err = heap_bd->read(read_buf, 2 * heap_erase_size, heap_erase_size);
TEST_ASSERT_EQUAL(0, err);
TEST_ASSERT_EQUAL_UINT8_ARRAY(write_buf, read_buf, 41 + 21);
err = bd->read(read_buf, 2 * heap_erase_size + 10, 41 + 21 - 10);
TEST_ASSERT_EQUAL(0, err);
TEST_ASSERT_EQUAL_UINT8_ARRAY(write_buf + 10, read_buf, 41 + 21 - 10);
bd->deinit();
delete[] read_buf;
delete[] write_buf;
delete bd;
delete heap_bd;
}
}
// Test setup
utest::v1::status_t test_setup(const size_t number_of_cases)
{
GREENTEA_SETUP(30, "default_auto");
return verbose_test_setup_handler(number_of_cases);
}
Case cases[] = {
Case("BufferedBlockDevice functionality test", functionality_test),
};
Specification specification(test_setup, cases);
int main()
{
return !Harness::run(specification);
}

View File

@@ -0,0 +1,147 @@
/* mbed Microcontroller Library
* Copyright (c) 2018 ARM Limited
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "mbed.h"
#include "greentea-client/test_env.h"
#include "unity.h"
#include "utest.h"
#include "FlashSimBlockDevice.h"
#include "HeapBlockDevice.h"
#include <stdlib.h>
using namespace utest::v1;
static const bd_size_t read_size = 1;
static const bd_size_t prog_size = 8;
static const bd_size_t erase_size = 512;
static const bd_size_t num_blocks = 4;
static const bd_size_t test_buf_size = 64;
static const uint8_t blank = 0xFF;
// Simple test for all APIs
void functionality_test()
{
uint8_t *dummy = new (std::nothrow) uint8_t[num_blocks * erase_size];
TEST_SKIP_UNLESS_MESSAGE(dummy, "Not enough memory for test");
delete[] dummy;
HeapBlockDevice heap_bd(num_blocks * erase_size, read_size, prog_size, erase_size);
FlashSimBlockDevice bd(&heap_bd, blank);
int err = bd.init();
TEST_ASSERT_EQUAL(0, err);
uint8_t read_buf[test_buf_size], write_buf[test_buf_size];
TEST_ASSERT_EQUAL(num_blocks * erase_size, bd.size());
TEST_ASSERT_EQUAL(read_size, bd.get_read_size());
TEST_ASSERT_EQUAL(prog_size, bd.get_program_size());
TEST_ASSERT_EQUAL(erase_size, bd.get_erase_size());
TEST_ASSERT_EQUAL(blank, bd.get_erase_value());
srand(1);
for (bd_size_t i = 0; i < test_buf_size; i++) {
write_buf[i] = 0xff & rand();
}
// Make sure we can't program if not erased (even after init)
err = bd.program(write_buf, 0, test_buf_size);
TEST_ASSERT_EQUAL(BD_ERROR_NOT_ERASED, err);
err = bd.erase(0, erase_size * 2);
TEST_ASSERT_EQUAL(0, err);
err = bd.program(write_buf, 0, test_buf_size);
TEST_ASSERT_EQUAL(0, err);
// Allow programming same data
err = bd.program(write_buf, 0, test_buf_size);
TEST_ASSERT_EQUAL(0, err);
srand(2);
for (bd_size_t i = 0; i < test_buf_size; i++) {
write_buf[i] = 0xff & rand();
}
err = bd.program(write_buf, 0, test_buf_size);
TEST_ASSERT_EQUAL(BD_ERROR_NOT_ERASED, err);
err = bd.program(write_buf, 2 * erase_size - test_buf_size, test_buf_size);
TEST_ASSERT_EQUAL(0, err);
memset(write_buf, blank, test_buf_size / 2);
err = bd.read(read_buf, test_buf_size * 2, test_buf_size / 2);
TEST_ASSERT_EQUAL(0, err);
TEST_ASSERT_EQUAL_UINT8_ARRAY(write_buf, read_buf, test_buf_size / 2);
srand(1);
for (bd_size_t i = 0; i < test_buf_size; i++) {
write_buf[i] = 0xff & rand();
}
err = bd.read(read_buf, 0, test_buf_size);
TEST_ASSERT_EQUAL(0, err);
TEST_ASSERT_EQUAL_UINT8_ARRAY(write_buf, read_buf, test_buf_size);
srand(2);
for (bd_size_t i = 0; i < test_buf_size; i++) {
write_buf[i] = 0xff & rand();
}
err = bd.read(read_buf, 2 * erase_size - test_buf_size, test_buf_size);
TEST_ASSERT_EQUAL(0, err);
TEST_ASSERT_EQUAL_UINT8_ARRAY(write_buf, read_buf, test_buf_size);
err = bd.deinit();
TEST_ASSERT_EQUAL(0, err);
err = bd.init();
TEST_ASSERT_EQUAL(0, err);
// Make sure data lives across inits
err = bd.read(read_buf, 2 * erase_size - test_buf_size, test_buf_size);
TEST_ASSERT_EQUAL(0, err);
TEST_ASSERT_EQUAL_UINT8_ARRAY(write_buf, read_buf, test_buf_size);
err = bd.erase(0, erase_size);
TEST_ASSERT_EQUAL(0, err);
// Make sure erase returns the erase value
memset(write_buf, blank, test_buf_size);
err = bd.read(read_buf, 0, test_buf_size);
TEST_ASSERT_EQUAL(0, err);
TEST_ASSERT_EQUAL_UINT8_ARRAY(write_buf, read_buf, test_buf_size);
}
// Test setup
utest::v1::status_t test_setup(const size_t number_of_cases)
{
GREENTEA_SETUP(30, "default_auto");
return verbose_test_setup_handler(number_of_cases);
}
Case cases[] = {
Case("FlashSimBlockDevice functionality test", functionality_test),
};
Specification specification(test_setup, cases);
int main()
{
return !Harness::run(specification);
}

View File

@@ -0,0 +1,71 @@
# Getting started with the Mbed OS block device test
You can use the Mbed OS block device test to test existing and new block devices.
You can find more information about the Mbed OS block device and other related pieces of the Mbed OS storage stack [in the storage overview](https://os.mbed.com/docs/latest/reference/storage.html).
**Table of contents:**
1. [Hardware requirements](#hardware-requirements)
2. [Usage](#usage)
- [Compile the test](#compile-the-test)
- [Run the test](#run-the-test)
3. [Changing the block device](#changing-the-block-device)
4. [Tested configurations](#tested-configurations)
## Hardware requirements
This test uses a block device as storage. This can be either an external block device (one of SPI flash, DataFlash or an SD card) or simulated on a heap block device on boards with enough RAM.
## Usage
#### Compile the test
Invoke `mbed test`, and specify the name of your platform and your favorite toolchain (`GCC_ARM`, `ARM`, `IAR`). For example, for the ARM Compiler 5:
```
mbed test -m K64F -t ARM -n mbed-os-features-storage-tests-blockdevice-general_block_device --compile
```
#### Run the test
Use `mbed test` again:
```
mbed test -m K64F -t ARM -n mbed-os-features-storage-tests-blockdevice-general_block_device --run -v
```
#### Troubleshooting
Please review the [documentation](https://os.mbed.com/docs/latest/tutorials/debugging.html) for suggestions about how to fix possible issues you may face.
## Changing the block device
In Mbed OS, a C++ class that inherits from the [BlockDevice](https://os.mbed.com/docs/latest/reference/storage.html#block-devices) interface represents each block device.
This test uses the default block device that the function `get_default_instance()` receives. [PlatformStorage.cpp](https://github.com/ARMmbed/mbed-os/blob/master/storage/platform/source/PlatformStorage.cpp#L35-L77) defines this as `MBED_WEAK`. If you would like to test a new block device, you have to override it.
First add the new block device .cpp and header files to the [blockdevice folder](https://github.com/ARMmbed/mbed-os/tree/master/storage/blockdevice). Then, implement `get_default_instance()` inside the test `main.cpp`.
For example, to test the HeapBlockDevice, add:
``` diff
+#define TEST_BLOCK_SIZE 128
+#define TEST_BLOCK_DEVICE_SIZE 32*TEST_BLOCK_SIZE
+BlockDevice *BlockDevice::get_default_instance()
+{
+ utest_printf("NEW test block device!!!\n");
+ static HeapBlockDevice default_bd(TEST_BLOCK_DEVICE_SIZE, TEST_BLOCK_SIZE);
+ return &default_bd;
+}
```
Now you can recompile and run.
## Tested configurations
- K64F + SD.
- K64F + Heap.
- K82F + SPIF.
- K82F + Heap.

View File

@@ -0,0 +1,850 @@
/* mbed Microcontroller Library
* Copyright (c) 2018 ARM Limited
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef __STDC_FORMAT_MACROS
#define __STDC_FORMAT_MACROS //Required for PRIu64
#endif
#include "mbed.h"
#include "greentea-client/test_env.h"
#include "unity.h"
#include "utest.h"
#include "mbed_trace.h"
#include <inttypes.h>
#include <stdlib.h>
#include "BufferedBlockDevice.h"
#include "BlockDevice.h"
#include <algorithm>
#if COMPONENT_SPIF
#include "SPIFBlockDevice.h"
#endif
#if COMPONENT_QSPIF
#include "QSPIFBlockDevice.h"
#endif
#if COMPONENT_DATAFLASH
#include "DataFlashBlockDevice.h"
#endif
#if COMPONENT_SD
#include "SDBlockDevice.h"
#endif
#if COMPONENT_FLASHIAP
#include "FlashIAPBlockDevice.h"
#endif
// Debug available
#ifndef MODE_DEBUG
#define MODE_DEBUG 0
#endif
#if MODE_DEBUG
#define DEBUG_PRINTF(...) printf(__VA_ARGS__)
#else
#define DEBUG_PRINTF(...)
#endif
using namespace utest::v1;
#define TEST_BLOCK_COUNT 10
#define TEST_ERROR_MASK 16
#define TEST_NUM_OF_THREADS 5
#define TEST_THREAD_STACK_SIZE 1152
uint8_t num_of_sectors = TEST_NUM_OF_THREADS * TEST_BLOCK_COUNT;
uint32_t sectors_addr[TEST_NUM_OF_THREADS * TEST_BLOCK_COUNT] = {0};
const struct {
const char *name;
bd_size_t (BlockDevice::*method)() const;
} ATTRS[] = {
{"read size", &BlockDevice::get_read_size},
{"program size", &BlockDevice::get_program_size},
{"erase size", &BlockDevice::get_erase_size},
{"total size", &BlockDevice::size},
};
enum bd_type {
spif = 0,
qspif,
dataflash,
sd,
flashiap,
default_bd
};
uint8_t bd_arr[5] = {0};
static uint8_t test_iteration = 0;
static SingletonPtr<PlatformMutex> _mutex;
BlockDevice *block_device = NULL;
#if COMPONENT_FLASHIAP
static inline uint32_t align_up(uint32_t val, uint32_t size)
{
return (((val - 1) / size) + 1) * size;
}
#endif
static BlockDevice *get_bd_instance(uint8_t bd_type)
{
switch (bd_arr[bd_type]) {
case spif: {
#if COMPONENT_SPIF
static SPIFBlockDevice default_bd(
MBED_CONF_SPIF_DRIVER_SPI_MOSI,
MBED_CONF_SPIF_DRIVER_SPI_MISO,
MBED_CONF_SPIF_DRIVER_SPI_CLK,
MBED_CONF_SPIF_DRIVER_SPI_CS,
MBED_CONF_SPIF_DRIVER_SPI_FREQ
);
return &default_bd;
#endif
break;
}
case qspif: {
#if COMPONENT_QSPIF
static QSPIFBlockDevice default_bd(
MBED_CONF_QSPIF_QSPI_IO0,
MBED_CONF_QSPIF_QSPI_IO1,
MBED_CONF_QSPIF_QSPI_IO2,
MBED_CONF_QSPIF_QSPI_IO3,
MBED_CONF_QSPIF_QSPI_SCK,
MBED_CONF_QSPIF_QSPI_CSN,
MBED_CONF_QSPIF_QSPI_POLARITY_MODE,
MBED_CONF_QSPIF_QSPI_FREQ
);
return &default_bd;
#endif
break;
}
case dataflash: {
#if COMPONENT_DATAFLASH
static DataFlashBlockDevice default_bd(
MBED_CONF_DATAFLASH_SPI_MOSI,
MBED_CONF_DATAFLASH_SPI_MISO,
MBED_CONF_DATAFLASH_SPI_CLK,
MBED_CONF_DATAFLASH_SPI_CS
);
return &default_bd;
#endif
break;
}
case sd: {
#if COMPONENT_SD
static SDBlockDevice default_bd(
MBED_CONF_SD_SPI_MOSI,
MBED_CONF_SD_SPI_MISO,
MBED_CONF_SD_SPI_CLK,
MBED_CONF_SD_SPI_CS
);
return &default_bd;
#endif
break;
}
case flashiap: {
#if COMPONENT_FLASHIAP
#if (MBED_CONF_FLASHIAP_BLOCK_DEVICE_SIZE == 0) && (MBED_CONF_FLASHIAP_BLOCK_DEVICE_BASE_ADDRESS == 0xFFFFFFFF)
size_t flash_size;
uint32_t start_address;
uint32_t bottom_address;
mbed::FlashIAP flash;
int ret = flash.init();
if (ret != 0) {
return NULL;
}
//Find the start of first sector after text area
bottom_address = align_up(FLASHIAP_APP_ROM_END_ADDR, flash.get_sector_size(FLASHIAP_APP_ROM_END_ADDR));
start_address = flash.get_flash_start();
flash_size = flash.get_flash_size();
ret = flash.deinit();
static FlashIAPBlockDevice default_bd(bottom_address, start_address + flash_size - bottom_address);
#else
static FlashIAPBlockDevice default_bd;
#endif
return &default_bd;
#endif
break;
}
}
return NULL;
}
// Mutex is protecting rand() per srand for buffer writing and verification.
// Mutex is also protecting printouts for clear logs.
// Mutex is NOT protecting Block Device actions: erase/program/read - which is the purpose of the multithreaded test!
void basic_erase_program_read_test(BlockDevice *block_device, bd_size_t block_size, uint8_t *write_block,
uint8_t *read_block, unsigned addrwidth, int thread_num)
{
int err = 0;
_mutex->lock();
// Make sure block address per each test is unique
static unsigned block_seed = 1;
srand(block_seed++);
// Find a random block
bd_addr_t block = sectors_addr[thread_num];
bd_size_t curr_block_size = block_device->get_erase_size(block);
block_size = std::min(block_size, curr_block_size);
// Use next random number as temporary seed to keep
// the address progressing in the pseudorandom sequence
unsigned seed = rand();
// Fill with random sequence
srand(seed);
for (bd_size_t i_ind = 0; i_ind < block_size; i_ind++) {
write_block[i_ind] = 0xff & rand();
}
// Write, sync, and read the block
DEBUG_PRINTF("test %0*llx:%llu...\n", addrwidth, block, curr_block_size);
err = block_device->erase(block, curr_block_size);
TEST_ASSERT_EQUAL(0, err);
err = block_device->program(write_block, block, block_size);
TEST_ASSERT_EQUAL(0, err);
err = block_device->read(read_block, block, block_size);
TEST_ASSERT_EQUAL(0, err);
// Check that the data was unmodified
srand(seed);
int val_rand;
for (bd_size_t i_ind = 0; i_ind < block_size; i_ind++) {
val_rand = rand();
if ((0xff & val_rand) != read_block[i_ind]) {
utest_printf("\n Assert Failed Buf Read - block:size: %llx:%llu \n", block, block_size);
utest_printf("\n pos: %llu, exp: %02x, act: %02x, wrt: %02x \n", i_ind, (0xff & val_rand),
read_block[i_ind],
write_block[i_ind]);
}
TEST_ASSERT_EQUAL(0xff & val_rand, read_block[i_ind]);
}
_mutex->unlock();
}
void test_init_bd()
{
utest_printf("\nTest Init block device.\n");
block_device = get_bd_instance(test_iteration);
TEST_SKIP_UNLESS_MESSAGE(block_device != NULL, "no block device found.");
int err = block_device->init();
TEST_ASSERT_EQUAL(0, err);
bd_addr_t start_address = 0;
uint8_t i = 0;
for (; i < num_of_sectors && start_address < block_device->size(); i++) {
sectors_addr[i] = start_address;
DEBUG_PRINTF("start_address = 0x%llx, sector_size = %d\n", start_address, block_device->get_erase_size(start_address));
start_address += block_device->get_erase_size(start_address);
}
num_of_sectors = i;
}
void test_random_program_read_erase()
{
utest_printf("\nTest Random Program Read Erase Starts..\n");
TEST_SKIP_UNLESS_MESSAGE(block_device != NULL, "no block device found.");
for (unsigned atr = 0; atr < sizeof(ATTRS) / sizeof(ATTRS[0]); atr++) {
static const char *prefixes[] = {"", "k", "M", "G"};
for (int i_ind = 3; i_ind >= 0; i_ind--) {
bd_size_t size = (block_device->*ATTRS[atr].method)();
if (size >= (1ULL << 10 * i_ind)) {
utest_printf("%s: %llu%sbytes (%llubytes)\n",
ATTRS[atr].name, size >> 10 * i_ind, prefixes[i_ind], size);
break;
}
}
}
bd_size_t block_size = block_device->get_erase_size();
unsigned addrwidth = ceil(log(float(block_device->size() - 1)) / log(float(16))) + 1;
uint8_t *write_block = new (std::nothrow) uint8_t[block_size];
uint8_t *read_block = new (std::nothrow) uint8_t[block_size];
if (!write_block || !read_block) {
utest_printf("Not enough memory for test\n");
goto end;
}
for (int b = 0; b < std::min((uint8_t)TEST_BLOCK_COUNT, num_of_sectors); b++) {
basic_erase_program_read_test(block_device, block_size, write_block, read_block, addrwidth, b);
}
end:
delete[] read_block;
delete[] write_block;
}
#if defined(MBED_CONF_RTOS_PRESENT)
static void test_thread_job()
{
static int thread_num = 0;
_mutex->lock();
int block_num = thread_num++ % TEST_NUM_OF_THREADS;
_mutex->unlock();
uint8_t sector_per_thread = (num_of_sectors / TEST_NUM_OF_THREADS);
bd_size_t block_size = block_device->get_erase_size();
unsigned addrwidth = ceil(log(float(block_device->size() - 1)) / log(float(16))) + 1;
uint8_t *write_block = new (std::nothrow) uint8_t[block_size];
uint8_t *read_block = new (std::nothrow) uint8_t[block_size];
if (!write_block || !read_block) {
utest_printf("Not enough memory for test\n");
goto end;
}
for (int b = 0; b < sector_per_thread; b++) {
basic_erase_program_read_test(block_device, block_size, write_block, read_block, addrwidth, block_num * sector_per_thread + b);
}
end:
delete[] read_block;
delete[] write_block;
}
void test_multi_threads()
{
utest_printf("\nTest Multi Threaded Erase/Program/Read Starts..\n");
TEST_SKIP_UNLESS_MESSAGE(block_device != NULL, "no block device found.");
for (unsigned atr = 0; atr < sizeof(ATTRS) / sizeof(ATTRS[0]); atr++) {
static const char *prefixes[] = {"", "k", "M", "G"};
for (int i_ind = 3; i_ind >= 0; i_ind--) {
bd_size_t size = (block_device->*ATTRS[atr].method)();
if (size >= (1ULL << 10 * i_ind)) {
utest_printf("%s: %llu%sbytes (%llubytes)\n",
ATTRS[atr].name, size >> 10 * i_ind, prefixes[i_ind], size);
break;
}
}
}
osStatus threadStatus;
int i_ind, j_ind;
char *dummy;
rtos::Thread **bd_thread = new (std::nothrow) rtos::Thread*[TEST_NUM_OF_THREADS];
TEST_SKIP_UNLESS_MESSAGE((*bd_thread) != NULL, "not enough heap to run test.");
memset(bd_thread, 0, TEST_NUM_OF_THREADS * sizeof(rtos::Thread *));
for (i_ind = 0; i_ind < TEST_NUM_OF_THREADS; i_ind++) {
bd_thread[i_ind] = new (std::nothrow) rtos::Thread((osPriority_t)((int)osPriorityNormal), TEST_THREAD_STACK_SIZE);
dummy = new (std::nothrow) char[TEST_THREAD_STACK_SIZE];
if (!bd_thread[i_ind] || !dummy) {
utest_printf("Not enough heap to run Thread %d !\n", i_ind + 1);
break;
}
delete[] dummy;
threadStatus = bd_thread[i_ind]->start(callback(test_thread_job));
if (threadStatus != 0) {
utest_printf("Thread %d Start Failed!\n", i_ind + 1);
break;
}
}
for (j_ind = 0; j_ind < i_ind; j_ind++) {
bd_thread[j_ind]->join();
}
if (bd_thread) {
for (j_ind = 0; j_ind < i_ind; j_ind++) {
delete bd_thread[j_ind];
}
delete[] bd_thread;
}
}
#endif
void test_erase_functionality()
{
utest_printf("\nTest BlockDevice::get_erase_value()..\n");
// Test flow:
// 1. Write data to selected region
// - Known starting point
// 2. Erase selected region
// 3. Read erased region and compare with get_erase_value()
TEST_SKIP_UNLESS_MESSAGE(block_device != NULL, "no block device found.");
// Check erase value
int erase_value_int = block_device->get_erase_value();
TEST_SKIP_UNLESS_MESSAGE(erase_value_int >= 0, "Erase not supported in this block device. Test skipped.");
// Assuming that get_erase_value() returns byte value as documentation mentions
// "If get_erase_value() returns a non-negative byte value" for unknown case.
TEST_ASSERT(erase_value_int <= 255);
uint8_t erase_value = (uint8_t)erase_value_int;
// Determine start_address
bd_addr_t start_address = sectors_addr[rand() % num_of_sectors];
utest_printf("start_address=0x%016" PRIx64 "\n", start_address);
// Determine data_buf_size
bd_size_t erase_size = block_device->get_erase_size(start_address);
TEST_ASSERT(erase_size > 0);
bd_size_t data_buf_size = erase_size;
// Allocate buffer for write test data
uint8_t *data_buf = new (std::nothrow) uint8_t[data_buf_size];
TEST_SKIP_UNLESS_MESSAGE(data_buf != NULL, "Not enough memory for test");
// Allocate buffer for read test data
uint8_t *out_data_buf = new (std::nothrow) uint8_t[data_buf_size];
TEST_SKIP_UNLESS_MESSAGE(out_data_buf != NULL, "Not enough memory for test");
// First must Erase given memory region
utest_printf("erasing given memory region\n");
int err = block_device->erase(start_address, data_buf_size);
TEST_ASSERT_EQUAL(0, err);
// Write random data to selected region to make sure data is not accidentally set to "erased" value.
// With this pre-write, the test case will fail even if block_device->erase() is broken.
for (bd_size_t i = 0; i < data_buf_size; i++) {
data_buf[i] = (uint8_t) rand();
}
utest_printf("writing given memory region\n");
err = block_device->program((const void *)data_buf, start_address, data_buf_size);
TEST_ASSERT_EQUAL(0, err);
// Read written memory region to verify it contains information
memset(out_data_buf, 0, data_buf_size);
utest_printf("reading written memory region\n");
err = block_device->read((void *)out_data_buf, start_address, data_buf_size);
TEST_ASSERT_EQUAL(0, err);
// Verify erased memory region
utest_printf("verifying written memory region\n");
for (bd_size_t i = 0; i < data_buf_size; i++) {
TEST_ASSERT_EQUAL(out_data_buf[i], data_buf[i]);
}
// Erase given memory region
utest_printf("erasing written memory region\n");
err = block_device->erase(start_address, data_buf_size);
TEST_ASSERT_EQUAL(0, err);
// Read erased memory region
utest_printf("reading erased memory region\n");
memset(out_data_buf, 0, data_buf_size);
err = block_device->read((void *)out_data_buf, start_address, data_buf_size);
TEST_ASSERT_EQUAL(0, err);
// Verify erased memory region
utest_printf("verifying erased memory region\n");
for (bd_size_t i = 0; i < data_buf_size; i++) {
TEST_ASSERT_EQUAL(erase_value, out_data_buf[i]);
}
delete[] out_data_buf;
delete[] data_buf;
}
void test_contiguous_erase_write_read()
{
utest_printf("\nTest Contiguous Erase/Program/Read Starts..\n");
TEST_SKIP_UNLESS_MESSAGE(block_device != NULL, "no block device found.");
// Test flow:
// 1. Erase whole test area
// - Tests contiguous erase
// 2. Write smaller memory area
// - Tests contiguous sector writes
// 3. Return step 2 for whole erase region
// Test parameters
bd_size_t program_size = block_device->get_program_size();
TEST_ASSERT(program_size > 0);
utest_printf("program_size=%" PRId64 "\n", program_size);
utest_printf("block_device->size()=%" PRId64 "\n", block_device->size());
// Determine start_address & stop_address
uint8_t sector_num = rand() % (num_of_sectors - 2);
bd_addr_t start_address = sectors_addr[sector_num];
bd_addr_t stop_address = sectors_addr[sector_num + 2];
utest_printf("start_address=0x%016" PRIx64 "\n", start_address);
utest_printf("stop_address=0x%016" PRIx64 "\n", stop_address);
bd_size_t contiguous_erase_size = stop_address - start_address;
TEST_ASSERT(contiguous_erase_size > 0);
utest_printf("contiguous_erase_size=%d\n", contiguous_erase_size);
bd_size_t write_read_buf_size = program_size;
if (contiguous_erase_size / program_size > 8 && contiguous_erase_size % (program_size * 8) == 0) {
write_read_buf_size = program_size * 8;
}
// Allocate write/read buffer
uint8_t *write_read_buf = new (std::nothrow) uint8_t[write_read_buf_size];
TEST_SKIP_UNLESS_MESSAGE(contiguous_erase_size, "Not enough memory for test.\n");
// Must Erase the whole region first
utest_printf("erasing memory, from 0x%" PRIx64 " of size 0x%" PRIx64 "\n", start_address, contiguous_erase_size);
int err = block_device->erase(start_address, contiguous_erase_size);
TEST_ASSERT_EQUAL(0, err);
// Pre-fill the to-be-erased region. By pre-filling the region,
// we can be sure the test will not pass if the erase doesn't work.
for (bd_size_t offset = 0; start_address + offset < stop_address; offset += write_read_buf_size) {
for (size_t i = 0; i < write_read_buf_size; i++) {
write_read_buf[i] = (uint8_t)rand();
}
DEBUG_PRINTF("pre-filling memory, from 0x%" PRIx64 " of size 0x%" PRIx64 "", start_address + offset,
write_read_buf_size);
err = block_device->program((const void *)write_read_buf, start_address + offset, write_read_buf_size);
TEST_ASSERT_EQUAL(0, err);
}
// Erase the whole region again
utest_printf("erasing memory, from 0x%" PRIx64 " of size 0x%" PRIx64 "\n", start_address, contiguous_erase_size);
err = block_device->erase(start_address, contiguous_erase_size);
TEST_ASSERT_EQUAL(0, err);
// Loop through all write/read regions
int region = 0;
for (; start_address < stop_address; start_address += write_read_buf_size) {
// Generate test data
unsigned int seed = rand();
srand(seed);
for (size_t i = 0; i < write_read_buf_size; i++) {
write_read_buf[i] = (uint8_t)rand();
}
// Write test data
err = block_device->program((const void *)write_read_buf, start_address, write_read_buf_size);
TEST_ASSERT_EQUAL(0, err);
// Read test data
memset(write_read_buf, 0, (size_t)write_read_buf_size);
err = block_device->read(write_read_buf, start_address, write_read_buf_size);
TEST_ASSERT_EQUAL(0, err);
// Verify read data
srand(seed);
for (size_t i = 0; i < write_read_buf_size; i++) {
uint8_t expected_value = (uint8_t)rand();
if (write_read_buf[i] != expected_value) {
utest_printf("data verify failed, write_read_buf[%d]=%" PRIu8 " and not %" PRIu8 "\n",
i, write_read_buf[i], expected_value);
}
TEST_ASSERT_EQUAL(write_read_buf[i], expected_value);
}
}
free(write_read_buf);
}
void test_program_read_small_data_sizes()
{
utest_printf("\nTest program-read small data sizes, from 1 to 7 bytes..\n");
TEST_SKIP_UNLESS_MESSAGE(block_device != NULL, "no block device found.");
bd_size_t erase_size = block_device->get_erase_size();
bd_size_t program_size = block_device->get_program_size();
bd_size_t read_size = block_device->get_read_size();
TEST_ASSERT(program_size > 0);
// See that we have enough memory for buffered block device
char *dummy = new (std::nothrow) char[program_size + read_size];
TEST_SKIP_UNLESS_MESSAGE(dummy, "Not enough memory for test.\n");
delete[] dummy;
// use BufferedBlockDevice for better handling of block devices program and read
BufferedBlockDevice *buff_block_device = new BufferedBlockDevice(block_device);
// BlockDevice initialization
int err = buff_block_device->init();
TEST_ASSERT_EQUAL(0, err);
const char write_buffer[] = "1234567";
char read_buffer[7] = {};
// Determine starting address
bd_addr_t start_address = 0;
for (int i = 1; i <= 7; i++) {
err = buff_block_device->erase(start_address, erase_size);
TEST_ASSERT_EQUAL(0, err);
err = buff_block_device->program((const void *)write_buffer, start_address, i);
TEST_ASSERT_EQUAL(0, err);
err = buff_block_device->sync();
TEST_ASSERT_EQUAL(0, err);
err = buff_block_device->read(read_buffer, start_address, i);
TEST_ASSERT_EQUAL(0, err);
err = memcmp(write_buffer, read_buffer, i);
TEST_ASSERT_EQUAL(0, err);
}
// BlockDevice deinitialization
err = buff_block_device->deinit();
TEST_ASSERT_EQUAL(0, err);
delete buff_block_device;
}
void test_unaligned_erase_blocks()
{
utest_printf("\nTest Unaligned Erase Starts..\n");
TEST_SKIP_UNLESS_MESSAGE(block_device != NULL, "no block device found.");
TEST_SKIP_UNLESS_MESSAGE(block_device->get_erase_value() != -1, "block device has no erase functionality.");
bd_addr_t addr = 0;
bd_size_t sector_erase_size = block_device->get_erase_size(addr);
unsigned addrwidth = ceil(log(float(block_device->size() - 1)) / log(float(16))) + 1;
utest_printf("\ntest %0*llx:%llu...", addrwidth, addr, sector_erase_size);
//unaligned start address
addr += 1;
int err = block_device->erase(addr, sector_erase_size - 1);
TEST_ASSERT_NOT_EQUAL(0, err);
err = block_device->erase(addr, sector_erase_size);
TEST_ASSERT_NOT_EQUAL(0, err);
err = block_device->erase(addr, 1);
TEST_ASSERT_NOT_EQUAL(0, err);
//unaligned end address
addr = 0;
err = block_device->erase(addr, 1);
TEST_ASSERT_NOT_EQUAL(0, err);
err = block_device->erase(addr, sector_erase_size + 1);
TEST_ASSERT_NOT_EQUAL(0, err);
//erase size exceeds flash device size
err = block_device->erase(addr, block_device->size() + 1);
TEST_ASSERT_NOT_EQUAL(0, err);
// Valid erase
err = block_device->erase(addr, sector_erase_size);
TEST_ASSERT_EQUAL(0, err);
}
void test_deinit_bd()
{
utest_printf("\nTest deinit block device.\n");
test_iteration++;
TEST_SKIP_UNLESS_MESSAGE(block_device != NULL, "no block device found.");
int err = block_device->deinit();
TEST_ASSERT_EQUAL(0, err);
block_device = NULL;
}
void test_write_deinit_init()
{
TEST_SKIP_UNLESS_MESSAGE(block_device != NULL, "no block device found.");
// Determine start_address & stop_address
bd_addr_t addr = sectors_addr[rand() % num_of_sectors];
bd_size_t erase_size = block_device->get_erase_size(addr);
bd_size_t prog_size = block_device->get_program_size();
uint8_t *prog = (uint8_t *) malloc(prog_size);
TEST_ASSERT_NOT_EQUAL(prog, 0);
uint8_t *buf = (uint8_t *) malloc(prog_size);
TEST_ASSERT_NOT_EQUAL(buf, 0);
for (int i = 0; i < 3; i++) {
// Generate test pattern
for (int j = 0; j < prog_size; j++) {
prog[j] = (uint8_t)'0' + i + j;
}
int err;
err = block_device->erase(addr, erase_size);
TEST_ASSERT_EQUAL(err, 0);
err = block_device->program(prog, addr, prog_size);
TEST_ASSERT_EQUAL(err, 0);
err = block_device->deinit();
TEST_ASSERT_EQUAL(0, err);
err = block_device->init();
TEST_ASSERT_EQUAL(0, err);
err = block_device->read(buf, addr, prog_size);
TEST_ASSERT_EQUAL(0, memcmp(prog, buf, prog_size));
}
free(prog);
free(buf);
}
void test_get_type_functionality()
{
utest_printf("\nTest get blockdevice type..\n");
block_device = BlockDevice::get_default_instance();
TEST_SKIP_UNLESS_MESSAGE(block_device != NULL, "no block device found.");
const char *bd_type = block_device->get_type();
TEST_ASSERT_NOT_EQUAL(0, bd_type);
#if COMPONENT_QSPIF
TEST_ASSERT_EQUAL(0, strcmp(bd_type, "QSPIF"));
#elif COMPONENT_SPIF
TEST_ASSERT_EQUAL(0, strcmp(bd_type, "SPIF"));
#elif COMPONENT_DATAFLASH
TEST_ASSERT_EQUAL(0, strcmp(bd_type, "DATAFLASH"));
#elif COMPONENT_SD
TEST_ASSERT_EQUAL(0, strcmp(bd_type, "SD"));
#elif COMPONET_FLASHIAP
TEST_ASSERT_EQUAL(0, strcmp(bd_type, "FLASHIAP"));
#endif
}
utest::v1::status_t greentea_failure_handler(const Case *const source, const failure_t reason)
{
greentea_case_failure_abort_handler(source, reason);
return STATUS_CONTINUE;
}
typedef struct {
const char *description;
const case_handler_t case_handler;
const case_failure_handler_t failure_handler;
} template_case_t;
template_case_t template_cases[] = {
{"Testing Init block device", test_init_bd, greentea_failure_handler},
{"Testing write -> deinit -> init -> read", test_write_deinit_init, greentea_failure_handler},
{"Testing read write random blocks", test_random_program_read_erase, greentea_failure_handler},
#if defined(MBED_CONF_RTOS_PRESENT)
{"Testing multi threads erase program read", test_multi_threads, greentea_failure_handler},
#endif
{"Testing contiguous erase, write and read", test_contiguous_erase_write_read, greentea_failure_handler},
{"Testing BlockDevice erase functionality", test_erase_functionality, greentea_failure_handler},
{"Testing program read small data sizes", test_program_read_small_data_sizes, greentea_failure_handler},
{"Testing unaligned erase blocks", test_unaligned_erase_blocks, greentea_failure_handler},
{"Testing Deinit block device", test_deinit_bd, greentea_failure_handler},
};
template_case_t def_template_case = {"Testing get type functionality", test_get_type_functionality, greentea_failure_handler};
utest::v1::status_t greentea_test_setup(const size_t number_of_cases)
{
return greentea_test_setup_handler(number_of_cases);
}
int get_bd_count()
{
int count = 0;
#if COMPONENT_SPIF
bd_arr[count++] = spif; //0
#endif
#if COMPONENT_QSPIF
bd_arr[count++] = qspif; //1
#endif
#if COMPONENT_DATAFLASH
bd_arr[count++] = dataflash; //2
#endif
#if COMPONENT_SD
bd_arr[count++] = sd; //3
#endif
#if COMPONENT_FLASHIAP
bd_arr[count++] = flashiap; //4
#endif
return count;
}
static const char *prefix[] = {"SPIF ", "QSPIF ", "DATAFLASH ", "SD ", "FLASHIAP ", "DEFAULT "};
int main()
{
GREENTEA_SETUP(300, "default_auto");
// We want to replicate our test cases to different types
size_t num_cases = sizeof(template_cases) / sizeof(template_case_t);
size_t total_num_cases = 0;
int bd_count = get_bd_count();
void *raw_mem = new (std::nothrow) uint8_t[(bd_count * num_cases + 1) * sizeof(Case)];
Case *cases = static_cast<Case *>(raw_mem);
for (int j = 0; j < bd_count; j++) {
for (size_t i = 0; i < num_cases; i++) {
char desc[128], *desc_ptr;
sprintf(desc, "%s%s", prefix[bd_arr[j]], template_cases[i].description);
desc_ptr = new char[strlen(desc) + 1];
strcpy(desc_ptr, desc);
new (&cases[total_num_cases]) Case((const char *) desc_ptr, template_cases[i].case_handler,
template_cases[i].failure_handler);
total_num_cases++;
}
//Add test_get_type_functionality once, runs on default blockdevice
if (j == bd_count - 1) {
char desc[128], *desc_ptr;
sprintf(desc, "%s%s", prefix[default_bd], def_template_case.description);
desc_ptr = new char[strlen(desc) + 1];
strcpy(desc_ptr, desc);
new (&cases[total_num_cases]) Case((const char *) desc_ptr, def_template_case.case_handler,
def_template_case.failure_handler);
total_num_cases++;
}
}
Specification specification(greentea_test_setup, cases, total_num_cases,
greentea_test_teardown_handler, default_handler);
return !Harness::run(specification);
}

View File

@@ -0,0 +1,187 @@
/* mbed Microcontroller Library
* Copyright (c) 2017 ARM Limited
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "mbed.h"
#include "greentea-client/test_env.h"
#include "unity.h"
#include "utest.h"
#include "HeapBlockDevice.h"
#include <stdlib.h>
using namespace utest::v1;
#define TEST_BLOCK_SIZE 128
#define TEST_BLOCK_DEVICE_SIZE 32*TEST_BLOCK_SIZE
#define TEST_BLOCK_COUNT 10
#define TEST_ERROR_MASK 16
#if ((MBED_RAM_SIZE - MBED_BOOT_STACK_SIZE) <= TEST_BLOCK_DEVICE_SIZE)
#error [NOT_SUPPORTED] Insufficient heap for heap block device tests
#endif
const struct {
const char *name;
bd_size_t (BlockDevice::*method)() const;
} ATTRS[] = {
{"read size", &BlockDevice::get_read_size},
{"program size", &BlockDevice::get_program_size},
{"erase size", &BlockDevice::get_erase_size},
{"total size", &BlockDevice::size},
};
// Simple test that read/writes random set of blocks
void test_read_write()
{
uint8_t *dummy = new (std::nothrow) uint8_t[TEST_BLOCK_DEVICE_SIZE];
TEST_SKIP_UNLESS_MESSAGE(dummy, "Not enough memory for test");
delete[] dummy;
HeapBlockDevice bd(TEST_BLOCK_DEVICE_SIZE, TEST_BLOCK_SIZE);
int err = bd.init();
TEST_ASSERT_EQUAL(0, err);
for (unsigned a = 0; a < sizeof(ATTRS) / sizeof(ATTRS[0]); a++) {
static const char *prefixes[] = {"", "k", "M", "G"};
for (int i = 3; i >= 0; i--) {
bd_size_t size = (bd.*ATTRS[a].method)();
if (size >= (1ULL << 10 * i)) {
printf("%s: %llu%sbytes (%llubytes)\n",
ATTRS[a].name, size >> 10 * i, prefixes[i], size);
break;
}
}
}
unsigned addrwidth = ceil(log(float(bd.size() - 1)) / log(float(16))) + 1;
bd_size_t block_size = bd.get_erase_size();
uint8_t *write_block = new (std::nothrow) uint8_t[block_size];
uint8_t *read_block = new (std::nothrow) uint8_t[block_size];
uint8_t *error_mask = new (std::nothrow) uint8_t[TEST_ERROR_MASK];
if (!write_block || !read_block || !error_mask) {
printf("Not enough memory for test");
goto end;
}
for (int b = 0; b < TEST_BLOCK_COUNT; b++) {
// Find a random block
bd_addr_t block = (rand() * block_size) % bd.size();
// Use next random number as temporary seed to keep
// the address progressing in the pseudorandom sequence
unsigned seed = rand();
// Fill with random sequence
srand(seed);
for (bd_size_t i = 0; i < block_size; i++) {
write_block[i] = 0xff & rand();
}
// erase, program, and read the block
printf("test %0*llx:%llu...\n", addrwidth, block, block_size);
err = bd.erase(block, block_size);
TEST_ASSERT_EQUAL(0, err);
err = bd.program(write_block, block, block_size);
TEST_ASSERT_EQUAL(0, err);
printf("write %0*llx:%llu ", addrwidth, block, block_size);
for (int i = 0; i < 16; i++) {
printf("%02x", write_block[i]);
}
printf("...\n");
err = bd.read(read_block, block, block_size);
TEST_ASSERT_EQUAL(0, err);
printf("read %0*llx:%llu ", addrwidth, block, block_size);
for (int i = 0; i < 16; i++) {
printf("%02x", read_block[i]);
}
printf("...\n");
// Find error mask for debugging
memset(error_mask, 0, TEST_ERROR_MASK);
bd_size_t error_scale = block_size / (TEST_ERROR_MASK * 8);
srand(seed);
for (bd_size_t i = 0; i < TEST_ERROR_MASK * 8; i++) {
for (bd_size_t j = 0; j < error_scale; j++) {
if ((0xff & rand()) != read_block[i * error_scale + j]) {
error_mask[i / 8] |= 1 << (i % 8);
}
}
}
printf("error %0*llx:%llu ", addrwidth, block, block_size);
for (int i = 0; i < 16; i++) {
printf("%02x", error_mask[i]);
}
printf("\n");
// Check that the data was unmodified
srand(seed);
for (bd_size_t i = 0; i < block_size; i++) {
TEST_ASSERT_EQUAL(0xff & rand(), read_block[i]);
}
}
err = bd.deinit();
TEST_ASSERT_EQUAL(0, err);
end:
delete[] write_block;
delete[] read_block;
delete[] error_mask;
}
void test_get_type_functionality()
{
uint8_t *dummy = new (std::nothrow) uint8_t[TEST_BLOCK_DEVICE_SIZE];
TEST_SKIP_UNLESS_MESSAGE(dummy, "Not enough memory for test");
delete[] dummy;
HeapBlockDevice bd(TEST_BLOCK_DEVICE_SIZE, TEST_BLOCK_SIZE);
const char *bd_type = bd.get_type();
TEST_ASSERT_NOT_EQUAL(0, bd_type);
TEST_ASSERT_EQUAL(0, strcmp(bd_type, "HEAP"));
}
// Test setup
utest::v1::status_t test_setup(const size_t number_of_cases)
{
GREENTEA_SETUP(30, "default_auto");
return verbose_test_setup_handler(number_of_cases);
}
Case cases[] = {
Case("Testing read write random blocks", test_read_write),
Case("Testing get type functionality", test_get_type_functionality)
};
Specification specification(test_setup, cases);
int main()
{
return !Harness::run(specification);
}

View File

@@ -0,0 +1,251 @@
/* mbed Microcontroller Library
* Copyright (c) 2017 ARM Limited
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "mbed.h"
#include "greentea-client/test_env.h"
#include "unity.h"
#include "utest.h"
#include "HeapBlockDevice.h"
#include "MBRBlockDevice.h"
#include <stdlib.h>
using namespace utest::v1;
#define BLOCK_COUNT 16
#define BLOCK_SIZE 512
#if ((MBED_RAM_SIZE - MBED_BOOT_STACK_SIZE) <= (BLOCK_COUNT * BLOCK_SIZE))
#error [NOT_SUPPORTED] Insufficient heap for mbr block device tests
#endif
HeapBlockDevice bd(BLOCK_COUNT *BLOCK_SIZE, BLOCK_SIZE);
// Testing formatting of master boot record
void test_mbr_format()
{
uint8_t *dummy = new (std::nothrow) uint8_t[BLOCK_COUNT * BLOCK_SIZE];
TEST_SKIP_UNLESS_MESSAGE(dummy, "Not enough memory for test");
delete[] dummy;
// Create two partitions splitting device in ~half
int err = MBRBlockDevice::partition(&bd, 1, 0x83, 0, (BLOCK_COUNT / 2) * BLOCK_SIZE);
TEST_ASSERT_EQUAL(0, err);
err = MBRBlockDevice::partition(&bd, 2, 0x83, -(BLOCK_COUNT / 2) * BLOCK_SIZE);
TEST_ASSERT_EQUAL(0, err);
// Load both partitions, as well as a third to check for invalid partitions
MBRBlockDevice part1(&bd, 1);
err = part1.init();
TEST_ASSERT_EQUAL(0, err);
MBRBlockDevice part2(&bd, 2);
err = part2.init();
TEST_ASSERT_EQUAL(0, err);
MBRBlockDevice part3(&bd, 3);
err = part3.init();
TEST_ASSERT_EQUAL(BD_ERROR_INVALID_PARTITION, err);
// Deinit partitions
err = part1.deinit();
TEST_ASSERT_EQUAL(0, err);
err = part2.deinit();
TEST_ASSERT_EQUAL(0, err);
}
// Testing mbr attributes
void test_mbr_attr()
{
uint8_t *dummy = new (std::nothrow) uint8_t[BLOCK_COUNT * BLOCK_SIZE];
TEST_SKIP_UNLESS_MESSAGE(dummy, "Not enough memory for test");
delete[] dummy;
// Load partitions
MBRBlockDevice part1(&bd, 1);
int err = part1.init();
TEST_ASSERT_EQUAL(0, err);
MBRBlockDevice part2(&bd, 2);
err = part2.init();
TEST_ASSERT_EQUAL(0, err);
// Test attributes on partitions
printf("partition 1 partition number: %d\n", part1.get_partition_number());
printf("partition 1 partition start: 0x%llx\n", part1.get_partition_start());
printf("partition 1 partition stop: 0x%llx\n", part1.get_partition_stop());
printf("partition 1 partition type: 0x%02x\n", part1.get_partition_type());
printf("partition 1 read size: %llu bytes\n", part1.get_read_size());
printf("partition 1 program size: %llu bytes\n", part1.get_program_size());
printf("partition 1 erase size: %llu bytes\n", part1.get_erase_size());
printf("partition 1 size: %llu bytes\n", part1.size());
TEST_ASSERT_EQUAL(1, part1.get_partition_number());
TEST_ASSERT_EQUAL(1 * BLOCK_SIZE, part1.get_partition_start());
TEST_ASSERT_EQUAL((BLOCK_COUNT / 2)*BLOCK_SIZE, part1.get_partition_stop());
TEST_ASSERT_EQUAL(0x83, part1.get_partition_type());
TEST_ASSERT_EQUAL(BLOCK_SIZE, part1.get_read_size());
TEST_ASSERT_EQUAL(BLOCK_SIZE, part1.get_program_size());
TEST_ASSERT_EQUAL(BLOCK_SIZE, part1.get_erase_size());
TEST_ASSERT_EQUAL(((BLOCK_COUNT / 2) - 1)*BLOCK_SIZE, part1.size());
printf("partition 2 partition number: %d\n", part2.get_partition_number());
printf("partition 2 partition start: 0x%llx\n", part2.get_partition_start());
printf("partition 2 partition stop: 0x%llx\n", part2.get_partition_stop());
printf("partition 2 partition type: 0x%02x\n", part2.get_partition_type());
printf("partition 2 read size: %llu bytes\n", part2.get_read_size());
printf("partition 2 program size: %llu bytes\n", part2.get_program_size());
printf("partition 2 erase size: %llu bytes\n", part2.get_erase_size());
printf("partition 2 size: %llu bytes\n", part2.size());
TEST_ASSERT_EQUAL(2, part2.get_partition_number());
TEST_ASSERT_EQUAL((BLOCK_COUNT / 2)*BLOCK_SIZE, part2.get_partition_start());
TEST_ASSERT_EQUAL(BLOCK_COUNT * BLOCK_SIZE, part2.get_partition_stop());
TEST_ASSERT_EQUAL(0x83, part2.get_partition_type());
TEST_ASSERT_EQUAL(BLOCK_SIZE, part2.get_read_size());
TEST_ASSERT_EQUAL(BLOCK_SIZE, part2.get_program_size());
TEST_ASSERT_EQUAL(BLOCK_SIZE, part2.get_erase_size());
TEST_ASSERT_EQUAL((BLOCK_COUNT / 2)*BLOCK_SIZE, part2.size());
// Deinit partitions
err = part1.deinit();
TEST_ASSERT_EQUAL(0, err);
err = part2.deinit();
TEST_ASSERT_EQUAL(0, err);
}
// Testing mbr read write
void test_mbr_read_write()
{
uint8_t *dummy = new (std::nothrow) uint8_t[BLOCK_COUNT * BLOCK_SIZE];
TEST_SKIP_UNLESS_MESSAGE(dummy, "Not enough memory for test");
delete[] dummy;
int err;
// Load partitions
MBRBlockDevice part1(&bd, 1);
err = part1.init();
TEST_ASSERT_EQUAL(0, err);
MBRBlockDevice part2(&bd, 2);
err = part2.init();
TEST_ASSERT_EQUAL(0, err);
// Test reading/writing the partitions
uint8_t *write_block = new (std::nothrow) uint8_t[BLOCK_SIZE];
uint8_t *read_block = new (std::nothrow) uint8_t[BLOCK_SIZE];
if (!write_block || !read_block) {
printf("Not enough memory for test");
goto end;
}
// Fill with random sequence
srand(1);
for (int i = 0; i < BLOCK_SIZE; i++) {
write_block[i] = 0xff & rand();
}
// Write, sync, and read the block
err = part1.erase(0, BLOCK_SIZE);
TEST_ASSERT_EQUAL(0, err);
err = part1.program(write_block, 0, BLOCK_SIZE);
TEST_ASSERT_EQUAL(0, err);
err = part1.read(read_block, 0, BLOCK_SIZE);
TEST_ASSERT_EQUAL(0, err);
// Check that the data was unmodified
srand(1);
for (int i = 0; i < BLOCK_SIZE; i++) {
TEST_ASSERT_EQUAL(0xff & rand(), read_block[i]);
}
// Check with original block device
err = bd.read(read_block, 1 * BLOCK_SIZE, BLOCK_SIZE);
TEST_ASSERT_EQUAL(0, err);
// Check that the data was unmodified
srand(1);
for (int i = 0; i < BLOCK_SIZE; i++) {
TEST_ASSERT_EQUAL(0xff & rand(), read_block[i]);
}
// Test with second slice of block device
srand(1);
for (int i = 0; i < BLOCK_SIZE; i++) {
write_block[i] = 0xff & rand();
}
// Write, sync, and read the block
err = part2.erase(0, BLOCK_SIZE);
TEST_ASSERT_EQUAL(0, err);
err = part2.program(write_block, 0, BLOCK_SIZE);
TEST_ASSERT_EQUAL(0, err);
err = part2.read(read_block, 0, BLOCK_SIZE);
TEST_ASSERT_EQUAL(0, err);
// Check that the data was unmodified
srand(1);
for (int i = 0; i < BLOCK_SIZE; i++) {
TEST_ASSERT_EQUAL(0xff & rand(), read_block[i]);
}
// Check with original block device
err = bd.read(read_block, (BLOCK_COUNT / 2) * BLOCK_SIZE, BLOCK_SIZE);
TEST_ASSERT_EQUAL(0, err);
// Check that the data was unmodified
srand(1);
for (int i = 0; i < BLOCK_SIZE; i++) {
TEST_ASSERT_EQUAL(0xff & rand(), read_block[i]);
}
err = part1.deinit();
TEST_ASSERT_EQUAL(0, err);
err = part2.deinit();
TEST_ASSERT_EQUAL(0, err);
end:
// Clean up
delete[] write_block;
delete[] read_block;
}
// Test setup
utest::v1::status_t test_setup(const size_t number_of_cases)
{
GREENTEA_SETUP(10, "default_auto");
return verbose_test_setup_handler(number_of_cases);
}
Case cases[] = {
Case("Testing formatting of master boot record", test_mbr_format),
Case("Testing mbr attributes", test_mbr_attr),
Case("Testing mbr read write", test_mbr_read_write),
};
Specification specification(test_setup, cases);
int main()
{
return !Harness::run(specification);
}

View File

@@ -0,0 +1,309 @@
/* mbed Microcontroller Library
* Copyright (c) 2017 ARM Limited
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "mbed.h"
#include "greentea-client/test_env.h"
#include "unity.h"
#include "utest.h"
#include "HeapBlockDevice.h"
#include "SlicingBlockDevice.h"
#include "ChainingBlockDevice.h"
#include "ProfilingBlockDevice.h"
#include <stdlib.h>
using namespace utest::v1;
#define BLOCK_COUNT 16
#define BLOCK_SIZE 512
#if ((MBED_RAM_SIZE - MBED_BOOT_STACK_SIZE) <= (BLOCK_COUNT * BLOCK_SIZE))
#error [NOT_SUPPORTED] Insufficient heap for util block device tests
#endif
// Simple test which read/writes blocks on a sliced block device
void test_slicing()
{
uint8_t *dummy = new (std::nothrow) uint8_t[BLOCK_COUNT * BLOCK_SIZE];
TEST_SKIP_UNLESS_MESSAGE(dummy, "Not enough memory for test");
delete[] dummy;
int err;
HeapBlockDevice bd(BLOCK_COUNT * BLOCK_SIZE, BLOCK_SIZE);
SlicingBlockDevice slice1(&bd, 0, (BLOCK_COUNT / 2)*BLOCK_SIZE);
SlicingBlockDevice slice2(&bd, -(BLOCK_COUNT / 2)*BLOCK_SIZE);
// Test with first slice of block device
err = slice1.init();
TEST_ASSERT_EQUAL(0, err);
TEST_ASSERT_EQUAL(BLOCK_SIZE, slice1.get_program_size());
TEST_ASSERT_EQUAL(BLOCK_SIZE, slice1.get_erase_size(BLOCK_SIZE));
TEST_ASSERT_EQUAL((BLOCK_COUNT / 2)*BLOCK_SIZE, slice1.size());
uint8_t *write_block = new (std::nothrow) uint8_t[BLOCK_SIZE];
uint8_t *read_block = new (std::nothrow) uint8_t[BLOCK_SIZE];
if (!write_block || !read_block) {
printf("Not enough memory for test");
goto end;
}
// Fill with random sequence
srand(1);
for (int i = 0; i < BLOCK_SIZE; i++) {
write_block[i] = 0xff & rand();
}
// Write, sync, and read the block
err = slice1.program(write_block, 0, BLOCK_SIZE);
TEST_ASSERT_EQUAL(0, err);
err = slice1.read(read_block, 0, BLOCK_SIZE);
TEST_ASSERT_EQUAL(0, err);
// Check that the data was unmodified
srand(1);
for (int i = 0; i < BLOCK_SIZE; i++) {
TEST_ASSERT_EQUAL(0xff & rand(), read_block[i]);
}
// Check with original block device
err = bd.read(read_block, 0, BLOCK_SIZE);
TEST_ASSERT_EQUAL(0, err);
// Check that the data was unmodified
srand(1);
for (int i = 0; i < BLOCK_SIZE; i++) {
TEST_ASSERT_EQUAL(0xff & rand(), read_block[i]);
}
// Test with second slice of block device
err = slice2.init();
TEST_ASSERT_EQUAL(0, err);
TEST_ASSERT_EQUAL(BLOCK_SIZE, slice2.get_program_size());
TEST_ASSERT_EQUAL((BLOCK_COUNT / 2)*BLOCK_SIZE, slice2.size());
// Fill with random sequence
srand(1);
for (int i = 0; i < BLOCK_SIZE; i++) {
write_block[i] = 0xff & rand();
}
// Deinitialize slice1 here, to check whether init reference count works
err = slice1.deinit();
TEST_ASSERT_EQUAL(0, err);
// Write, sync, and read the block
err = slice2.program(write_block, 0, BLOCK_SIZE);
TEST_ASSERT_EQUAL(0, err);
err = slice2.read(read_block, 0, BLOCK_SIZE);
TEST_ASSERT_EQUAL(0, err);
// Check that the data was unmodified
srand(1);
for (int i = 0; i < BLOCK_SIZE; i++) {
TEST_ASSERT_EQUAL(0xff & rand(), read_block[i]);
}
// Check with original block device
err = bd.read(read_block, (BLOCK_COUNT / 2) * BLOCK_SIZE, BLOCK_SIZE);
TEST_ASSERT_EQUAL(0, err);
// Check that the data was unmodified
srand(1);
for (int i = 0; i < BLOCK_SIZE; i++) {
TEST_ASSERT_EQUAL(0xff & rand(), read_block[i]);
}
err = slice2.deinit();
TEST_ASSERT_EQUAL(0, err);
end:
delete[] write_block;
delete[] read_block;
}
// Simple test which read/writes blocks on a chain of block devices
void test_chaining()
{
uint8_t *dummy = new (std::nothrow) uint8_t[BLOCK_COUNT * BLOCK_SIZE];
TEST_SKIP_UNLESS_MESSAGE(dummy, "Not enough memory for test");
delete[] dummy;
int err;
HeapBlockDevice bd1((BLOCK_COUNT / 2)*BLOCK_SIZE, BLOCK_SIZE);
HeapBlockDevice bd2((BLOCK_COUNT / 2)*BLOCK_SIZE, BLOCK_SIZE);
// Test with chain of block device
BlockDevice *bds[] = {&bd1, &bd2};
ChainingBlockDevice chain(bds);
uint8_t *write_block = new (std::nothrow) uint8_t[BLOCK_SIZE];
uint8_t *read_block = new (std::nothrow) uint8_t[BLOCK_SIZE];
if (!write_block || !read_block) {
printf("Not enough memory for test");
goto end;
}
err = chain.init();
TEST_ASSERT_EQUAL(0, err);
TEST_ASSERT_EQUAL(BLOCK_SIZE, chain.get_program_size());
TEST_ASSERT_EQUAL(BLOCK_SIZE, chain.get_erase_size((BLOCK_COUNT / 2)*BLOCK_SIZE + 1));
TEST_ASSERT_EQUAL(BLOCK_COUNT * BLOCK_SIZE, chain.size());
// Fill with random sequence
srand(1);
for (int i = 0; i < BLOCK_SIZE; i++) {
write_block[i] = 0xff & rand();
}
// Write, sync, and read the block
err = chain.program(write_block, 0, BLOCK_SIZE);
TEST_ASSERT_EQUAL(0, err);
err = chain.read(read_block, 0, BLOCK_SIZE);
TEST_ASSERT_EQUAL(0, err);
// Check that the data was unmodified
srand(1);
for (int i = 0; i < BLOCK_SIZE; i++) {
TEST_ASSERT_EQUAL(0xff & rand(), read_block[i]);
}
// Write, sync, and read the block
err = chain.program(write_block, (BLOCK_COUNT / 2) * BLOCK_SIZE, BLOCK_SIZE);
TEST_ASSERT_EQUAL(0, err);
err = chain.read(read_block, (BLOCK_COUNT / 2) * BLOCK_SIZE, BLOCK_SIZE);
TEST_ASSERT_EQUAL(0, err);
// Check that the data was unmodified
srand(1);
for (int i = 0; i < BLOCK_SIZE; i++) {
TEST_ASSERT_EQUAL(0xff & rand(), read_block[i]);
}
err = chain.deinit();
TEST_ASSERT_EQUAL(0, err);
end:
delete[] write_block;
delete[] read_block;
}
// Simple test which read/writes blocks on a chain of block devices
void test_profiling()
{
uint8_t *dummy = new (std::nothrow) uint8_t[BLOCK_COUNT * BLOCK_SIZE];
TEST_SKIP_UNLESS_MESSAGE(dummy, "Not enough memory for test");
delete[] dummy;
int err;
bd_size_t read_count, program_count, erase_count;
HeapBlockDevice bd(BLOCK_COUNT * BLOCK_SIZE, BLOCK_SIZE);
// Test under profiling
ProfilingBlockDevice profiler(&bd);
err = profiler.init();
TEST_ASSERT_EQUAL(0, err);
TEST_ASSERT_EQUAL(BLOCK_SIZE, profiler.get_erase_size());
TEST_ASSERT_EQUAL(BLOCK_COUNT * BLOCK_SIZE, profiler.size());
uint8_t *write_block = new (std::nothrow) uint8_t[BLOCK_SIZE];
uint8_t *read_block = new (std::nothrow) uint8_t[BLOCK_SIZE];
if (!write_block || !read_block) {
printf("Not enough memory for test");
goto end;
}
// Fill with random sequence
srand(1);
for (int i = 0; i < BLOCK_SIZE; i++) {
write_block[i] = 0xff & rand();
}
// Write, sync, and read the block
err = profiler.erase(0, BLOCK_SIZE);
TEST_ASSERT_EQUAL(0, err);
err = profiler.program(write_block, 0, BLOCK_SIZE);
TEST_ASSERT_EQUAL(0, err);
err = profiler.read(read_block, 0, BLOCK_SIZE);
TEST_ASSERT_EQUAL(0, err);
// Check that the data was unmodified
srand(1);
for (int i = 0; i < BLOCK_SIZE; i++) {
TEST_ASSERT_EQUAL(0xff & rand(), read_block[i]);
}
// Check with original block device
err = bd.read(read_block, 0, BLOCK_SIZE);
TEST_ASSERT_EQUAL(0, err);
// Check that the data was unmodified
srand(1);
for (int i = 0; i < BLOCK_SIZE; i++) {
TEST_ASSERT_EQUAL(0xff & rand(), read_block[i]);
}
err = profiler.deinit();
TEST_ASSERT_EQUAL(0, err);
// Check that profiled operations match expectations
read_count = profiler.get_read_count();
TEST_ASSERT_EQUAL(BLOCK_SIZE, read_count);
program_count = profiler.get_program_count();
TEST_ASSERT_EQUAL(BLOCK_SIZE, program_count);
erase_count = profiler.get_erase_count();
TEST_ASSERT_EQUAL(BLOCK_SIZE, erase_count);
end:
delete[] write_block;
delete[] read_block;
}
// Test setup
utest::v1::status_t test_setup(const size_t number_of_cases)
{
GREENTEA_SETUP(10, "default_auto");
return verbose_test_setup_handler(number_of_cases);
}
Case cases[] = {
Case("Testing slicing of a block device", test_slicing),
Case("Testing chaining of block devices", test_chaining),
Case("Testing profiling of block devices", test_profiling),
};
Specification specification(test_setup, cases);
int main()
{
return !Harness::run(specification);
}