Skip to content

Commit 7d7a553

Browse files
committed
Add general block device tests
1 parent ba23fef commit 7d7a553

File tree

2 files changed

+294
-0
lines changed

2 files changed

+294
-0
lines changed
Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
# Getting started with the Mbed OS block device test
2+
3+
Mbed OS block device test is used to test exsiting and new block devices.
4+
5+
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).
6+
7+
**Table of contents:**
8+
9+
1. [Hardware requirements](#hardware-requirements)
10+
1. [Usage](#usage)
11+
- [Compile the test](#compile-the-test)
12+
- [Run the test](#run-the-test)
13+
1. [Changing the block device](#changing-the-block-device)
14+
1. [Tested configurations](#tested-configurations)
15+
16+
## Hardware requirements
17+
18+
This test uses a block device as storage. This can be either an external
19+
block device (one of SPI flash, DataFlash or an SD card) or simulated on a
20+
heap block device on boards with enough RAM.
21+
22+
## Usage
23+
24+
#### Compile the test
25+
26+
Invoke `mbed test`, and specify the name of your platform and your favorite
27+
toolchain (`GCC_ARM`, `ARM`, `IAR`). For example, for the ARM Compiler 5:
28+
29+
```
30+
mbed test -m K64F -t ARM -n mbed-os-features-storage-tests-blockdevice-general_block_device --compile
31+
```
32+
33+
#### Run the example
34+
35+
Use `mbed test` again:
36+
37+
```
38+
mbed test -m K64F -t ARM -n mbed-os-features-storage-tests-blockdevice-general_block_device --run
39+
```
40+
41+
#### Troubleshooting
42+
43+
If you have problems, you can review the [documentation](https://os.mbed.com/docs/latest/tutorials/debugging.html)
44+
for suggestions on what could be wrong and how to fix it.
45+
46+
## Changing the block device
47+
48+
In Mbed OS, a C++ classes that inherits from the [BlockDevice](https://os.mbed.com/docs/latest/reference/storage.html#block-devices)
49+
interface represents each block device.
50+
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.
51+
52+
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.
53+
54+
for example, in order to test the HeapBlockDevice add:
55+
56+
``` diff
57+
+#define TEST_BLOCK_SIZE 128
58+
+#define TEST_BLOCK_DEVICE_SIZE 32*TEST_BLOCK_SIZE
59+
60+
+BlockDevice *BlockDevice::get_default_instance()
61+
+{
62+
+ utest_printf("NEW test block device!!!\n");
63+
+ static HeapBlockDevice default_bd(TEST_BLOCK_DEVICE_SIZE, TEST_BLOCK_SIZE);
64+
+ return &default_bd;
65+
+}
66+
```
67+
68+
Now you can recompile and run.
69+
70+
## Tested configurations
71+
72+
- K64F + SD
73+
- K64F + Heap
74+
- K82F + SPIF
75+
- K82F + Heap
Lines changed: 219 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,219 @@
1+
/* mbed Microcontroller Library
2+
* Copyright (c) 2018 ARM Limited
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
#include "greentea-client/test_env.h"
17+
#include "unity.h"
18+
#include "utest.h"
19+
#include "mbed_trace.h"
20+
#include <stdlib.h>
21+
22+
using namespace utest::v1;
23+
24+
#define TEST_BLOCK_COUNT 10
25+
#define TEST_ERROR_MASK 16
26+
#define TEST_NUM_OF_THREADS 5
27+
28+
const struct {
29+
const char *name;
30+
bd_size_t (BlockDevice::*method)() const;
31+
} ATTRS[] = {
32+
{"read size", &BlockDevice::get_read_size},
33+
{"program size", &BlockDevice::get_program_size},
34+
{"erase size", &BlockDevice::get_erase_size},
35+
{"total size", &BlockDevice::size},
36+
};
37+
38+
static SingletonPtr<PlatformMutex> _mutex;
39+
40+
// Mutex is protecting rand() per srand for buffer writing and verification.
41+
// Mutex is also protecting printouts for clear logs.
42+
// Mutex is NOT protecting Block Device actions: erase/program/read - which is the purpose of the multithreaded test!
43+
void basic_erase_program_read_test(BlockDevice *block_device, bd_size_t block_size, uint8_t *write_block,
44+
uint8_t *read_block, unsigned addrwidth)
45+
{
46+
int err = 0;
47+
_mutex->lock();
48+
// Find a random block
49+
bd_addr_t block = (rand() * block_size) % (block_device->size());
50+
51+
// Use next random number as temporary seed to keep
52+
// the address progressing in the pseudorandom sequence
53+
unsigned seed = rand();
54+
55+
// Fill with random sequence
56+
srand(seed);
57+
for (bd_size_t i_ind = 0; i_ind < block_size; i_ind++) {
58+
write_block[i_ind] = 0xff & rand();
59+
}
60+
// Write, sync, and read the block
61+
utest_printf("\ntest %0*llx:%llu...", addrwidth, block, block_size);
62+
_mutex->unlock();
63+
64+
err = block_device->erase(block, block_size);
65+
TEST_ASSERT_EQUAL(0, err);
66+
67+
err = block_device->program(write_block, block, block_size);
68+
TEST_ASSERT_EQUAL(0, err);
69+
70+
err = block_device->read(read_block, block, block_size);
71+
TEST_ASSERT_EQUAL(0, err);
72+
73+
_mutex->lock();
74+
// Check that the data was unmodified
75+
srand(seed);
76+
int val_rand;
77+
for (bd_size_t i_ind = 0; i_ind < block_size; i_ind++) {
78+
val_rand = rand();
79+
if ( (0xff & val_rand) != read_block[i_ind] ) {
80+
utest_printf("\n Assert Failed Buf Read - block:size: %llx:%llu \n", block, block_size);
81+
utest_printf("\n pos: %llu, exp: %02x, act: %02x, wrt: %02x \n", i_ind, (0xff & val_rand), read_block[i_ind],
82+
write_block[i_ind] );
83+
}
84+
TEST_ASSERT_EQUAL(0xff & val_rand, read_block[i_ind]);
85+
}
86+
_mutex->unlock();
87+
}
88+
89+
void test_random_program_read_erase()
90+
{
91+
utest_printf("\nTest Random Program Read Erase Starts..\n");
92+
93+
BlockDevice *block_device = BlockDevice::get_default_instance();
94+
95+
int err = block_device->init();
96+
TEST_ASSERT_EQUAL(0, err);
97+
98+
for (unsigned atr = 0; atr < sizeof(ATTRS) / sizeof(ATTRS[0]); atr++) {
99+
static const char *prefixes[] = {"", "k", "M", "G"};
100+
for (int i_ind = 3; i_ind >= 0; i_ind--) {
101+
bd_size_t size = (block_device->*ATTRS[atr].method)();
102+
if (size >= (1ULL << 10 * i_ind)) {
103+
utest_printf("%s: %llu%sbytes (%llubytes)\n",
104+
ATTRS[atr].name, size >> 10 * i_ind, prefixes[i_ind], size);
105+
break;
106+
}
107+
}
108+
}
109+
110+
bd_size_t block_size = block_device->get_erase_size();
111+
unsigned addrwidth = ceil(log(float(block_device->size() - 1)) / log(float(16))) + 1;
112+
113+
uint8_t *write_block = new (std::nothrow) uint8_t[block_size];
114+
uint8_t *read_block = new (std::nothrow) uint8_t[block_size];
115+
if (!write_block || !read_block) {
116+
utest_printf("\n Not enough memory for test");
117+
goto end;
118+
}
119+
120+
for (int b = 0; b < TEST_BLOCK_COUNT; b++) {
121+
basic_erase_program_read_test(block_device, block_size, write_block, read_block, addrwidth);
122+
}
123+
124+
err = block_device->deinit();
125+
TEST_ASSERT_EQUAL(0, err);
126+
127+
end:
128+
delete[] write_block;
129+
delete[] read_block;
130+
}
131+
132+
static void test_thread_job(void *block_device_ptr)
133+
{
134+
static int thread_num = 0;
135+
thread_num++;
136+
BlockDevice *block_device = (BlockDevice *)block_device_ptr;
137+
138+
bd_size_t block_size = block_device->get_erase_size();
139+
unsigned addrwidth = ceil(log(float(block_device->size() - 1)) / log(float(16))) + 1;
140+
141+
uint8_t *write_block = new (std::nothrow) uint8_t[block_size];
142+
uint8_t *read_block = new (std::nothrow) uint8_t[block_size];
143+
144+
if (!write_block || !read_block ) {
145+
utest_printf("\n Not enough memory for test");
146+
goto end;
147+
}
148+
149+
for (int b = 0; b < TEST_BLOCK_COUNT; b++) {
150+
basic_erase_program_read_test(block_device, block_size, write_block, read_block, addrwidth);
151+
}
152+
153+
end:
154+
delete[] write_block;
155+
delete[] read_block;
156+
}
157+
158+
void test_multi_threads()
159+
{
160+
utest_printf("\nTest Multi Threaded Erase/Program/Read Starts..\n");
161+
162+
BlockDevice *block_device = BlockDevice::get_default_instance();
163+
164+
int err = block_device->init();
165+
TEST_ASSERT_EQUAL(0, err);
166+
167+
for (unsigned atr = 0; atr < sizeof(ATTRS) / sizeof(ATTRS[0]); atr++) {
168+
static const char *prefixes[] = {"", "k", "M", "G"};
169+
for (int i_ind = 3; i_ind >= 0; i_ind--) {
170+
bd_size_t size = (block_device->*ATTRS[atr].method)();
171+
if (size >= (1ULL << 10 * i_ind)) {
172+
utest_printf("%s: %llu%sbytes (%llubytes)\n",
173+
ATTRS[atr].name, size >> 10 * i_ind, prefixes[i_ind], size);
174+
break;
175+
}
176+
}
177+
}
178+
179+
rtos::Thread bd_thread[TEST_NUM_OF_THREADS];
180+
181+
osStatus threadStatus;
182+
int i_ind;
183+
184+
for (i_ind = 0; i_ind < TEST_NUM_OF_THREADS; i_ind++) {
185+
threadStatus = bd_thread[i_ind].start(callback(test_thread_job, (void *)block_device));
186+
if (threadStatus != 0) {
187+
utest_printf("\n Thread %d Start Failed!", i_ind + 1);
188+
}
189+
}
190+
191+
for (i_ind = 0; i_ind < TEST_NUM_OF_THREADS; i_ind++) {
192+
bd_thread[i_ind].join();
193+
}
194+
195+
err = block_device->deinit();
196+
TEST_ASSERT_EQUAL(0, err);
197+
}
198+
199+
200+
// Test setup
201+
utest::v1::status_t test_setup(const size_t number_of_cases)
202+
{
203+
GREENTEA_SETUP(60, "default_auto");
204+
return verbose_test_setup_handler(number_of_cases);
205+
}
206+
207+
Case cases[] = {
208+
Case("Testing read write random blocks", test_random_program_read_erase),
209+
Case("Testing Multi Threads Erase Program Read", test_multi_threads)
210+
};
211+
212+
Specification specification(test_setup, cases);
213+
214+
int main()
215+
{
216+
//mbed_trace_init();
217+
utest_printf("MAIN STARTS\n");
218+
return !Harness::run(specification);
219+
}

0 commit comments

Comments
 (0)