Skip to content

Implement FlashSimBlockDevice - flash simulated block device over RAM #6559

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
May 18, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
142 changes: 142 additions & 0 deletions features/TESTS/filesystem/flashsim_block_device/main.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
/* 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;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you think there is value in making any of these values configurable? If the intention is to test the upper level code using the simulated layer wouldn't making this more configurable makes senses? Otherwise, everything looks ok to me.

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()
{
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);
}
149 changes: 149 additions & 0 deletions features/filesystem/bd/FlashSimBlockDevice.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,149 @@
/* 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 "FlashSimBlockDevice.h"
#include "mbed_assert.h"
#include <algorithm>
#include <stdlib.h>
#include <string.h>

static const bd_size_t min_blank_buf_size = 32;

static inline uint32_t align_up(bd_size_t val, bd_size_t size)
{
return (((val - 1) / size) + 1) * size;
}

FlashSimBlockDevice::FlashSimBlockDevice(BlockDevice *bd, uint8_t erase_value) :
_erase_value(erase_value), _blank_buf_size(0),
_blank_buf(0), _bd(bd)
{
}

FlashSimBlockDevice::~FlashSimBlockDevice()
{
deinit();
delete[] _blank_buf;
}

int FlashSimBlockDevice::init()
{
int ret = _bd->init();
if (ret) {
return ret;
}
_blank_buf_size = align_up(min_blank_buf_size, _bd->get_program_size());
if (!_blank_buf) {
_blank_buf = new uint8_t[_blank_buf_size];
MBED_ASSERT(_blank_buf);

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Non blocking and can be fixed in future - New will assert if memory allocation fails, additional check is not needed. Do you intent to use malloc instead here?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks, will remove the redundant check. And no - will keep using new[].

}
return BD_ERROR_OK;
}

int FlashSimBlockDevice::deinit()
{
return _bd->deinit();
}

int FlashSimBlockDevice::sync()
{
return _bd->sync();
}

bd_size_t FlashSimBlockDevice::get_read_size() const
{
return _bd->get_read_size();
}

bd_size_t FlashSimBlockDevice::get_program_size() const
{
return _bd->get_program_size();
}

bd_size_t FlashSimBlockDevice::get_erase_size() const
{
return _bd->get_erase_size();
}

bd_size_t FlashSimBlockDevice::get_erase_size(bd_addr_t addr) const
{
return _bd->get_erase_size(addr);
}

bd_size_t FlashSimBlockDevice::size() const
{
return _bd->size();
}

int FlashSimBlockDevice::read(void *b, bd_addr_t addr, bd_size_t size)
{
return _bd->read(b, addr, size);
}

int FlashSimBlockDevice::program(const void *b, bd_addr_t addr, bd_size_t size)
{
MBED_ASSERT(is_valid_program(addr, size));
bd_addr_t curr_addr = addr;
bd_size_t curr_size = size;

const uint8_t *buf = (const uint8_t *) b;
while (curr_size) {
bd_size_t read_size = std::min(_blank_buf_size, curr_size);
int ret = _bd->read(_blank_buf, curr_addr, read_size);
if (ret) {
return ret;
}
for (bd_size_t i = 0; i < read_size; i++) {
// Allow either programming on blanks or programming the same value
// (as real flash devices do)
if ((_blank_buf[i] != _erase_value) && (_blank_buf[i] != *buf)) {
return BD_ERROR_NOT_ERASED;
}
buf++;
}
curr_addr += read_size;
curr_size -= read_size;
}

return _bd->program(b, addr, size);
}

int FlashSimBlockDevice::erase(bd_addr_t addr, bd_size_t size)
{
MBED_ASSERT(is_valid_erase(addr, size));

bd_addr_t curr_addr = addr;
bd_size_t curr_size = size;

memset(_blank_buf, _erase_value, (unsigned int) _blank_buf_size);

while (curr_size) {
bd_size_t prog_size = std::min(_blank_buf_size, curr_size);
int ret = _bd->program(_blank_buf, curr_addr, prog_size);
if (ret) {
return ret;
}
curr_addr += prog_size;
curr_size -= prog_size;
}

return BD_ERROR_OK;
}

int FlashSimBlockDevice::get_erase_value() const
{
return _erase_value;
}
Loading