mirror of https://github.com/ARMmbed/mbed-os.git
Add general block device tests
parent
ba23fef90b
commit
7d7a553836
|
@ -0,0 +1,75 @@
|
|||
# Getting started with the Mbed OS block device test
|
||||
|
||||
Mbed OS block device test is used to test exsiting 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)
|
||||
1. [Usage](#usage)
|
||||
- [Compile the test](#compile-the-test)
|
||||
- [Run the test](#run-the-test)
|
||||
1. [Changing the block device](#changing-the-block-device)
|
||||
1. [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 example
|
||||
|
||||
Use `mbed test` again:
|
||||
|
||||
```
|
||||
mbed test -m K64F -t ARM -n mbed-os-features-storage-tests-blockdevice-general_block_device --run
|
||||
```
|
||||
|
||||
#### Troubleshooting
|
||||
|
||||
If you have problems, you can review the [documentation](https://os.mbed.com/docs/latest/tutorials/debugging.html)
|
||||
for suggestions on what could be wrong and how to fix it.
|
||||
|
||||
## Changing the block device
|
||||
|
||||
In Mbed OS, a C++ classes 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 received by the function get_default_instance(), it is defined in [SystemStorage.cpp](https://github.com/ARMmbed/mbed-os/blob/master/features/storage/system_storage/SystemStorage.cpp#L35-L77) as MBED_WEAK, in case we would like to test a new block device we will have to override it.
|
||||
|
||||
first add the new block device cpp and header files to [blockdevice folder](https://github.com/ARMmbed/mbed-os/tree/master/features/storage/blockdevice), then implement get_default_instance() inside the test main.cpp.
|
||||
|
||||
for example, in order 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
|
|
@ -0,0 +1,219 @@
|
|||
/* 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 "mbed_trace.h"
|
||||
#include <stdlib.h>
|
||||
|
||||
using namespace utest::v1;
|
||||
|
||||
#define TEST_BLOCK_COUNT 10
|
||||
#define TEST_ERROR_MASK 16
|
||||
#define TEST_NUM_OF_THREADS 5
|
||||
|
||||
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},
|
||||
};
|
||||
|
||||
static SingletonPtr<PlatformMutex> _mutex;
|
||||
|
||||
// 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 err = 0;
|
||||
_mutex->lock();
|
||||
// Find a random block
|
||||
bd_addr_t block = (rand() * block_size) % (block_device->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
|
||||
utest_printf("\ntest %0*llx:%llu...", addrwidth, block, block_size);
|
||||
_mutex->unlock();
|
||||
|
||||
err = block_device->erase(block, 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);
|
||||
|
||||
_mutex->lock();
|
||||
// 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_random_program_read_erase()
|
||||
{
|
||||
utest_printf("\nTest Random Program Read Erase Starts..\n");
|
||||
|
||||
BlockDevice *block_device = BlockDevice::get_default_instance();
|
||||
|
||||
int err = block_device->init();
|
||||
TEST_ASSERT_EQUAL(0, err);
|
||||
|
||||
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("\n Not enough memory for test");
|
||||
goto end;
|
||||
}
|
||||
|
||||
for (int b = 0; b < TEST_BLOCK_COUNT; b++) {
|
||||
basic_erase_program_read_test(block_device, block_size, write_block, read_block, addrwidth);
|
||||
}
|
||||
|
||||
err = block_device->deinit();
|
||||
TEST_ASSERT_EQUAL(0, err);
|
||||
|
||||
end:
|
||||
delete[] write_block;
|
||||
delete[] read_block;
|
||||
}
|
||||
|
||||
static void test_thread_job(void *block_device_ptr)
|
||||
{
|
||||
static int thread_num = 0;
|
||||
thread_num++;
|
||||
BlockDevice *block_device = (BlockDevice *)block_device_ptr;
|
||||
|
||||
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("\n Not enough memory for test");
|
||||
goto end;
|
||||
}
|
||||
|
||||
for (int b = 0; b < TEST_BLOCK_COUNT; b++) {
|
||||
basic_erase_program_read_test(block_device, block_size, write_block, read_block, addrwidth);
|
||||
}
|
||||
|
||||
end:
|
||||
delete[] write_block;
|
||||
delete[] read_block;
|
||||
}
|
||||
|
||||
void test_multi_threads()
|
||||
{
|
||||
utest_printf("\nTest Multi Threaded Erase/Program/Read Starts..\n");
|
||||
|
||||
BlockDevice *block_device = BlockDevice::get_default_instance();
|
||||
|
||||
int err = block_device->init();
|
||||
TEST_ASSERT_EQUAL(0, err);
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
rtos::Thread bd_thread[TEST_NUM_OF_THREADS];
|
||||
|
||||
osStatus threadStatus;
|
||||
int i_ind;
|
||||
|
||||
for (i_ind = 0; i_ind < TEST_NUM_OF_THREADS; i_ind++) {
|
||||
threadStatus = bd_thread[i_ind].start(callback(test_thread_job, (void *)block_device));
|
||||
if (threadStatus != 0) {
|
||||
utest_printf("\n Thread %d Start Failed!", i_ind + 1);
|
||||
}
|
||||
}
|
||||
|
||||
for (i_ind = 0; i_ind < TEST_NUM_OF_THREADS; i_ind++) {
|
||||
bd_thread[i_ind].join();
|
||||
}
|
||||
|
||||
err = block_device->deinit();
|
||||
TEST_ASSERT_EQUAL(0, err);
|
||||
}
|
||||
|
||||
|
||||
// Test setup
|
||||
utest::v1::status_t test_setup(const size_t number_of_cases)
|
||||
{
|
||||
GREENTEA_SETUP(60, "default_auto");
|
||||
return verbose_test_setup_handler(number_of_cases);
|
||||
}
|
||||
|
||||
Case cases[] = {
|
||||
Case("Testing read write random blocks", test_random_program_read_erase),
|
||||
Case("Testing Multi Threads Erase Program Read", test_multi_threads)
|
||||
};
|
||||
|
||||
Specification specification(test_setup, cases);
|
||||
|
||||
int main()
|
||||
{
|
||||
//mbed_trace_init();
|
||||
utest_printf("MAIN STARTS\n");
|
||||
return !Harness::run(specification);
|
||||
}
|
Loading…
Reference in New Issue