Compare commits

...

2 commits

Author SHA1 Message Date
Stephan I. Böttcher
97d00bc908 one more day … 2026-01-04 22:40:35 +01:00
Stephan I. Böttcher
90aaf8d48e flash_stream complete 2026-01-04 12:04:00 +01:00
12 changed files with 474 additions and 148 deletions

View file

@ -8,7 +8,7 @@ all: $(PROJ).hex
SN_dose = 1
MCU_dose = attiny424
C_FILES_dose = config.c cmd.c pwm.c uart.c base85.c bch4369.c rtc.c spi.c flash.c adc.c
C_FILES_dose = config.c cmd.c pipe.c pwm.c uart.c base85.c bch4369.c rtc.c spi.c flash.c adc.c
S_FILES_dose = uart_tx.S base85a.S
MCU = $(MCU_$(PROJ))

View file

@ -49,9 +49,16 @@ void bch4369(uint8_t d)
}
}
void bch4369_str(uint8_t *b, uint8_t n)
{
do
bch4369(*b++);
while (--n);
}
#else
__attribute__ ((noinline, noclone))
__attribute__ ((noinline, naked))
void bch4369(uint8_t d)
{
__asm__ __volatile__(
@ -65,7 +72,7 @@ void bch4369(uint8_t d)
"adiw r30, 16" "\n\t"
"ldi r26, lo8(bch_genpoly+16)" "\n\t"
"ldi r27, hi8(bch_genpoly+16)" "\n\t"
"rol %[D]" "\n\t" // move next input bit → r25.7
"rol r24" "\n\t" // move next input bit → r25.7
"ror r25" "\n\t"
"2:" "\n\t"
"rol r25" "\n\t" // Move r25.7 → C-bit
@ -81,13 +88,27 @@ void bch4369(uint8_t d)
"brne 2b" "\n\t"
"bst r25, 6" "\n\t" // copy MSB r25.6 → T-bit
"subi r20, 1" "\n\t"
"brne 1b" "\n"
: [D] "+w" (d)
:
: "memory", "cc",
"r0", "r20", "r21", "r25",
"r26", "r27", "r30", "r31"
);
"brne 1b" "\n\t"
"ret" "\n"
);
}
__attribute__ ((noinline, naked))
void bch4369_str(const uint8_t *b, uint8_t n)
{
// _bch4369 preserves r22, r23, r18, r19
__asm__ __volatile__ (
""
"movw r18, r28" "\n\t"
"movw r28, r24" "\n"
"1:" "\n\t"
"ld r24, Y+" "\n\t"
"rcall bch4369" "\n\t"
"subi r22, 1" "\n\t"
"brne 1b" "\n\t"
"mov r28, r18" "\n\t"
"ret" "\n"
);
}
#endif

View file

@ -4,3 +4,4 @@
extern uint8_t bch_parity[16];
void bch4369(uint8_t d);
static inline void bch4369_init() { memset(bch_parity, 0 , 16); }
void bch4369_str(const uint8_t *b, uint8_t n);

226
src/cmd.c
View file

@ -8,85 +8,14 @@
#include "bch4369.h"
#include "base85.h"
#include "uart.h"
#include "pipe.h"
void base85_send_buffer(const uint8_t *buf);
const uint8_t *base85_fill_buffer(const uint8_t *s);
const uint8_t *skip_space(const uint8_t *s);
uint8_t cmd_buffer[16];
uint8_t uart_stream_source;
void parse_command(const uint8_t *s, uint8_t n)
{
uint8_t cmd = *s++;
if (*s =='|')
return;
s = skip_space(s);
uint8_t flag = *s++;
uint8_t r = 0;
send_char(cmd);
send_char('|');
if (cmd == 'A') {
r = adc_current;
if (r >= N_ADC) {
if (flag == '>') {
base85_send_buffer((void*)adc_readings);
flag = *s++;
}
if (flag == '+')
start_adc();
}
}
if (cmd == 'W') {
s = base85_fill_buffer(s);
r = base85_error;
}
else if (cmd == 'B') {
if (flag == '<') {
s = base85_fill_buffer(s);
flag = *s - '0';
if (flag < 4) {
s++;
if (!(uart_stream_source & 0xe0)) {
memcpy(flash_buffer+16*flag, cmd_buffer, 16);
uart_stream_source = flag;
}
r = uart_stream_source;
}
flag = *s++;
}
r = base85_error;
if (!r && flag == '%') {
if (*s=='>')
base85_send_buffer(bch_parity);
else if (*s=='<')
bch4369_feed_buffer(*++s);
r = bch4369_feed_n;
}
else if (flag == '>') {
uint8_t *b = cmd_buffer;
flag = *s - '0';
if (flag < 4)
b = flash_buffer + 16*flag;
base85_send_buffer(b);
}
}
else if (cmd=='F') {
base85_fill_buffer(s);
r = base85_error;
if (!r)
r = flash_submit_command(cmd_buffer);
}
else
send_char('?');
send_hex_byte_eol(r);
}
const uint8_t *skip_space(const uint8_t *s) {
while (*s == ' ')
s++;
return s;
}
uint8_t cmd_buffer_valid;
static inline
const uint8_t *base85_fill_buffer(const uint8_t *s) {
base85_error = 0;
s = skip_space(s);
@ -96,6 +25,151 @@ const uint8_t *base85_fill_buffer(const uint8_t *s) {
return s;
}
uint8_t * poke_addr;
uint8_t peek_size;
struct peak_poke {
uint8_t *addr;
uint8_t size;
uint8_t ccp;
uint8_t data[12];
};
static inline
uint8_t poke(struct peak_poke *p, uint8_t hp, uint8_t poke)
{
if (hp) {
poke_addr = p->addr;
peek_size = p->size;
}
else {
p->addr = poke_addr;
p->size = peek_size;
}
uint8_t s = peek_size;
uint8_t *a = poke_addr;
if (s > 12)
s = 12;
peek_size -= s;
poke_addr += s;
if (poke) {
if (p->ccp)
NVMCTRL.CTRLA = NVMCTRL_CMD_PAGEBUFCLR_gc;
memcpy(a, p->data, s);
if (p->ccp && !(0x8000 & (uint16_t)a))
__asm__(
"out %[ccp], %[key] \n\t"
"sts %[ctrla], %[cmd] \n"
:: [ccp] "n" (&CCP),
[ctrla] "n" (&NVMCTRL.CTRLA),
[key] "r" (p->ccp),
[cmd] "r" (NVMCTRL_CMD_PAGEERASEWRITE_gc)
: "memory", "r0"
);
}
else
memcpy(p->data, a, s);
return s && !poke;
}
void parse_command(const uint8_t *s, uint8_t n)
{
uint8_t cmd = *s++;
if (*s =='`')
return;
uint8_t r = 0x08|cmd;
send_char('`');
send_char(cmd);
uint8_t have_b = !(cmd & 0x20);
cmd |= 0x20;
if (cmd > 'z')
goto error;
if (have_b) {
s = base85_fill_buffer(s);
if (base85_error) {
r = base85_error;
goto error;
}
cmd_buffer_valid |= 0x80;
}
s = skip_space(s);
uint8_t flag = *s++;
switch(cmd) {
case 'a':
r = adc_current;
if (r >= N_ADC) {
if (flag=='=') {
memcpy(cmd_buffer, adc_readings, 16);
cmd_buffer_valid |= 0x40;
flag = *s++;
}
if (flag == '<') {
base85_send_buffer((void*)adc_readings);
flag = *s++;
}
if (flag == '+')
start_adc();
}
break;
case 'b':
r = cmd_buffer_valid;
if (have_b) {
flag -= '0';
if (flag < 5 && flag==cmd_buffer_valid) {
memcpy(flash_buffer+16*flag, cmd_buffer, 16);
r = cmd_buffer_valid |= 1<<flag;
flag = *++s;
}
}
if (flag == '%') {
if (*s=='<')
base85_send_buffer(bch_parity);
if (have_b)
bch4369_feed_buffer(*++s);
r = bch4369_feed_n;
}
else if (flag == '<') {
flag = *s - '0';
uint8_t *b;
if (flag < 5)
b = flash_buffer + 16*flag;
else {
b = cmd_buffer;
}
base85_send_buffer(b);
}
break;
case 'f':
if (have_b) {
r = flash_submit_command(cmd_buffer);
cmd_buffer_valid &=~ 0x80;
}
else
r = flash_poll(flag=='!');
break;
case 'p':
if (have_b) {
r = pipe_config(cmd_buffer, flag);
cmd_buffer_valid &= ~0x80;
}
else
r = pipe_poll(flag=='!');
break;
case 'm':
if (poke((void*)cmd_buffer, have_b, have_b && flag=='!'))
base85_send_buffer(cmd_buffer);
default:
error:
send_char('?');
}
send_hex_byte_eol(r);
}
const uint8_t *skip_space(const uint8_t *s) {
while (*s == ' ')
s++;
return s;
}
void base85_send_buffer(const uint8_t *buf)
{
uint8_t s[6];
@ -113,7 +187,7 @@ void bch4369_feed_buffer(uint8_t flag)
bch4369_feed_n = 0;
memset(bch_parity, 0, 16);
}
for (int i=0; i<16; i++)
bch4369(cmd_buffer[i]);
bch4369_str(cmd_buffer,16);
bch4369_feed_n++;
}

View file

@ -1,5 +1,30 @@
#include "config.h"
#include "flash.h"
////////////////////////////////////////////////////////////////////////////////
//
// Configuration in USERROW
__attribute__((section(".userrow")))
const struct config config = {
.magic = USE_USERROW,
.version = USE_VERSION,
.cpu_clk = CLKCTRL_PDIV_2X_gc | 1, // 10MHz (max @ 3V)
.flash_page_size = FM_528 >> 8,
.burn_page = 0x88, // Buffer 1 Page Program w/o Erase
.write_buffer = 0x84 | FM_WRITE>>8, // Buffer 1 Write
.read_array = 0x03 | FM_READ>>8, // Continuous Array Read (Low-Frequency)
.read_buffer = {
[0] = 0xd1 | FM_READ>>8, // Buffer 1 Read (Low-Frequency)
[1] = 0xd3 | FM_READ>>8, // Buffer 2 Read (Low-Frequency)
},
};
////////////////////////////////////////////////////////////////////////////////
//
// Configuration in EEPROM
struct_ioconf(port_config) = {
conf_prefix(PORTA),

View file

@ -4,6 +4,29 @@
#include <avr/io.h>
#include <stdint.h>
// USERROW
struct config {
uint8_t magic;
uint8_t version;
uint8_t cpu_clk;
uint8_t flash_page_size;
uint16_t burn_page;
uint16_t erase_page;
uint16_t write_buffer;
uint16_t read_array;
uint16_t read_buffer[2];
};
enum magic_flags {
USE_USERROW = 0xD0,
USE_VERSION = 0x01,
};
extern const struct config config;
// EEPROM
struct io_config {
uint8_t addr;
uint8_t val;

View file

@ -21,18 +21,6 @@
#include "pwm.h"
#include "flash.h"
////////////////////////////////////////////////////////////////////////////////
//
// Configuration in USERROW
__attribute__((section(".userrow")))
const struct config config = {
.magic = USE_USERROW,
.version = USE_VERSION,
.cpu_clk = CLKCTRL_PDIV_2X_gc | 1, // 10MHz (max @ 3V)
.flash_page_size = FM_528,
};
////////////////////////////////////////////////////////////////////////////////
//
// main()

View file

@ -9,17 +9,3 @@
#include <stdint.h>
#include <avr/io.h>
struct config {
uint8_t magic;
uint8_t version;
uint8_t cpu_clk;
uint8_t flash_page_size;
};
enum magic_flags {
USE_USERROW = 0xD0,
USE_VERSION = 0x01,
};
extern const struct config config;

View file

@ -82,8 +82,10 @@ miscellanious
*/
#include "config.h"
#include "flash.h"
#include "cmd.h"
#include "bch4369.h"
uint8_t flash_cmd_buffer[4];
uint8_t flash_status_bytes[2];
@ -98,7 +100,6 @@ uint8_t flash_cmd(uint16_t mode, uint16_t what, uint16_t page, uint16_t byte)
{
uint8_t spi_mode = SPI_FLASH | mode & FM_SPI;
uint8_t op = mode;
mode >>= 8;
uint8_t size = what;
what >>= 8;
uint8_t s = spi_select(spi_mode);
@ -124,11 +125,16 @@ uint8_t flash_cmd(uint16_t mode, uint16_t what, uint16_t page, uint16_t byte)
case FM_PAD2: pads += 1;
case FM_PAD1: pads += 1;
}
if (size >= 128) {
// for read of the security register
pads += size-64;
size = 64;
}
spi.zsize = pads;
if (what + size <= 64)
if (what + size <= 80)
b = flash_buffer + what;
else if (what+size <= 80)
b = cmd_buffer + (what-64);
else if (what>=96 && what+size <= 112)
b = cmd_buffer + (what-96);
else if (size <= 2)
b = flash_status_bytes;
else
@ -148,58 +154,6 @@ uint8_t flash_cmd(uint16_t mode, uint16_t what, uint16_t page, uint16_t byte)
return 0;
}
uint16_t flash_page, flash_block, n_blocks;
uint8_t flash_stream_status;
enum {
FS_IDLE = 0,
FS_Rdy = 1,
FS_Bsy = 2,
FS_Read = 4,
FS_Write = 8,
FS_Erase = 12,
FS_Buff = 32,
FS_StBsy = 64,
FS_TxBCH = 128,
};
uint8_t flash_poll()
{
uint8_t r = spi_busy_p();
if (r)
// SPI is shifting something
return 0x80 | r;
r = flash_stream_status;
if (r & FS_StBsy) {
// status bytes arrived
if (flash_status_bytes[0] & 0x80)
// flash is still busy burning
goto rd_status;
// not busy any more, move Bsy → Rdy
if (r & FS_Bsy)
r |= FS_Rdy;
goto rdy;
}
if (!(r & FS_Bsy))
goto rdy;
if (r & FS_Write) {
rd_status:
// request status bytes for pending Tx od Er
r |= FS_StBsy;
flash_stream_status = r;
flash_cmd_na(0xd7, 0xff02);
return r & FS_Bsy;
}
// Rx is Rdy when SPI is idle
rdy:
// clear the Bsy bits
r &= ~(FS_Bsy | FS_StBsy);
// return the ready bits
flash_stream_status = r;
return r;
}
struct flash_cmd {
uint16_t mode, what, page, byte;
uint8_t buffer[8];
@ -210,3 +164,189 @@ uint8_t flash_submit_command(uint8_t *cmd)
struct flash_cmd *c = (void*)cmd;
return flash_cmd(c->mode, c->what, c->page, c->byte);
}
struct flash_stream fs;
__attribute__ ((noinline, noclone))
uint8_t flash_stream_submit(uint16_t mode, uint8_t size)
{
uint8_t b = fs.block;
uint16_t p = fs.page;
uint8_t r = fs.status & ~FS_Ready;
mode |= (uint16_t)config.flash_page_size << 8;
if (size) {
if (b & 8) {
b = 0;
fs.page = ++p;
fs.npages--;
}
if (b==7 && r & FS_BCH) {
size |= 16;
memcpy(flash_buffer+64, bch_parity, 16);
}
}
else if ((r & FS_Dir) == FS_Erase) {
fs.page += (uint16_t) b + 1;
fs.npages -= (uint16_t) b + 1;
b = 8;
}
fs.block = b+1;
uint8_t e = flash_cmd(mode, size, p, (uint16_t)(b&7) << 6);
if (e)
r |= FS_Ready; // FS_Error
r |= FS_Busy;
fs.status = r;
return e;
}
__attribute__ ((noinline, noclone))
uint8_t flash_stream_done()
{
uint8_t r = fs.status & FS_Error;
if (!r || r == FS_Error)
return 1;
if (fs.npages || !(fs.block & 8))
return 0;
return 1;
}
static inline
uint8_t flash_write_next_block()
{
return flash_stream_submit(config.write_buffer, 64);
}
static inline
uint8_t flash_read_next_block()
{
uint16_t mode;
if (fs.page & 0x1000)
mode = config.read_buffer[fs.page&1];
else
mode = config.read_array;
return flash_stream_submit(mode, 64);
}
static inline
uint8_t flash_erase_next_page()
{
uint16_t mode = 0x81; // Page Erase
uint8_t n = 0;
if (fs.page && !(fs.page & 0xff) && fs.npages & 0xff00) {
mode = 0x7c; // Sector 1…15 Erase
n = 0xff;
}
else if (!(fs.page & 7) && fs.npages >= 8) {
mode = 0x50; // Block Erase
n = 7;
}
fs.block = n;
return flash_stream_submit(mode, 0);
}
static inline
uint8_t flash_burn_page()
{
return flash_stream_submit(config.burn_page, 0);
}
static inline
void bch_flash_buffer(uint8_t n)
{
bch4369_str(flash_buffer, n);
}
void bch_flash_init()
{
bch4369_init();
}
uint8_t flash_poll(uint8_t rr)
{
uint8_t r = fs.status;
if (spi_busy_p()) {
if (rr)
goto collision;
fs.status = r | FS_Busy;
return r;
}
if ((r & FS_Error) == FS_Error)
return r;
if (r & FS_StBsy) {
// status bytes arrived
if (flash_status_bytes[0] & 0x80)
// flash is still busy burning
goto rd_status;
// not busy any more, move Bsy → Rdy
if (r & FS_Busy)
r |= FS_Ready;
goto ready;
}
if (!(r & FS_Busy))
goto ready;
if (rr) {
collision:
r |= FS_Error;
fs.status = r;
return r;
}
if (r & FS_Write) {
if (fs.block == 9) {
rd_status:
// request status bytes for pending Write or Error
r |= FS_StBsy;
fs.status = r;
flash_cmd_na(0xd7, 0xff02);
return r;
}
}
else if (r & FS_BCH) {
// checksum the received buffer
if (fs.block == 1)
bch_flash_init();
if (fs.block & 8)
bch_flash_buffer(80);
else
bch_flash_buffer(64);
}
ready:
if (rr)
r |= FS_Ack;
// clear the Bsy bits
r &= ~(FS_Busy | FS_StBsy);
fs.status = r;
if (r & FS_Dir == FS_Write && fs.block == 8)
flash_burn_page();
else if (!flash_stream_done()) {
if (r & (FS_Dir|FS_Ack) == (FS_Read|FS_Ack))
flash_read_next_block();
else if (r & (FS_Dir|FS_Ack) == (FS_Write|FS_Ack)) {
if (r & FS_BCH) {
if (!(fs.block & 7))
bch_flash_init();
bch_flash_buffer(64);
}
flash_write_next_block();
}
else if (r & FS_Dir == FS_Erase)
flash_erase_next_page();
}
return fs.status;
}
uint8_t flash_start_stream(uint16_t page, uint16_t npages, uint8_t flags)
{
uint8_t r = flash_poll(0);
if ((r & FS_Error) == FS_Busy)
return FS_Error;
r = flags | FS_Ready;
if (config.flash_page_size != FM_528)
r &=~ FS_BCH;
fs.page = page;
fs.block = 0;
fs.npages = npages;
fs.status = r;
flash_status_bytes[0] = 0xff;
return flash_poll(0);
}

View file

@ -2,23 +2,49 @@
#include "spi.h"
enum flash_mode_bits {
FM_PAD1 = 0x01,
FM_PAD2 = 0x02,
FM_PAD4 = 0x03,
FM_PAD = 0x03,
FM_512 = 0x04,
FM_528 = 0x08,
FM_SEC = 0x0c,
FM_ADDR = 0x0c,
FM_WRITE = 0x10,
FM_READ = 0x20,
FM_WAIT = 0x30,
FM_START = 0x30,
FM_CONT = SPI_CONT,
FM_PAD1 = 0x0100,
FM_PAD2 = 0x0200,
FM_PAD4 = 0x0300,
FM_PAD = 0x0300,
FM_512 = 0x0400,
FM_528 = 0x0800,
FM_SEC = 0x0c00,
FM_ADDR = 0x0c00,
FM_WRITE = 0x1000,
FM_READ = 0x2000,
FM_WAIT = 0x3000,
FM_START = 0x3000,
FM_CONT = (uint16_t)SPI_CONT << 8,
FM_SPI = FM_CONT,
FM_NSTR = 0x8000,
};
uint8_t flash_cmd_na(uint16_t mode, uint16_t what);
uint8_t flash_cmd(uint16_t mode, uint16_t what, uint16_t page, uint16_t byte);
#define FB_SIZE 64
#define FB_SIZE 80
extern uint8_t flash_buffer[FB_SIZE];
uint8_t flash_submit_command(uint8_t *cmd);
uint8_t flash_start_stream(uint16_t page, uint16_t npages, uint8_t flags);
uint8_t flash_poll(uint8_t rr);
extern
struct flash_stream {
uint16_t page; // page address of buffer number
uint16_t npages; // more pages to read
uint8_t block; // next block to read 0…9
uint8_t status; // FS_… flags
} fs;
enum {
FS_Ready = 1, // ready for the next buffer
FS_Busy = 2, // processing …
FS_Error = 3, // Aborted
FS_Read = 4, // array read
FS_Write = 8, // buffer write and burn
FS_Erase = 12, // Erase
FS_Dir = 12, // Mask for the last three
FS_Ack = 32, // Next buffer is provided, continue …
FS_StBsy = 64, // Waiting to Flash status register
FS_BCH = 128, // do 528 byte pages.
};

22
src/pipe.c Normal file
View file

@ -0,0 +1,22 @@
#include <pipe.h>
struct pipe pipe;
struct pipe_config {
uint8_t source;
uint8_t dest;
uint16_t npages;
uint8_t flash_flags;
uint8_t flash_page;
};
uint8_t pipe_config(const uint8_t *, uint8_t flag)
{
return 0;
}
uint8_t pipe_poll(uint8_t flag)
{
return 0;
}

20
src/pipe.h Normal file
View file

@ -0,0 +1,20 @@
#include <stdint.h>
uint8_t pipe_config(const uint8_t *, uint8_t flag);
uint8_t pipe_poll(uint8_t flag);
enum pipe_ports {
pipe_cmd = 1,
pipe_hk = 2,
pipe_flash = 4,
pipe_fpgs = 8,
};
extern
struct pipe {
uint8_t source;
uint8_t dest;
uint8_t status;
uint16_t size;
} pipe;