Import Mbed OS hard-float snapshot
This commit is contained in:
@@ -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);
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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.
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
Reference in New Issue
Block a user