Skip to content

Commit fc2f7ff

Browse files
authored
Merge pull request #6757 from davidsaada/david_buffered_bd
Implement BufferedBlockDevice
2 parents db73ed0 + df7fb16 commit fc2f7ff

File tree

3 files changed

+533
-0
lines changed

3 files changed

+533
-0
lines changed
Lines changed: 139 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,139 @@
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+
20+
#include "BufferedBlockDevice.h"
21+
#include "HeapBlockDevice.h"
22+
#include <stdlib.h>
23+
24+
using namespace utest::v1;
25+
26+
static const bd_size_t heap_erase_size = 512;
27+
static const bd_size_t heap_prog_size = heap_erase_size;
28+
static const bd_size_t heap_read_size = 256;
29+
static const bd_size_t num_blocks = 4;
30+
31+
void functionality_test()
32+
{
33+
HeapBlockDevice heap_bd(num_blocks * heap_erase_size, heap_read_size, heap_prog_size, heap_erase_size);
34+
BufferedBlockDevice bd(&heap_bd);
35+
36+
int err = bd.init();
37+
TEST_ASSERT_EQUAL(0, err);
38+
39+
uint8_t *read_buf, *write_buf;
40+
read_buf = new uint8_t[heap_prog_size];
41+
write_buf = new uint8_t[heap_prog_size];
42+
43+
TEST_ASSERT_EQUAL(1, bd.get_read_size());
44+
TEST_ASSERT_EQUAL(1, bd.get_program_size());
45+
TEST_ASSERT_EQUAL(heap_erase_size, bd.get_erase_size());
46+
47+
for (bd_size_t i = 0; i < num_blocks; i++) {
48+
memset(write_buf, i, heap_prog_size);
49+
err = heap_bd.program(write_buf, i * heap_prog_size, heap_prog_size);
50+
TEST_ASSERT_EQUAL(0, err);
51+
}
52+
53+
err = bd.read(read_buf, heap_prog_size + heap_prog_size / 2, 1);
54+
TEST_ASSERT_EQUAL(0, err);
55+
TEST_ASSERT_EQUAL(1, read_buf[0]);
56+
57+
err = bd.read(read_buf, 2 * heap_prog_size + heap_prog_size / 2, 4);
58+
TEST_ASSERT_EQUAL(0, err);
59+
memset(write_buf, 2, 4);
60+
TEST_ASSERT_EQUAL_UINT8_ARRAY(write_buf, read_buf, 4);
61+
62+
memset(write_buf, 1, heap_prog_size);
63+
memset(write_buf + 64, 0x5A, 8);
64+
memset(write_buf + 72, 0xA5, 8);
65+
err = bd.program(write_buf + 64, heap_prog_size + 64, 8);
66+
TEST_ASSERT_EQUAL(0, err);
67+
err = bd.program(write_buf + 72, heap_prog_size + 72, 8);
68+
TEST_ASSERT_EQUAL(0, err);
69+
err = bd.read(read_buf, heap_prog_size, heap_prog_size);
70+
TEST_ASSERT_EQUAL(0, err);
71+
TEST_ASSERT_EQUAL_UINT8_ARRAY(write_buf, read_buf, heap_prog_size);
72+
memset(write_buf, 1, heap_prog_size);
73+
// Underlying BD should not be updated before sync
74+
err = heap_bd.read(read_buf, heap_prog_size, heap_prog_size);
75+
TEST_ASSERT_EQUAL(0, err);
76+
TEST_ASSERT_EQUAL_UINT8_ARRAY(write_buf, read_buf, heap_prog_size);
77+
err = bd.sync();
78+
TEST_ASSERT_EQUAL(0, err);
79+
memset(write_buf + 64, 0x5A, 8);
80+
memset(write_buf + 72, 0xA5, 8);
81+
// Should be updated now
82+
err = bd.read(read_buf, heap_prog_size, heap_prog_size);
83+
TEST_ASSERT_EQUAL(0, err);
84+
TEST_ASSERT_EQUAL_UINT8_ARRAY(write_buf, read_buf, heap_prog_size);
85+
err = heap_bd.read(read_buf, heap_prog_size, heap_prog_size);
86+
TEST_ASSERT_EQUAL(0, err);
87+
TEST_ASSERT_EQUAL_UINT8_ARRAY(write_buf, read_buf, heap_prog_size);
88+
89+
memset(write_buf, 0xAA, 16);
90+
memset(write_buf + 16, 0xBB, 16);
91+
err = bd.program(write_buf, 3 * heap_prog_size - 16, 32);
92+
TEST_ASSERT_EQUAL(0, err);
93+
// First block should sync, but second still shouldn't
94+
memset(write_buf, 2, heap_prog_size - 16);
95+
memset(write_buf + heap_prog_size - 16, 0xAA, 16);
96+
err = heap_bd.read(read_buf, 2 * heap_prog_size, heap_prog_size);
97+
TEST_ASSERT_EQUAL(0, err);
98+
TEST_ASSERT_EQUAL_UINT8_ARRAY(write_buf, read_buf, heap_prog_size);
99+
memset(write_buf, 3, heap_prog_size);
100+
err = heap_bd.read(read_buf, 3 * heap_prog_size, heap_prog_size);
101+
TEST_ASSERT_EQUAL(0, err);
102+
TEST_ASSERT_EQUAL_UINT8_ARRAY(write_buf, read_buf, heap_prog_size);
103+
memset(write_buf, 0xBB, 16);
104+
err = bd.read(read_buf, 3 * heap_prog_size, heap_prog_size);
105+
TEST_ASSERT_EQUAL(0, err);
106+
TEST_ASSERT_EQUAL_UINT8_ARRAY(write_buf, read_buf, heap_prog_size);
107+
// Moving to another block should automatically sync
108+
err = bd.read(read_buf, 15, 1);
109+
TEST_ASSERT_EQUAL(0, err);
110+
TEST_ASSERT_EQUAL(0, read_buf[0]);
111+
err = heap_bd.read(read_buf, 3 * heap_prog_size, heap_prog_size);
112+
TEST_ASSERT_EQUAL(0, err);
113+
TEST_ASSERT_EQUAL_UINT8_ARRAY(write_buf, read_buf, heap_prog_size);
114+
err = bd.read(read_buf, 3 * heap_prog_size, heap_prog_size);
115+
TEST_ASSERT_EQUAL(0, err);
116+
TEST_ASSERT_EQUAL_UINT8_ARRAY(write_buf, read_buf, heap_prog_size);
117+
118+
delete[] read_buf;
119+
delete[] write_buf;
120+
}
121+
122+
// Test setup
123+
utest::v1::status_t test_setup(const size_t number_of_cases)
124+
{
125+
GREENTEA_SETUP(30, "default_auto");
126+
return verbose_test_setup_handler(number_of_cases);
127+
}
128+
129+
Case cases[] = {
130+
Case("BufferedBlockDevice functionality test", functionality_test),
131+
};
132+
133+
Specification specification(test_setup, cases);
134+
135+
int main()
136+
{
137+
return !Harness::run(specification);
138+
}
139+
Lines changed: 228 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,228 @@
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 "BufferedBlockDevice.h"
18+
#include "mbed_assert.h"
19+
#include <algorithm>
20+
#include <string.h>
21+
22+
static inline uint32_t align_down(bd_size_t val, bd_size_t size)
23+
{
24+
return val / size * size;
25+
}
26+
27+
BufferedBlockDevice::BufferedBlockDevice(BlockDevice *bd)
28+
: _bd(bd), _bd_program_size(0), _curr_aligned_addr(0), _flushed(true), _cache(0)
29+
{
30+
}
31+
32+
BufferedBlockDevice::~BufferedBlockDevice()
33+
{
34+
deinit();
35+
}
36+
37+
int BufferedBlockDevice::init()
38+
{
39+
int err = _bd->init();
40+
if (err) {
41+
return err;
42+
}
43+
44+
_bd_program_size = _bd->get_program_size();
45+
46+
if (!_cache) {
47+
_cache = new uint8_t[_bd_program_size];
48+
}
49+
50+
_curr_aligned_addr = _bd->size();
51+
_flushed = true;
52+
53+
return 0;
54+
}
55+
56+
int BufferedBlockDevice::deinit()
57+
{
58+
delete[] _cache;
59+
_cache = 0;
60+
return _bd->deinit();
61+
}
62+
63+
int BufferedBlockDevice::flush()
64+
{
65+
if (!_flushed) {
66+
int ret = _bd->program(_cache, _curr_aligned_addr, _bd_program_size);
67+
if (ret) {
68+
return ret;
69+
}
70+
_flushed = true;
71+
}
72+
return 0;
73+
}
74+
75+
int BufferedBlockDevice::sync()
76+
{
77+
int ret = flush();
78+
if (ret) {
79+
return ret;
80+
}
81+
return _bd->sync();
82+
}
83+
84+
int BufferedBlockDevice::read(void *b, bd_addr_t addr, bd_size_t size)
85+
{
86+
MBED_ASSERT(_cache);
87+
bool moved_unit = false;
88+
89+
bd_addr_t aligned_addr = align_down(addr, _bd_program_size);
90+
91+
uint8_t *buf = static_cast<uint8_t *> (b);
92+
93+
if (aligned_addr != _curr_aligned_addr) {
94+
// Need to flush if moved to another program unit
95+
flush();
96+
_curr_aligned_addr = aligned_addr;
97+
moved_unit = true;
98+
}
99+
100+
while (size) {
101+
_curr_aligned_addr = align_down(addr, _bd_program_size);
102+
if (moved_unit) {
103+
int ret = _bd->read(_cache, _curr_aligned_addr, _bd_program_size);
104+
if (ret) {
105+
return ret;
106+
}
107+
}
108+
bd_addr_t offs_in_buf = addr - _curr_aligned_addr;
109+
bd_size_t chunk = std::min(_bd_program_size - offs_in_buf, size);
110+
memcpy(buf, _cache + offs_in_buf, chunk);
111+
moved_unit = true;
112+
buf += chunk;
113+
addr += chunk;
114+
size -= chunk;
115+
}
116+
117+
return 0;
118+
}
119+
120+
int BufferedBlockDevice::program(const void *b, bd_addr_t addr, bd_size_t size)
121+
{
122+
MBED_ASSERT(_cache);
123+
int ret;
124+
bool moved_unit = false;
125+
126+
bd_addr_t aligned_addr = align_down(addr, _bd_program_size);
127+
128+
const uint8_t *buf = static_cast <const uint8_t *> (b);
129+
130+
// Need to flush if moved to another program unit
131+
if (aligned_addr != _curr_aligned_addr) {
132+
flush();
133+
_curr_aligned_addr = aligned_addr;
134+
moved_unit = true;
135+
}
136+
137+
while (size) {
138+
_curr_aligned_addr = align_down(addr, _bd_program_size);
139+
bd_addr_t offs_in_buf = addr - _curr_aligned_addr;
140+
bd_size_t chunk = std::min(_bd_program_size - offs_in_buf, size);
141+
const uint8_t *prog_buf;
142+
if (chunk < _bd_program_size) {
143+
// If moved a unit, and program doesn't cover entire unit, it means we don't have the entire
144+
// program unit cached - need to complete it from underlying BD
145+
if (moved_unit) {
146+
ret = _bd->read(_cache, _curr_aligned_addr, _bd_program_size);
147+
if (ret) {
148+
return ret;
149+
}
150+
}
151+
memcpy(_cache + offs_in_buf, buf, chunk);
152+
prog_buf = _cache;
153+
} else {
154+
// No need to copy data to our cache on each iteration. Just make sure it's updated
155+
// on the last iteration, when size is not greater than program size (can't be smaller, as
156+
// this is covered in the previous condition).
157+
prog_buf = buf;
158+
if (size == _bd_program_size) {
159+
memcpy(_cache, buf, _bd_program_size);
160+
}
161+
}
162+
163+
// Don't flush on the last iteration, just on all preceding ones.
164+
if (size > chunk) {
165+
ret = _bd->program(prog_buf, _curr_aligned_addr, _bd_program_size);
166+
if (ret) {
167+
return ret;
168+
}
169+
_bd->sync();
170+
} else {
171+
_flushed = false;
172+
}
173+
174+
moved_unit = true;
175+
buf += chunk;
176+
addr += chunk;
177+
size -= chunk;
178+
}
179+
180+
return 0;
181+
}
182+
183+
int BufferedBlockDevice::erase(bd_addr_t addr, bd_size_t size)
184+
{
185+
MBED_ASSERT(is_valid_erase(addr, size));
186+
return _bd->erase(addr, size);
187+
}
188+
189+
int BufferedBlockDevice::trim(bd_addr_t addr, bd_size_t size)
190+
{
191+
MBED_ASSERT(is_valid_erase(addr, size));
192+
193+
if ((_curr_aligned_addr >= addr) && (_curr_aligned_addr <= addr + size)) {
194+
_flushed = true;
195+
_curr_aligned_addr = _bd->size();
196+
}
197+
return _bd->trim(addr, size);
198+
}
199+
200+
bd_size_t BufferedBlockDevice::get_read_size() const
201+
{
202+
return 1;
203+
}
204+
205+
bd_size_t BufferedBlockDevice::get_program_size() const
206+
{
207+
return 1;
208+
}
209+
210+
bd_size_t BufferedBlockDevice::get_erase_size() const
211+
{
212+
return _bd->get_erase_size();
213+
}
214+
215+
bd_size_t BufferedBlockDevice::get_erase_size(bd_addr_t addr) const
216+
{
217+
return _bd->get_erase_size(addr);
218+
}
219+
220+
int BufferedBlockDevice::get_erase_value() const
221+
{
222+
return _bd->get_erase_value();
223+
}
224+
225+
bd_size_t BufferedBlockDevice::size() const
226+
{
227+
return _bd->size();
228+
}

0 commit comments

Comments
 (0)