Skip to content

Commit 2a50409

Browse files
author
Cruz Monrreal
authored
Merge pull request #8430 from theamirocohen/block_device_general_tests
Block device general tests
2 parents 0db8960 + 54d2f66 commit 2a50409

File tree

2 files changed

+301
-0
lines changed

2 files changed

+301
-0
lines changed
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
# Getting started with the Mbed OS block device test
2+
3+
You can use the Mbed OS block device test to test existing 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+
2. [Usage](#usage)
11+
- [Compile the test](#compile-the-test)
12+
- [Run the test](#run-the-test)
13+
3. [Changing the block device](#changing-the-block-device)
14+
4. [Tested configurations](#tested-configurations)
15+
16+
## Hardware requirements
17+
18+
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.
19+
20+
## Usage
21+
22+
#### Compile the test
23+
24+
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:
25+
26+
```
27+
mbed test -m K64F -t ARM -n mbed-os-features-storage-tests-blockdevice-general_block_device --compile
28+
```
29+
30+
#### Run the test
31+
32+
Use `mbed test` again:
33+
34+
```
35+
mbed test -m K64F -t ARM -n mbed-os-features-storage-tests-blockdevice-general_block_device --run -v
36+
```
37+
38+
#### Troubleshooting
39+
40+
Please review the [documentation](https://os.mbed.com/docs/latest/tutorials/debugging.html) for suggestions about how to fix possible issues you may face.
41+
42+
## Changing the block device
43+
44+
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.
45+
46+
This test uses the default block device that the function `get_default_instance()` receives. [SystemStorage.cpp](https://github.com/ARMmbed/mbed-os/blob/master/features/storage/system_storage/SystemStorage.cpp#L35-L77) defines this as `MBED_WEAK`. If you would like to test a new block device, you have to override it.
47+
48+
First add the new block device .cpp and header files to the [blockdevice folder](https://github.com/ARMmbed/mbed-os/tree/master/features/storage/blockdevice). Then, implement `get_default_instance()` inside the test `main.cpp`.
49+
50+
For example, to test the HeapBlockDevice, add:
51+
52+
``` diff
53+
+#define TEST_BLOCK_SIZE 128
54+
+#define TEST_BLOCK_DEVICE_SIZE 32*TEST_BLOCK_SIZE
55+
56+
+BlockDevice *BlockDevice::get_default_instance()
57+
+{
58+
+ utest_printf("NEW test block device!!!\n");
59+
+ static HeapBlockDevice default_bd(TEST_BLOCK_DEVICE_SIZE, TEST_BLOCK_SIZE);
60+
+ return &default_bd;
61+
+}
62+
```
63+
64+
Now you can recompile and run.
65+
66+
## Tested configurations
67+
68+
- K64F + SD.
69+
- K64F + Heap.
70+
- K82F + SPIF.
71+
- K82F + Heap.
Lines changed: 230 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,230 @@
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+
17+
#include "mbed.h"
18+
#include "greentea-client/test_env.h"
19+
#include "unity.h"
20+
#include "utest.h"
21+
#include "mbed_trace.h"
22+
#include <stdlib.h>
23+
24+
using namespace utest::v1;
25+
26+
#define TEST_BLOCK_COUNT 10
27+
#define TEST_ERROR_MASK 16
28+
#define TEST_NUM_OF_THREADS 5
29+
30+
const struct {
31+
const char *name;
32+
bd_size_t (BlockDevice::*method)() const;
33+
} ATTRS[] = {
34+
{"read size", &BlockDevice::get_read_size},
35+
{"program size", &BlockDevice::get_program_size},
36+
{"erase size", &BlockDevice::get_erase_size},
37+
{"total size", &BlockDevice::size},
38+
};
39+
40+
static SingletonPtr<PlatformMutex> _mutex;
41+
42+
// Mutex is protecting rand() per srand for buffer writing and verification.
43+
// Mutex is also protecting printouts for clear logs.
44+
// Mutex is NOT protecting Block Device actions: erase/program/read - which is the purpose of the multithreaded test!
45+
void basic_erase_program_read_test(BlockDevice *block_device, bd_size_t block_size, uint8_t *write_block,
46+
uint8_t *read_block, unsigned addrwidth)
47+
{
48+
int err = 0;
49+
_mutex->lock();
50+
// Find a random block
51+
bd_addr_t block = (rand() * block_size) % (block_device->size());
52+
53+
// Use next random number as temporary seed to keep
54+
// the address progressing in the pseudorandom sequence
55+
unsigned seed = rand();
56+
57+
// Fill with random sequence
58+
srand(seed);
59+
for (bd_size_t i_ind = 0; i_ind < block_size; i_ind++) {
60+
write_block[i_ind] = 0xff & rand();
61+
}
62+
// Write, sync, and read the block
63+
utest_printf("\ntest %0*llx:%llu...", addrwidth, block, block_size);
64+
_mutex->unlock();
65+
66+
err = block_device->erase(block, block_size);
67+
TEST_ASSERT_EQUAL(0, err);
68+
69+
err = block_device->program(write_block, block, block_size);
70+
TEST_ASSERT_EQUAL(0, err);
71+
72+
err = block_device->read(read_block, block, block_size);
73+
TEST_ASSERT_EQUAL(0, err);
74+
75+
_mutex->lock();
76+
// Check that the data was unmodified
77+
srand(seed);
78+
int val_rand;
79+
for (bd_size_t i_ind = 0; i_ind < block_size; i_ind++) {
80+
val_rand = rand();
81+
if ( (0xff & val_rand) != read_block[i_ind] ) {
82+
utest_printf("\n Assert Failed Buf Read - block:size: %llx:%llu \n", block, block_size);
83+
utest_printf("\n pos: %llu, exp: %02x, act: %02x, wrt: %02x \n", i_ind, (0xff & val_rand),
84+
read_block[i_ind],
85+
write_block[i_ind] );
86+
}
87+
TEST_ASSERT_EQUAL(0xff & val_rand, read_block[i_ind]);
88+
}
89+
_mutex->unlock();
90+
}
91+
92+
void test_random_program_read_erase()
93+
{
94+
utest_printf("\nTest Random Program Read Erase Starts..\n");
95+
96+
BlockDevice *block_device = BlockDevice::get_default_instance();
97+
98+
if (!block_device) {
99+
utest_printf("\nno block device found.\n");
100+
return;
101+
}
102+
103+
int err = block_device->init();
104+
TEST_ASSERT_EQUAL(0, err);
105+
106+
for (unsigned atr = 0; atr < sizeof(ATTRS) / sizeof(ATTRS[0]); atr++) {
107+
static const char *prefixes[] = {"", "k", "M", "G"};
108+
for (int i_ind = 3; i_ind >= 0; i_ind--) {
109+
bd_size_t size = (block_device->*ATTRS[atr].method)();
110+
if (size >= (1ULL << 10 * i_ind)) {
111+
utest_printf("%s: %llu%sbytes (%llubytes)\n",
112+
ATTRS[atr].name, size >> 10 * i_ind, prefixes[i_ind], size);
113+
break;
114+
}
115+
}
116+
}
117+
118+
bd_size_t block_size = block_device->get_erase_size();
119+
unsigned addrwidth = ceil(log(float(block_device->size() - 1)) / log(float(16))) + 1;
120+
121+
uint8_t *write_block = new (std::nothrow) uint8_t[block_size];
122+
uint8_t *read_block = new (std::nothrow) uint8_t[block_size];
123+
if (!write_block || !read_block) {
124+
utest_printf("\n Not enough memory for test");
125+
goto end;
126+
}
127+
128+
for (int b = 0; b < TEST_BLOCK_COUNT; b++) {
129+
basic_erase_program_read_test(block_device, block_size, write_block, read_block, addrwidth);
130+
}
131+
132+
err = block_device->deinit();
133+
TEST_ASSERT_EQUAL(0, err);
134+
135+
end:
136+
delete[] write_block;
137+
delete[] read_block;
138+
}
139+
140+
static void test_thread_job(void *block_device_ptr)
141+
{
142+
static int thread_num = 0;
143+
thread_num++;
144+
BlockDevice *block_device = (BlockDevice *)block_device_ptr;
145+
146+
bd_size_t block_size = block_device->get_erase_size();
147+
unsigned addrwidth = ceil(log(float(block_device->size() - 1)) / log(float(16))) + 1;
148+
149+
uint8_t *write_block = new (std::nothrow) uint8_t[block_size];
150+
uint8_t *read_block = new (std::nothrow) uint8_t[block_size];
151+
152+
if (!write_block || !read_block ) {
153+
utest_printf("\n Not enough memory for test");
154+
goto end;
155+
}
156+
157+
for (int b = 0; b < TEST_BLOCK_COUNT; b++) {
158+
basic_erase_program_read_test(block_device, block_size, write_block, read_block, addrwidth);
159+
}
160+
161+
end:
162+
delete[] write_block;
163+
delete[] read_block;
164+
}
165+
166+
void test_multi_threads()
167+
{
168+
utest_printf("\nTest Multi Threaded Erase/Program/Read Starts..\n");
169+
170+
BlockDevice *block_device = BlockDevice::get_default_instance();
171+
172+
if (!block_device) {
173+
utest_printf("\nno block device found.\n");
174+
return;
175+
}
176+
177+
int err = block_device->init();
178+
TEST_ASSERT_EQUAL(0, err);
179+
180+
for (unsigned atr = 0; atr < sizeof(ATTRS) / sizeof(ATTRS[0]); atr++) {
181+
static const char *prefixes[] = {"", "k", "M", "G"};
182+
for (int i_ind = 3; i_ind >= 0; i_ind--) {
183+
bd_size_t size = (block_device->*ATTRS[atr].method)();
184+
if (size >= (1ULL << 10 * i_ind)) {
185+
utest_printf("%s: %llu%sbytes (%llubytes)\n",
186+
ATTRS[atr].name, size >> 10 * i_ind, prefixes[i_ind], size);
187+
break;
188+
}
189+
}
190+
}
191+
192+
rtos::Thread bd_thread[TEST_NUM_OF_THREADS];
193+
194+
osStatus threadStatus;
195+
int i_ind;
196+
197+
for (i_ind = 0; i_ind < TEST_NUM_OF_THREADS; i_ind++) {
198+
threadStatus = bd_thread[i_ind].start(callback(test_thread_job, (void *)block_device));
199+
if (threadStatus != 0) {
200+
utest_printf("\n Thread %d Start Failed!", i_ind + 1);
201+
}
202+
}
203+
204+
for (i_ind = 0; i_ind < TEST_NUM_OF_THREADS; i_ind++) {
205+
bd_thread[i_ind].join();
206+
}
207+
208+
err = block_device->deinit();
209+
TEST_ASSERT_EQUAL(0, err);
210+
}
211+
212+
213+
// Test setup
214+
utest::v1::status_t test_setup(const size_t number_of_cases)
215+
{
216+
GREENTEA_SETUP(60, "default_auto");
217+
return verbose_test_setup_handler(number_of_cases);
218+
}
219+
220+
Case cases[] = {
221+
Case("Testing read write random blocks", test_random_program_read_erase),
222+
Case("Testing Multi Threads Erase Program Read", test_multi_threads)
223+
};
224+
225+
Specification specification(test_setup, cases);
226+
227+
int main()
228+
{
229+
return !Harness::run(specification);
230+
}

0 commit comments

Comments
 (0)