mirror of https://github.com/ARMmbed/mbed-os.git
Merge branch 'david_buffered_bd_perf' of ssh://github.com/davidsaada/mbed-os into rollup-b.1
commit
0e4f843022
|
@ -24,105 +24,155 @@
|
||||||
using namespace utest::v1;
|
using namespace utest::v1;
|
||||||
|
|
||||||
static const bd_size_t heap_erase_size = 512;
|
static const bd_size_t heap_erase_size = 512;
|
||||||
static const bd_size_t heap_prog_size = heap_erase_size;
|
|
||||||
static const bd_size_t heap_read_size = 256;
|
|
||||||
static const bd_size_t num_blocks = 4;
|
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()
|
void functionality_test()
|
||||||
{
|
{
|
||||||
uint8_t *dummy = new (std::nothrow) uint8_t[num_blocks * heap_erase_size + heap_prog_size];
|
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");
|
TEST_SKIP_UNLESS_MESSAGE(dummy, "Not enough memory for test");
|
||||||
delete[] dummy;
|
delete[] dummy;
|
||||||
|
|
||||||
HeapBlockDevice heap_bd(num_blocks * heap_erase_size, heap_read_size, heap_prog_size, heap_erase_size);
|
HeapBlockDevice *heap_bd = new HeapBlockDevice(num_blocks * heap_erase_size, heap_read_size, heap_prog_size, heap_erase_size);
|
||||||
BufferedBlockDevice bd(&heap_bd);
|
BufferedBlockDevice *bd = new BufferedBlockDevice(heap_bd);
|
||||||
|
|
||||||
int err = bd.init();
|
int err = bd->init();
|
||||||
TEST_ASSERT_EQUAL(0, err);
|
TEST_ASSERT_EQUAL(0, err);
|
||||||
|
|
||||||
uint8_t *read_buf, *write_buf;
|
TEST_ASSERT_EQUAL(1, bd->get_read_size());
|
||||||
read_buf = new (std::nothrow) uint8_t[heap_prog_size];
|
TEST_ASSERT_EQUAL(1, bd->get_program_size());
|
||||||
TEST_SKIP_UNLESS_MESSAGE(read_buf, "Not enough memory for test");
|
TEST_ASSERT_EQUAL(heap_erase_size, bd->get_erase_size());
|
||||||
write_buf = new (std::nothrow) uint8_t[heap_prog_size];
|
|
||||||
TEST_SKIP_UNLESS_MESSAGE(write_buf, "Not enough memory for test");
|
|
||||||
|
|
||||||
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++) {
|
for (bd_size_t i = 0; i < num_blocks; i++) {
|
||||||
memset(write_buf, i, heap_prog_size);
|
memset(write_buf, i, heap_erase_size);
|
||||||
err = heap_bd.program(write_buf, i * heap_prog_size, heap_prog_size);
|
err = heap_bd->program(write_buf, i * heap_erase_size, heap_erase_size);
|
||||||
TEST_ASSERT_EQUAL(0, err);
|
// 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_prog_size + heap_prog_size / 2, 1);
|
err = bd->read(read_buf, heap_erase_size + heap_erase_size / 2, 1);
|
||||||
TEST_ASSERT_EQUAL(0, err);
|
TEST_ASSERT_EQUAL(0, err);
|
||||||
TEST_ASSERT_EQUAL(1, read_buf[0]);
|
TEST_ASSERT_EQUAL(1, read_buf[0]);
|
||||||
|
|
||||||
err = bd.read(read_buf, 2 * heap_prog_size + heap_prog_size / 2, 4);
|
err = bd->read(read_buf, 2 * heap_erase_size + heap_erase_size / 2, 4);
|
||||||
TEST_ASSERT_EQUAL(0, err);
|
TEST_ASSERT_EQUAL(0, err);
|
||||||
memset(write_buf, 2, 4);
|
memset(write_buf, 2, 4);
|
||||||
TEST_ASSERT_EQUAL_UINT8_ARRAY(write_buf, read_buf, 4);
|
TEST_ASSERT_EQUAL_UINT8_ARRAY(write_buf, read_buf, 4);
|
||||||
|
|
||||||
memset(write_buf, 1, heap_prog_size);
|
memset(write_buf, 1, heap_erase_size);
|
||||||
memset(write_buf + 64, 0x5A, 8);
|
memset(write_buf + 64, 0x5A, 8);
|
||||||
memset(write_buf + 72, 0xA5, 8);
|
memset(write_buf + 72, 0xA5, 8);
|
||||||
err = bd.program(write_buf + 64, heap_prog_size + 64, 8);
|
err = bd->program(write_buf + 64, heap_erase_size + 64, 8);
|
||||||
TEST_ASSERT_EQUAL(0, err);
|
TEST_ASSERT_EQUAL(0, err);
|
||||||
err = bd.program(write_buf + 72, heap_prog_size + 72, 8);
|
err = bd->program(write_buf + 72, heap_erase_size + 72, 8);
|
||||||
TEST_ASSERT_EQUAL(0, err);
|
TEST_ASSERT_EQUAL(0, err);
|
||||||
err = bd.read(read_buf, heap_prog_size, heap_prog_size);
|
err = bd->read(read_buf, heap_erase_size, heap_erase_size);
|
||||||
TEST_ASSERT_EQUAL(0, err);
|
TEST_ASSERT_EQUAL(0, err);
|
||||||
TEST_ASSERT_EQUAL_UINT8_ARRAY(write_buf, read_buf, heap_prog_size);
|
TEST_ASSERT_EQUAL_UINT8_ARRAY(write_buf, read_buf, heap_erase_size);
|
||||||
memset(write_buf, 1, heap_prog_size);
|
memset(write_buf, 1, heap_erase_size);
|
||||||
// Underlying BD should not be updated before sync
|
// Underlying BD should not be updated before sync
|
||||||
err = heap_bd.read(read_buf, heap_prog_size, heap_prog_size);
|
err = heap_bd->read(read_buf, heap_erase_size, heap_erase_size);
|
||||||
TEST_ASSERT_EQUAL(0, err);
|
TEST_ASSERT_EQUAL(0, err);
|
||||||
TEST_ASSERT_EQUAL_UINT8_ARRAY(write_buf, read_buf, heap_prog_size);
|
if (heap_prog_size > 1) {
|
||||||
err = bd.sync();
|
TEST_ASSERT_EQUAL_UINT8_ARRAY(write_buf, read_buf, heap_erase_size);
|
||||||
|
}
|
||||||
|
err = bd->sync();
|
||||||
TEST_ASSERT_EQUAL(0, err);
|
TEST_ASSERT_EQUAL(0, err);
|
||||||
memset(write_buf + 64, 0x5A, 8);
|
memset(write_buf + 64, 0x5A, 8);
|
||||||
memset(write_buf + 72, 0xA5, 8);
|
memset(write_buf + 72, 0xA5, 8);
|
||||||
// Should be updated now
|
// Should be updated now
|
||||||
err = bd.read(read_buf, heap_prog_size, heap_prog_size);
|
err = bd->read(read_buf, heap_erase_size, heap_erase_size);
|
||||||
TEST_ASSERT_EQUAL(0, err);
|
TEST_ASSERT_EQUAL(0, err);
|
||||||
TEST_ASSERT_EQUAL_UINT8_ARRAY(write_buf, read_buf, heap_prog_size);
|
TEST_ASSERT_EQUAL_UINT8_ARRAY(write_buf, read_buf, heap_erase_size);
|
||||||
err = heap_bd.read(read_buf, heap_prog_size, heap_prog_size);
|
err = heap_bd->read(read_buf, heap_erase_size, heap_erase_size);
|
||||||
TEST_ASSERT_EQUAL(0, err);
|
TEST_ASSERT_EQUAL(0, err);
|
||||||
TEST_ASSERT_EQUAL_UINT8_ARRAY(write_buf, read_buf, heap_prog_size);
|
TEST_ASSERT_EQUAL_UINT8_ARRAY(write_buf, read_buf, heap_erase_size);
|
||||||
|
|
||||||
memset(write_buf, 0xAA, 16);
|
memset(write_buf, 0xAA, 16);
|
||||||
memset(write_buf + 16, 0xBB, 16);
|
memset(write_buf + 16, 0xBB, 16);
|
||||||
err = bd.program(write_buf, 3 * heap_prog_size - 16, 32);
|
err = bd->program(write_buf, 3 * heap_erase_size - 16, 32);
|
||||||
TEST_ASSERT_EQUAL(0, err);
|
TEST_ASSERT_EQUAL(0, err);
|
||||||
// First block should sync, but second still shouldn't
|
// First block should sync, but second still shouldn't
|
||||||
memset(write_buf, 2, heap_prog_size - 16);
|
memset(write_buf, 2, heap_erase_size - 16);
|
||||||
memset(write_buf + heap_prog_size - 16, 0xAA, 16);
|
memset(write_buf + heap_erase_size - 16, 0xAA, 16);
|
||||||
err = heap_bd.read(read_buf, 2 * heap_prog_size, heap_prog_size);
|
err = heap_bd->read(read_buf, 2 * heap_erase_size, heap_erase_size);
|
||||||
TEST_ASSERT_EQUAL(0, err);
|
TEST_ASSERT_EQUAL(0, err);
|
||||||
TEST_ASSERT_EQUAL_UINT8_ARRAY(write_buf, read_buf, heap_prog_size);
|
TEST_ASSERT_EQUAL_UINT8_ARRAY(write_buf, read_buf, heap_erase_size);
|
||||||
memset(write_buf, 3, heap_prog_size);
|
memset(write_buf, 3, heap_erase_size);
|
||||||
err = heap_bd.read(read_buf, 3 * heap_prog_size, heap_prog_size);
|
err = heap_bd->read(read_buf, 3 * heap_erase_size, heap_erase_size);
|
||||||
TEST_ASSERT_EQUAL(0, err);
|
TEST_ASSERT_EQUAL(0, err);
|
||||||
TEST_ASSERT_EQUAL_UINT8_ARRAY(write_buf, read_buf, heap_prog_size);
|
if (heap_prog_size > 1) {
|
||||||
|
TEST_ASSERT_EQUAL_UINT8_ARRAY(write_buf, read_buf, heap_erase_size);
|
||||||
|
}
|
||||||
memset(write_buf, 0xBB, 16);
|
memset(write_buf, 0xBB, 16);
|
||||||
err = bd.read(read_buf, 3 * heap_prog_size, heap_prog_size);
|
err = bd->read(read_buf, 3 * heap_erase_size, heap_erase_size);
|
||||||
TEST_ASSERT_EQUAL(0, err);
|
TEST_ASSERT_EQUAL(0, err);
|
||||||
TEST_ASSERT_EQUAL_UINT8_ARRAY(write_buf, read_buf, heap_prog_size);
|
TEST_ASSERT_EQUAL_UINT8_ARRAY(write_buf, read_buf, heap_erase_size);
|
||||||
// Moving to another block should automatically sync
|
// Writing to another block should automatically sync
|
||||||
err = bd.read(read_buf, 15, 1);
|
err = bd->program(write_buf, 15, 1);
|
||||||
TEST_ASSERT_EQUAL(0, err);
|
TEST_ASSERT_EQUAL(0, err);
|
||||||
TEST_ASSERT_EQUAL(0, read_buf[0]);
|
err = heap_bd->read(read_buf, 3 * heap_erase_size, heap_erase_size);
|
||||||
err = heap_bd.read(read_buf, 3 * heap_prog_size, heap_prog_size);
|
|
||||||
TEST_ASSERT_EQUAL(0, err);
|
TEST_ASSERT_EQUAL(0, err);
|
||||||
TEST_ASSERT_EQUAL_UINT8_ARRAY(write_buf, read_buf, heap_prog_size);
|
TEST_ASSERT_EQUAL_UINT8_ARRAY(write_buf, read_buf, heap_erase_size);
|
||||||
err = bd.read(read_buf, 3 * heap_prog_size, heap_prog_size);
|
err = bd->read(read_buf, 3 * heap_erase_size, heap_erase_size);
|
||||||
TEST_ASSERT_EQUAL(0, err);
|
TEST_ASSERT_EQUAL(0, err);
|
||||||
TEST_ASSERT_EQUAL_UINT8_ARRAY(write_buf, read_buf, heap_prog_size);
|
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[] read_buf;
|
||||||
delete[] write_buf;
|
delete[] write_buf;
|
||||||
|
delete bd;
|
||||||
|
delete heap_bd;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test setup
|
// Test setup
|
||||||
|
|
|
@ -26,7 +26,8 @@ static inline uint32_t align_down(bd_size_t val, bd_size_t size)
|
||||||
}
|
}
|
||||||
|
|
||||||
BufferedBlockDevice::BufferedBlockDevice(BlockDevice *bd)
|
BufferedBlockDevice::BufferedBlockDevice(BlockDevice *bd)
|
||||||
: _bd(bd), _bd_program_size(0), _curr_aligned_addr(0), _flushed(true), _cache(0), _init_ref_count(0), _is_initialized(false)
|
: _bd(bd), _bd_program_size(0), _bd_read_size(0), _write_cache_addr(0), _write_cache_valid(false),
|
||||||
|
_write_cache(0), _read_buf(0), _init_ref_count(0), _is_initialized(false)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -48,14 +49,19 @@ int BufferedBlockDevice::init()
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_bd_read_size = _bd->get_read_size();
|
||||||
_bd_program_size = _bd->get_program_size();
|
_bd_program_size = _bd->get_program_size();
|
||||||
|
_bd_size = _bd->size();
|
||||||
|
|
||||||
if (!_cache) {
|
if (!_write_cache) {
|
||||||
_cache = new uint8_t[_bd_program_size];
|
_write_cache = new uint8_t[_bd_program_size];
|
||||||
}
|
}
|
||||||
|
|
||||||
_curr_aligned_addr = _bd->size();
|
if (!_read_buf) {
|
||||||
_flushed = true;
|
_read_buf = new uint8_t[_bd_read_size];
|
||||||
|
}
|
||||||
|
|
||||||
|
invalidate_write_cache();
|
||||||
|
|
||||||
_is_initialized = true;
|
_is_initialized = true;
|
||||||
return BD_ERROR_OK;
|
return BD_ERROR_OK;
|
||||||
|
@ -73,34 +79,44 @@ int BufferedBlockDevice::deinit()
|
||||||
return BD_ERROR_OK;
|
return BD_ERROR_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
delete[] _cache;
|
delete[] _write_cache;
|
||||||
_cache = 0;
|
_write_cache = 0;
|
||||||
|
delete[] _read_buf;
|
||||||
|
_read_buf = 0;
|
||||||
_is_initialized = false;
|
_is_initialized = false;
|
||||||
return _bd->deinit();
|
return _bd->deinit();
|
||||||
}
|
}
|
||||||
|
|
||||||
int BufferedBlockDevice::flush()
|
int BufferedBlockDevice::flush()
|
||||||
{
|
{
|
||||||
|
MBED_ASSERT(_write_cache);
|
||||||
if (!_is_initialized) {
|
if (!_is_initialized) {
|
||||||
return BD_ERROR_DEVICE_ERROR;
|
return BD_ERROR_DEVICE_ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!_flushed) {
|
if (_write_cache_valid) {
|
||||||
int ret = _bd->program(_cache, _curr_aligned_addr, _bd_program_size);
|
int ret = _bd->program(_write_cache, _write_cache_addr, _bd_program_size);
|
||||||
if (ret) {
|
if (ret) {
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
_flushed = true;
|
invalidate_write_cache();
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void BufferedBlockDevice::invalidate_write_cache()
|
||||||
|
{
|
||||||
|
_write_cache_addr = _bd_size;
|
||||||
|
_write_cache_valid = false;
|
||||||
|
}
|
||||||
|
|
||||||
int BufferedBlockDevice::sync()
|
int BufferedBlockDevice::sync()
|
||||||
{
|
{
|
||||||
if (!_is_initialized) {
|
if (!_is_initialized) {
|
||||||
return BD_ERROR_DEVICE_ERROR;
|
return BD_ERROR_DEVICE_ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
MBED_ASSERT(_write_cache);
|
||||||
int ret = flush();
|
int ret = flush();
|
||||||
if (ret) {
|
if (ret) {
|
||||||
return ret;
|
return ret;
|
||||||
|
@ -110,36 +126,52 @@ int BufferedBlockDevice::sync()
|
||||||
|
|
||||||
int BufferedBlockDevice::read(void *b, bd_addr_t addr, bd_size_t size)
|
int BufferedBlockDevice::read(void *b, bd_addr_t addr, bd_size_t size)
|
||||||
{
|
{
|
||||||
MBED_ASSERT(_cache);
|
|
||||||
if (!_is_initialized) {
|
if (!_is_initialized) {
|
||||||
return BD_ERROR_DEVICE_ERROR;
|
return BD_ERROR_DEVICE_ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool moved_unit = false;
|
MBED_ASSERT(_write_cache && _read_buf);
|
||||||
|
// Common case - no need to involve write cache or read buffer
|
||||||
bd_addr_t aligned_addr = align_down(addr, _bd_program_size);
|
if (_bd->is_valid_read(addr, size) &&
|
||||||
|
((addr + size <= _write_cache_addr) || (addr > _write_cache_addr + _bd_program_size))) {
|
||||||
|
return _bd->read(b, addr, size);
|
||||||
|
}
|
||||||
|
|
||||||
uint8_t *buf = static_cast<uint8_t *>(b);
|
uint8_t *buf = static_cast<uint8_t *>(b);
|
||||||
|
|
||||||
if (aligned_addr != _curr_aligned_addr) {
|
// Read logic: Split read to chunks, according to whether we cross the write cache
|
||||||
// Need to flush if moved to another program unit
|
while (size) {
|
||||||
flush();
|
bd_size_t chunk;
|
||||||
_curr_aligned_addr = aligned_addr;
|
bool read_from_bd = true;
|
||||||
moved_unit = true;
|
if (addr < _write_cache_addr) {
|
||||||
|
chunk = std::min(size, _write_cache_addr - addr);
|
||||||
|
} else if ((addr >= _write_cache_addr) && (addr < _write_cache_addr + _bd_program_size)) {
|
||||||
|
// One case we need to take our data from cache
|
||||||
|
chunk = std::min(size, _bd_program_size - addr % _bd_program_size);
|
||||||
|
memcpy(buf, _write_cache + addr % _bd_program_size, chunk);
|
||||||
|
read_from_bd = false;
|
||||||
|
} else {
|
||||||
|
chunk = size;
|
||||||
}
|
}
|
||||||
|
|
||||||
while (size) {
|
// Now, in case we read from the BD, make sure we are aligned with its read size.
|
||||||
_curr_aligned_addr = align_down(addr, _bd_program_size);
|
// If not, use read buffer as a helper.
|
||||||
if (moved_unit) {
|
if (read_from_bd) {
|
||||||
int ret = _bd->read(_cache, _curr_aligned_addr, _bd_program_size);
|
bd_size_t offs_in_read_buf = addr % _bd_read_size;
|
||||||
|
int ret;
|
||||||
|
if (offs_in_read_buf || (chunk < _bd_read_size)) {
|
||||||
|
chunk = std::min(chunk, _bd_read_size - offs_in_read_buf);
|
||||||
|
ret = _bd->read(_read_buf, addr - offs_in_read_buf, _bd_read_size);
|
||||||
|
memcpy(buf, _read_buf + offs_in_read_buf, chunk);
|
||||||
|
} else {
|
||||||
|
chunk = align_down(chunk, _bd_read_size);
|
||||||
|
ret = _bd->read(buf, addr, chunk);
|
||||||
|
}
|
||||||
if (ret) {
|
if (ret) {
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
bd_addr_t offs_in_buf = addr - _curr_aligned_addr;
|
|
||||||
bd_size_t chunk = std::min(_bd_program_size - offs_in_buf, size);
|
|
||||||
memcpy(buf, _cache + offs_in_buf, chunk);
|
|
||||||
moved_unit = true;
|
|
||||||
buf += chunk;
|
buf += chunk;
|
||||||
addr += chunk;
|
addr += chunk;
|
||||||
size -= chunk;
|
size -= chunk;
|
||||||
|
@ -154,58 +186,68 @@ int BufferedBlockDevice::program(const void *b, bd_addr_t addr, bd_size_t size)
|
||||||
return BD_ERROR_DEVICE_ERROR;
|
return BD_ERROR_DEVICE_ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
MBED_ASSERT(_write_cache);
|
||||||
|
|
||||||
int ret;
|
int ret;
|
||||||
bool moved_unit = false;
|
|
||||||
|
|
||||||
bd_addr_t aligned_addr = align_down(addr, _bd_program_size);
|
bd_addr_t aligned_addr = align_down(addr, _bd_program_size);
|
||||||
|
|
||||||
const uint8_t *buf = static_cast <const uint8_t *>(b);
|
const uint8_t *buf = static_cast <const uint8_t *>(b);
|
||||||
|
|
||||||
// Need to flush if moved to another program unit
|
// Need to flush if moved to another program unit
|
||||||
if (aligned_addr != _curr_aligned_addr) {
|
if (aligned_addr != _write_cache_addr) {
|
||||||
flush();
|
ret = flush();
|
||||||
_curr_aligned_addr = aligned_addr;
|
if (ret) {
|
||||||
moved_unit = true;
|
return ret;
|
||||||
|
}
|
||||||
|
_write_cache_addr = aligned_addr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Write logic: Keep data in cache as long as we don't reach the end of the program unit.
|
||||||
|
// Otherwise, program to the underlying BD.
|
||||||
while (size) {
|
while (size) {
|
||||||
_curr_aligned_addr = align_down(addr, _bd_program_size);
|
_write_cache_addr = align_down(addr, _bd_program_size);
|
||||||
bd_addr_t offs_in_buf = addr - _curr_aligned_addr;
|
bd_addr_t offs_in_buf = addr - _write_cache_addr;
|
||||||
bd_size_t chunk = std::min(_bd_program_size - offs_in_buf, size);
|
bd_size_t chunk;
|
||||||
|
if (offs_in_buf) {
|
||||||
|
chunk = std::min(_bd_program_size - offs_in_buf, size);
|
||||||
|
} else if (size >= _bd_program_size) {
|
||||||
|
chunk = align_down(size, _bd_program_size);
|
||||||
|
} else {
|
||||||
|
chunk = size;
|
||||||
|
}
|
||||||
|
|
||||||
const uint8_t *prog_buf;
|
const uint8_t *prog_buf;
|
||||||
if (chunk < _bd_program_size) {
|
if (chunk < _bd_program_size) {
|
||||||
// If moved a unit, and program doesn't cover entire unit, it means we don't have the entire
|
// If cache not valid, and program doesn't cover an entire unit, it means we need to
|
||||||
// program unit cached - need to complete it from underlying BD
|
// read it from the underlying BD
|
||||||
if (moved_unit) {
|
if (!_write_cache_valid) {
|
||||||
ret = _bd->read(_cache, _curr_aligned_addr, _bd_program_size);
|
ret = _bd->read(_write_cache, _write_cache_addr, _bd_program_size);
|
||||||
if (ret) {
|
if (ret) {
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
memcpy(_cache + offs_in_buf, buf, chunk);
|
memcpy(_write_cache + offs_in_buf, buf, chunk);
|
||||||
prog_buf = _cache;
|
prog_buf = _write_cache;
|
||||||
} else {
|
} else {
|
||||||
// No need to copy data to our cache on each iteration. Just make sure it's updated
|
|
||||||
// on the last iteration, when size is not greater than program size (can't be smaller, as
|
|
||||||
// this is covered in the previous condition).
|
|
||||||
prog_buf = buf;
|
prog_buf = buf;
|
||||||
if (size == _bd_program_size) {
|
|
||||||
memcpy(_cache, buf, _bd_program_size);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Don't flush on the last iteration, just on all preceding ones.
|
// Only program if we reached the end of a program unit
|
||||||
if (size > chunk) {
|
if (!((offs_in_buf + chunk) % _bd_program_size)) {
|
||||||
ret = _bd->program(prog_buf, _curr_aligned_addr, _bd_program_size);
|
ret = _bd->program(prog_buf, _write_cache_addr, std::max(chunk, _bd_program_size));
|
||||||
if (ret) {
|
if (ret) {
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
_bd->sync();
|
ret = _bd->sync();
|
||||||
|
if (ret) {
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
invalidate_write_cache();
|
||||||
} else {
|
} else {
|
||||||
_flushed = false;
|
_write_cache_valid = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
moved_unit = true;
|
|
||||||
buf += chunk;
|
buf += chunk;
|
||||||
addr += chunk;
|
addr += chunk;
|
||||||
size -= chunk;
|
size -= chunk;
|
||||||
|
@ -221,6 +263,9 @@ int BufferedBlockDevice::erase(bd_addr_t addr, bd_size_t size)
|
||||||
return BD_ERROR_DEVICE_ERROR;
|
return BD_ERROR_DEVICE_ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ((_write_cache_addr >= addr) && (_write_cache_addr <= addr + size)) {
|
||||||
|
invalidate_write_cache();
|
||||||
|
}
|
||||||
return _bd->erase(addr, size);
|
return _bd->erase(addr, size);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -231,9 +276,8 @@ int BufferedBlockDevice::trim(bd_addr_t addr, bd_size_t size)
|
||||||
return BD_ERROR_DEVICE_ERROR;
|
return BD_ERROR_DEVICE_ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((_curr_aligned_addr >= addr) && (_curr_aligned_addr <= addr + size)) {
|
if ((_write_cache_addr >= addr) && (_write_cache_addr <= addr + size)) {
|
||||||
_flushed = true;
|
invalidate_write_cache();
|
||||||
_curr_aligned_addr = _bd->size();
|
|
||||||
}
|
}
|
||||||
return _bd->trim(addr, size);
|
return _bd->trim(addr, size);
|
||||||
}
|
}
|
||||||
|
@ -281,5 +325,5 @@ bd_size_t BufferedBlockDevice::size() const
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
return _bd->size();
|
return _bd_size;
|
||||||
}
|
}
|
||||||
|
|
|
@ -150,9 +150,12 @@ public:
|
||||||
protected:
|
protected:
|
||||||
BlockDevice *_bd;
|
BlockDevice *_bd;
|
||||||
bd_size_t _bd_program_size;
|
bd_size_t _bd_program_size;
|
||||||
bd_size_t _curr_aligned_addr;
|
bd_size_t _bd_read_size;
|
||||||
bool _flushed;
|
bd_size_t _bd_size;
|
||||||
uint8_t *_cache;
|
bd_size_t _write_cache_addr;
|
||||||
|
bool _write_cache_valid;
|
||||||
|
uint8_t *_write_cache;
|
||||||
|
uint8_t *_read_buf;
|
||||||
uint32_t _init_ref_count;
|
uint32_t _init_ref_count;
|
||||||
bool _is_initialized;
|
bool _is_initialized;
|
||||||
|
|
||||||
|
@ -162,6 +165,12 @@ protected:
|
||||||
*/
|
*/
|
||||||
int flush();
|
int flush();
|
||||||
|
|
||||||
|
/** Invalidate write cache
|
||||||
|
*
|
||||||
|
* @return none
|
||||||
|
*/
|
||||||
|
void invalidate_write_cache();
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue