Skip to content

Commit

Permalink
Added optional ROM-backed p config option
Browse files Browse the repository at this point in the history
I thought this would be more effort than it was worth. But after putting
together rs-poly.py realized the only remaining bit of work was a config
option and a bit of documentation.

Added test_rom to test that this works as intended.

---

One downside is we still pay the code cost for generating the generator
polynomial, even if provided statically, but this is just an example.

We're limited a bit by sticking to littlefs's config patterns, which
need some significant improvements...
  • Loading branch information
geky committed Oct 29, 2024
1 parent a9098b4 commit 2ef6a78
Show file tree
Hide file tree
Showing 5 changed files with 116 additions and 86 deletions.
14 changes: 10 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -1326,15 +1326,15 @@ noting:

2. Storing the generator polynomial in ROM.

This isn't actually implemented in ramrsbd (it's a bit annoying
API-wise), but you don't really need to recompute the generator
polynomial every time you initialize the block device.
We don't really need to recompute the generator polynomial every time
we initialize the block device, it's just convenient API-wise when
`ecc_size` is unknown.

If you only need to support a fixed set of block device geometries,
precomputing and storing the generator polynomial in ROM will save a
couple (`ecc_size`) bytes of RAM.

[rs-poly.py][rs-poly.py] can help with this:
[rs-poly.py][rs-poly.py] can help generate the generator polynomial:

``` bash
$ ./rs-poly.py 8
Expand All @@ -1347,6 +1347,12 @@ noting:
};
```

Which can be provided to ramrsbd's [p][p] config option to avoid
allocating the `p_buffer` in RAM.

Unfortunately we still pay the code cost for generating the generator
polynomial, which is difficult to avoid with the current API.

3. Minimizing the number of polynomial buffers.

Reed-Solomon has quite a few polynomials flying around:
Expand Down
48 changes: 26 additions & 22 deletions ramrsbd.c
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,9 @@ int ramrsbd_create(const struct lfs_config *cfg,
}

// allocate generator polynomial buffer?
if (bd->cfg->p_buffer) {
if (bd->cfg->p) {
bd->p = (uint8_t*)bd->cfg->p;
} else if (bd->cfg->p_buffer) {
bd->p = (uint8_t*)bd->cfg->p_buffer;
} else {
bd->p = lfs_malloc(bd->cfg->ecc_size);
Expand Down Expand Up @@ -110,29 +112,31 @@ int ramrsbd_create(const struct lfs_config *cfg,
}
}

// calculate generator polynomial
//
// P(x) = prod_i^n-1 (x - g^i)
//
// the important property of P(x) is that it evaluates to 0
// at every x=g^i for i < n
//
// normally P(x) needs n+1 terms, but the leading term is always 1,
// so we can make it implicit
//
if (!bd->cfg->p) {
// calculate generator polynomial
//
// P(x) = prod_i^n-1 (x - g^i)
//
// the important property of P(x) is that it evaluates to 0
// at every x=g^i for i < n
//
// normally P(x) needs n+1 terms, but the leading term is
// always 1, so we can make it implicit
//

// let P(x) = 1
memset(bd->p, 0, bd->cfg->ecc_size);
bd->p[bd->cfg->ecc_size-1] = 1;
// let P(x) = 1
memset(bd->p, 0, bd->cfg->ecc_size);
bd->p[bd->cfg->ecc_size-1] = 1;

for (lfs_size_t i = 0; i < bd->cfg->ecc_size; i++) {
// let R(x) = x - g^i
uint8_t r[2] = {1, ramrsbd_gf_pow(RAMRSBD_GF_G, i)};
for (lfs_size_t i = 0; i < bd->cfg->ecc_size; i++) {
// let R(x) = x - g^i
uint8_t r[2] = {1, ramrsbd_gf_pow(RAMRSBD_GF_G, i)};

// let P(x) = P(x) * R(x)
ramrsbd_gf_p_mul(
bd->p, bd->cfg->ecc_size,
r, 2);
// let P(x) = P(x) * R(x)
ramrsbd_gf_p_mul(
bd->p, bd->cfg->ecc_size,
r, 2);
}
}

RAMRSBD_TRACE("ramrsbd_create -> %d", 0);
Expand All @@ -149,7 +153,7 @@ int ramrsbd_destroy(const struct lfs_config *cfg) {
if (!bd->cfg->code_buffer) {
lfs_free(bd->c);
}
if (!bd->cfg->p_buffer) {
if (!bd->cfg->p && !bd->cfg->p_buffer) {
lfs_free(bd->p);
}
if (!bd->cfg->s_buffer) {
Expand Down
10 changes: 10 additions & 0 deletions ramrsbd.h
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,14 @@ struct ramrsbd_config {
// -1 disables error correction and errors on any errors.
lfs_ssize_t error_correction;

// Optional precomputed generator polynomial.
//
// See the rs-poly.py script to help generate this.
//
// By default p is computed as needed for the configured ecc_size.
// Must be ecc_size.
const uint8_t *p;

// Optional statically allocated buffer for the block device.
void *buffer;

Expand All @@ -65,6 +73,8 @@ struct ramrsbd_config {

// Optional statically allocated generator polynomial buffer.
//
// Not needed if a precomputed p is provided.
//
// Must be ecc_size.
void *p_buffer;

Expand Down
60 changes: 0 additions & 60 deletions tests/test_correction.toml
Original file line number Diff line number Diff line change
Expand Up @@ -328,63 +328,3 @@ code = '''
ramrsbd_destroy(&cfg_) => 0;
'''



## test random 3 bit errors
#[cases.test_correction_3_bits_prng]
#defines.SEED = 'range(10)'
#defines.N = 1000
## this only works up to ~16 byte code words
#if = 'READ_SIZE <= 16'
#code = '''
# ramrsbd_t ramrsbd;
# struct lfs_config cfg_ = *cfg;
# cfg_.context = &ramrsbd;
# cfg_.read = ramrsbd_read;
# cfg_.prog = ramrsbd_prog;
# cfg_.erase = ramrsbd_erase;
# cfg_.sync = ramrsbd_sync;
# struct ramrsbd_config ramrsbdcfg = {
# .code_size = CODE_SIZE,
# .ecc_size = ECC_SIZE,
# .erase_size = ERASE_SIZE,
# .erase_count = ERASE_COUNT,
# };
# ramrsbd_create(&cfg_, &ramrsbdcfg) => 0;
#
# uint8_t buffer[READ_SIZE];
#
# // write data
# cfg_.erase(&cfg_, 0) => 0;
# for (lfs_off_t i = 0; i < READ_SIZE; i++) {
# buffer[i] = 'a' + (i % 26);
# }
# cfg_.prog(&cfg_, 0, 0, buffer, READ_SIZE) => 0;
#
# // try flipping random sets of bits
# uint32_t prng = SEED;
# for (lfs_size_t i = 0; i < N; i++) {
# lfs_size_t bit0 = TEST_PRNG(&prng) % (8*CODE_SIZE);
# lfs_size_t bit1 = TEST_PRNG(&prng) % (8*CODE_SIZE);
# lfs_size_t bit2 = TEST_PRNG(&prng) % (8*CODE_SIZE);
# ramrsbd.buffer[bit0/8] ^= 1 << (bit0%8);
# ramrsbd.buffer[bit1/8] ^= 1 << (bit1%8);
# ramrsbd.buffer[bit2/8] ^= 1 << (bit2%8);
#
# // read data
# cfg_.read(&cfg_, 0, 0, buffer, READ_SIZE) => 0;
#
# // error correction should repair the bit
# for (lfs_off_t i = 0; i < READ_SIZE; i++) {
# LFS_ASSERT(buffer[i] == 'a' + (i % 26));
# }
#
# // undo the bit flips
# ramrsbd.buffer[bit0/8] ^= 1 << (bit0%8);
# ramrsbd.buffer[bit1/8] ^= 1 << (bit1%8);
# ramrsbd.buffer[bit2/8] ^= 1 << (bit2%8);
# }
#
# ramrsbd_destroy(&cfg_) => 0;
#'''
#
70 changes: 70 additions & 0 deletions tests/test_rom.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
# Test moving config things into ROM
#

code = '''
#include "ramrsbd.h"
'''

defines.CODE_SIZE = [16, 64, 128]
defines.ECC_SIZE = 32
defines.ERASE_SIZE = 4096
if = 'ECC_SIZE < CODE_SIZE'

defines.READ_SIZE = 'CODE_SIZE - ECC_SIZE'
defines.PROG_SIZE = 'CODE_SIZE - ECC_SIZE'
defines.BLOCK_SIZE = 'ERASE_SIZE - ((ERASE_SIZE/CODE_SIZE)*ECC_SIZE)'

# test storing the generator polynomial in ROM
[cases.test_rom_p]
code = '''
// generated by: ./rs-poly.py 32
const uint8_t P[32] = {
0x74, 0x40, 0x34, 0xae, 0x36, 0x7e, 0x10, 0xc2,
0xa2, 0x21, 0x21, 0x9d, 0xb0, 0xc5, 0xe1, 0x0c,
0x3b, 0x37, 0xfd, 0xe4, 0x94, 0x2f, 0xb3, 0xb9,
0x18, 0x8a, 0xfd, 0x14, 0x8e, 0x37, 0xac, 0x58,
};
ramrsbd_t ramrsbd;
struct lfs_config cfg_ = *cfg;
cfg_.context = &ramrsbd;
cfg_.read = ramrsbd_read;
cfg_.prog = ramrsbd_prog;
cfg_.erase = ramrsbd_erase;
cfg_.sync = ramrsbd_sync;
struct ramrsbd_config ramrsbdcfg = {
.code_size = CODE_SIZE,
.ecc_size = ECC_SIZE,
.erase_size = ERASE_SIZE,
.erase_count = ERASE_COUNT,
.p = P,
};
ramrsbd_create(&cfg_, &ramrsbdcfg) => 0;
uint8_t buffer[READ_SIZE];
// write data
cfg_.erase(&cfg_, 0) => 0;
for (lfs_off_t i = 0; i < READ_SIZE; i++) {
buffer[i] = 'a' + (i % 26);
}
cfg_.prog(&cfg_, 0, 0, buffer, READ_SIZE) => 0;
// try flipping each bit
for (lfs_off_t i = 0; i < 8*CODE_SIZE; i++) {
ramrsbd.buffer[i/8] ^= 1 << (i%8);
// read data
cfg_.read(&cfg_, 0, 0, buffer, READ_SIZE) => 0;
// error correction should repair the bit
for (lfs_off_t i = 0; i < READ_SIZE; i++) {
LFS_ASSERT(buffer[i] == 'a' + (i % 26));
}
// undo the bit flip
ramrsbd.buffer[i/8] ^= 1 << (i%8);
}
ramrsbd_destroy(&cfg_) => 0;
'''

0 comments on commit 2ef6a78

Please sign in to comment.