Compare commits

..

12 commits

Author SHA1 Message Date
Stephan I. Böttcher
93e1c3230e main: race free sleep() 2026-03-29 16:02:54 +02:00
Stephan I. Böttcher
66525141bd cmd: "P" Pipe and "O" fpga confif 2026-03-29 16:01:40 +02:00
Stephan I. Böttcher
36ffd2c4f6 pipe: major development, fixes 2026-03-29 16:00:03 +02:00
Stephan I. Böttcher
6a2f5dc026 fpga_pipe_ready() fix return when not ready 2026-03-29 15:46:53 +02:00
Stephan I. Böttcher
6b668578ea fpga_start_read() requires configured FPGA 2026-03-29 15:43:10 +02:00
Stephan I. Böttcher
f18e923c96 .gitignore *.s 2026-03-29 10:59:39 +02:00
Stephan I. Böttcher
e63764042f FPGA: status and pipe support 2026-03-29 10:58:38 +02:00
Stephan I. Böttcher
51d3e09dd3 uart: new inline command_pending() 2026-03-29 10:55:06 +02:00
Stephan I. Böttcher
890aabf955 new inline flash_current_block() 2026-03-29 10:53:32 +02:00
Stephan I. Böttcher
2915785571 TODO comment: speed up spi writes
FPGA config takes 5µs per Byte.  The ISR could be faster (and shorter)
when the `wdata` is moved to `cmd` after czise and zsize are zero.
2026-03-29 10:48:44 +02:00
Stephan I. Böttcher
b3bb5396de Makefile: link order
The AVR HK packets are basically dumps of the named .bss segments.
The link order defines the layout of those packets.  Packet sizes are
16, 32 or 64 bytes.  Important things go first, larger packets contain
extra things.

A linker script could define the link order, but we cannot control that
easily.
2026-03-29 10:41:55 +02:00
Stephan I. Böttcher
367479409b Linker script: fix section overlap
Define MEMORY for .eemap and .uumap not overlapping .text.

With more than 4kBytes of flash, the .text segment overlaps
.eemap, .uumap at the real addresses.  Add high bits to the
mappings, which are ignored.

The mappings are now ar 0x80xxxx, which is defines for data/RAM,
which must not overlap with anything but .text.
2026-03-29 10:29:44 +02:00
13 changed files with 428 additions and 108 deletions

1
src/.gitignore vendored
View file

@ -1 +1,2 @@
thhor.userrow
*.s

View file

@ -11,22 +11,13 @@ thhor_all: thhor.eeprom thhor.userrow
CFLAGS_thhor = -Ibch4369 -DHAVE_FPGA -DSEND_HEX -DTHHOR
MCU_thhor = attiny3224
SN_thhor = 1
C_FILES_thhor = config.c uart.c cmd.c base85.c rtc.c adc.c pwm.c spi.c flash.c bch4369.c pipe.c fpga.c
# Link order defines ADC packet composition
# main() is first, with .bss.magic
# config.c includes .eeprom `port_config`, that should go second
# .bss: rtc, adc, flash, uart, spi, pipe
C_FILES_thhor = config.c rtc.c adc.c flash.c uart.c cmd.c base85.c pwm.c spi.c bch4369.c pipe.c fpga.c
S_FILES_thhor = uart_tx.S base85a.S
dose_all: dose.eeprom dose.userrow
SN_dose = 1
MCU_dose = $(MCU_$(VAR))
MCU_nFETs = attiny424
MCU_FPGA = attiny3224
VAR=nFETs
C_FILES_nFETs =
C_FILES_FPGA = fpga.c
CFLAGS_dose = -DHAVE_$(VAR)
C_FILES_dose = config.c rtc.c adc.c pwm.c $(C_FILES_$(VAR)) uart.c cmd.c pipe.c base85.c bch4369.c spi.c flash.c
S_FILES_dose = uart_tx.S base85a.S
MCU = $(MCU_$(PROJ))
OPT = -Os
@ -82,23 +73,24 @@ pMCU-attiny824 = t824
pMCU-attiny3224 = t3224
# WDT
fuse0_dose= 0x00
fuse0_thhor= 0x00
# BOD
fuse1_dose= 0x00
fuse1_thhor= 0x00
# OSC, 20 MHz
fuse2_dose= 0x7e
# ???
fuse4_dose= 0xff
fuse2_thhor= 0x7e
# Reserved
fuse3_thhor= 0xff
# Reserved
fuse4_thhor= 0xff
# SYS0 (default 0xf6) RESET, EEPROM erase
fuse5_dose= 0xf7
fuse5_thhor= 0xf7
# SYS1 startup time (64ms)
fuse6_dose= 0xff
fuse6_thhor= 0xff
# APPEND
fuse7_dose= 0x00
fuse7_thhor= 0x00
# BOOTEND
fuse8_dose= 0x00
fuses_dose =$(patsubst %, 0x%, 00 00 7e ff ff f7 ff 00 00)
fuses_thhor = $(fuses_dose)
fuse8_thhor= 0x00
fuses_thhor =$(patsubst %, 0x%, 00 00 7e ff ff f7 ff 00 00)
AVRDUDEPROG = avrdude
AVRDUDE = $(AVRDUDEPROG)
@ -107,8 +99,6 @@ AVRDUDE_PORT = /dev/ttyUSB0
AD = $(AVRDUDE) -p $(pMCU-$(MCU)) -P $(AVRDUDE_PORT) -c $(AVRDUDE_PROGRAMMER)
sig_dose = 0x1e 0x92 0x2c
sig_thhor = 0x1e 0x95 0x28

View file

@ -244,17 +244,38 @@ void parse_command(const uint8_t *s, uint8_t n)
goto send_buffer;
break;
case 'P':
if (cmd_flag('!'))
pipe.dest = 0;
if (have_b)
pipe_config((void*)cmd_buffer);
pipe_config((void*)cmd_buffer, (void*)bptr);
r = pipe_poll();
break;
#ifdef HAVE_FPGA
case 'O':
r = fpga_power();
if (bflg == 1)
if (bflg & 1)
// "O1" power off
fpga_power_off();
else if (bflg == 2)
if (bflg >= 2) {
// "O2" power on
fpga_power_on();
if (!r) {
fpga_reset();
break;
}
}
if (bflg <= 2)
break;
if (bflg <= 8 && r==as_configured)
// "O2": configure if unconfigured
break;
if (bflg & 8) {
// "O3": power of if unconfigured
fpga_power_off();
break;
}
// "O4" configure unconditionally
fpga_config(config.fpga_config_page, config.fpga_config_count);
break;
case 'C':
if (cmd_flag('@')) {

View file

@ -23,6 +23,8 @@ struct config {
uint16_t pwm_min;
uint16_t pwm_max;
#endif
uint16_t fpga_config_page;
uint16_t fpga_config_count;
};
enum magic_flags {
@ -32,7 +34,7 @@ enum magic_flags {
#endif
#ifdef HAVE_FPGA
MAGIC = 0xC5,
VERSION = 0x01,
VERSION = 0x00,
#endif
};
@ -159,6 +161,8 @@ extern struct magic {
#if 0
#define _memcopy memcpy
#define _memcopyyz memcpy
#define _memcopyzy memcpy
#define _memcmpyz memcmp
#else
// avoid the avr-libc memcpy.
// ¡ n must not be zero !
@ -192,6 +196,43 @@ void _memcopyyz(uint8_t *d, uint8_t *s, uint8_t n)
:: "r0", "memory");
}
static inline
void _memcopyzy(uint8_t *d, uint8_t *s, uint8_t n)
{
__asm__ volatile ("\n"
"1:" "\n\t"
"ld r0, Y+" "\n\t"
"st Z+, r0" "\n\t"
"dec %[N]" "\n\t"
"brne 1b" "\n"
: [D] "+z" (d),
[S] "+y" (s),
[N] "+r" (n)
:: "r0", "memory");
}
static inline
uint8_t _memcmpyz(uint8_t *d, uint8_t *s, uint8_t n)
{
uint8_t r;
__asm__ volatile (
"\n"
"1:" "\n\t"
"ld %[R], Y+" "\n\t"
"ld r0, Z+" "\n\t"
"sub %[R], r0" "\n\t"
"breq 1f" "\n\t"
"dec %[N]" "\n\t"
"brne 1b" "\n"
"1:" "\n"
: [D] "+x" (d),
[S] "+y" (s),
[N] "+r" (n),
[R] "=r" (r)
:
: "r0", "memory");
return r;
}
#endif

View file

@ -4,14 +4,14 @@
*/
MEMORY
{
eemap : ORIGIN = 0x1400, LENGTH = 0x100
eedef : ORIGIN = 0x810000, LENGTH = 0x100
uumap : ORIGIN = 0x1300, LENGTH = 0x20
uudef : ORIGIN = 0x850000, LENGTH = 0x20
eemap (rw!x) : ORIGIN = 0x801400, LENGTH = 0x100
eedef (rw!x) : ORIGIN = 0x810000, LENGTH = 0x100
uumap (rw!x) : ORIGIN = 0x801300, LENGTH = 0x020
uudef (rw!x) : ORIGIN = 0x850000, LENGTH = 0x020
}
SECTIONS
{
.eemap 0x1400:
.eemap 0x801400:
{
ee1_start = .;
*(.eeprom1)
@ -22,7 +22,7 @@ SECTIONS
ee9_end = .;
ee9_size = ee9_end - ee9_start;
} >eemap AT >eedef
.uumap 0x1300:
.uumap 0x801300:
{
*(.userrow)
} >uumap AT >uudef

View file

@ -28,6 +28,7 @@ uint8_t flash_cmd(uint16_t mode, uint16_t what, uint16_t page, uint16_t byte);
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_stream_done();
uint8_t flash_poll(uint8_t rr);
uint16_t flash_find_free();
@ -53,4 +54,6 @@ enum {
FS_528 = 128, // do 528 byte pages.
};
static inline uint8_t flash_current_block() { return fs.block; }
#endif

View file

@ -2,6 +2,7 @@
#include "pipe.h"
#include "fpga.h"
#include "spi.h"
#include <string.h>
uint8_t fpga_reset_poll()
{
@ -12,6 +13,18 @@ uint8_t fpga_reset_poll()
return r;
}
uint8_t fpga_status()
{
uint8_t r = fpga_power();
// all three altera pins must be on the same PORT
if (r)
r |= CRCERR_VPORT.IN &
( 1<<nCONFIG_PIN
| 1<<nSTATUS_PIN
| 1<<CRCERR_PIN );
return r;
}
void fpga_reset()
{
nCONFIG_VPORT.OUT &=~ (1<<nCONFIG_PIN);
@ -24,8 +37,8 @@ void fpga_cmd(struct fpga_cmd *c)
{
uint8_t n = c->n;
uint8_t z = c->z;
if (fpga_reset_poll()) {
c->n = n | fpga_busy;
if (fpga_status() != as_configured) {
c->n = n | fpga_dead;
return;
}
if (n & fpga_abort && spi_abort()) {
@ -41,7 +54,7 @@ void fpga_cmd(struct fpga_cmd *c)
return;
c->n = n | fpga_submitted;
spi_select(n & fpga_config ? SPI_CONFIG : 0);
spi_select(n & fpga_configure ? SPI_CONFIG : 0);
spi.csize = n &= fpga_size;
spi.zero = z & 0x80; // send 0x0000 or 0x8080
spi.cmd = c->d;
@ -62,25 +75,106 @@ void fpga_cmd(struct fpga_cmd *c)
spi_start();
}
void fpga_start(uint8_t write)
struct pipe_fpga_cmd pipe_fpga_cmd;
uint8_t fpga_start_write()
{
uint8_t mode = 0;
if (pipe.fpga.zero == SPI_CONFIG)
mode = SPI_CONFIG;
uint8_t mode = pipe.fpga.status & SPI_CONFIG;
spi_select(mode);
_memcopy(&spi.csize, &pipe.fpga.csize, 6);
spi.cmd = pipe.fpga.cmd;
spi.wdata = flash_buffer;
spi.rdata = flash_buffer;
uint8_t n = 64;
if (n > pipe.fpga.size)
n = pipe.fpga.size;
pipe.fpga.size -= n;
if (!n)
return;
if (write)
spi.wsize = n;
else
spi.rsize = n;
spi_start();
uint8_t n = 32;
if (pipe.fpga.count)
pipe.fpga.count--;
else {
uint8_t nn = pipe.fpga.size;
if (nn < n)
n = nn;
pipe.fpga.size -= n;
}
spi.wsize = n<<1;
if (n)
spi_start();
return n;
}
uint8_t fpga_start_read()
{
/************************************************************
* Send `counts` commands to the FPGA.
* Read `size` Words from each command.
* Fill the `flash_buffer` with 32 Words.
* AS_CONT:
* Continue reading Words after the buffer was used.
* User resets spi.fpga.val when done.
* Return the number of Words to be read now.
***********************************************************/
if (spi_select(0))
return 0;
if (fpga_status() != as_configured)
return 0;
if (!pipe.fpga.val)
memset(flash_buffer, 0, 64);
uint8_t n = 64 - pipe.fpga.val;
if (!n)
return n;
spi.rdata = flash_buffer + pipe.fpga.val;
n >>= 1;
if (~pipe.fpga.status & AS_CONT) {
if (!pipe.fpga.count)
return 0;
pipe.fpga.count--;
spi.cmd = pipe_fpga_cmd.cmd;
_memcopy((void*)&spi.csize, (void*)&pipe_fpga_cmd, 6);
pipe.fpga.pos = 0;
pipe.fpga.status |= AS_CONT;
}
uint8_t nn = pipe.fpga.size - pipe.fpga.pos;
if (nn && nn < n)
n = nn;
pipe.fpga.pos += n;
if (pipe.fpga.pos == pipe.fpga.size)
pipe.fpga.status &=~ AS_CONT;
spi.rsize = n << 1;
pipe.fpga.val += spi.rsize;
spi_start();
return n;
}
uint8_t fpga_pipe_ready()
{
/************************************************************
* Return the number of valid Words read in to the buffer,
* when
* - the FPGA is alive, and
* - the buffer is full, or
* - there is nothing more to read.
* Reset pipe.fpga.val, when returning nonzero,
* assuming those Words will be used now.
***********************************************************/
uint8_t r = pipe.fpga.val;
if (fpga_status() != as_configured)
return 0;
if (r >= 64 || !pipe.fpga.count && ~pipe.fpga.status & AS_CONT)
pipe.fpga.val = 0;
else
r = 0;
return r;
}
const struct pipe pipe_config_fpga_config = {
.source = pipe_flash,
.dest = pipe_fpga,
.fpga = {
.status = AS_CONFIG,
},
};
void fpga_config(uint16_t page, uint16_t count)
{
fpga_reset();
pipe = pipe_config_fpga_config;
pipe.fpga.count = count;
flash_start_stream(page, count>>8, FS_Read|FS_528);
}

View file

@ -13,7 +13,8 @@ enum fpga_flags {
fpga_aborted = 0x10,
fpga_busy = 0x20,
fpga_submitted = 0x40,
fpga_config = 0x80,
fpga_configure = 0x80,
fpga_dead = 0x80,
fpga_wait_nonzero = 0x01,
};
@ -21,8 +22,21 @@ enum fpga_flags {
void fpga_reset();
uint8_t fpga_reset_poll();
void fpga_cmd(struct fpga_cmd *c);
void fpga_start(uint8_t write);
uint8_t fpga_start_read();
uint8_t fpga_start_write();
uint8_t fpga_pipe_ready();
void fpga_config(uint16_t page, uint16_t count);
static inline uint8_t fpga_power() { return !!(PEN_VPORT.IN & (1<<PEN_PIN)); }
static inline void fpga_power_on() { PEN_VPORT.OUT |= 1<<PEN_PIN; }
static inline void fpga_power_off() { PEN_VPORT.OUT &=~ (1<<PEN_PIN); }
enum fpga_status_values {
as_off = 0,
as_on = 1,
as_reset = as_on | 1<<CRCERR_PIN,
as_configured = as_on | 1<<nCONFIG_PIN | 1<<nSTATUS_PIN,
as_crcerror = as_configured | 1<<CRCERR_PIN,
};
uint8_t fpga_status();

View file

@ -11,51 +11,120 @@
section_status(pipe) struct pipe pipe;
__attribute__((noinline))
uint8_t pipe_busy()
{
// Return true if anything is due to irq
// call this with cli() before sei();sleep()
if (spi_busy_p()) return 1;
if (flash_poll(0) & FS_Busy) return 1;
if (adc_poll(0)) return 1;
return 0;
}
uint8_t pipe_poll()
{
/************************************************************
* Drive data in `flash_buffer` from `source` to `dest`
* `pipe_cmd`:
* data comes and goes via cmd("B…")
* `pipe.valid` controls if a buffer was fully transfered
* `pipe_flash`:
* read or write pages from,to flash
* `PS_528`: use all 528 bytes
* `PS_BCH`: compute or check the BCH codes
* `pipe_adc`: (source)
* Fill the buffer with ADC readings and further status
* `pipe_fpga`:
* Configure the FPGA
* Write to the FPGA
* Read from the FPGA, with commands
***********************************************************/
// Return, if
// any hardware is busy,
// an FS_Error occured, or
// the data goes nowhere.
uint8_t r = pipe.status;
uint8_t fl = flash_poll(0);
if (spi_busy_p() || fl & FS_Busy || adc_poll(0))
goto done;
uint8_t dest = pipe.dest;
if (pipe_busy() || !dest)
return r;
#ifdef HAVE_FPGA
if (fpga_reset_poll())
goto done;
// we need to wait at least until the FPGA raises nCONFIG
if (dest & pipe_fpga && fpga_reset_poll())
return r;
#endif
uint8_t valid = pipe.valid;
if (r & PS_OUT) {
if (pipe.dest & pipe_cmd && pipe.valid & 0x1f)
// Sending the buffer to `dest`.
// Return if we are not done sending to all destinations.
if (dest & pipe_cmd && valid & 0x1f)
// cmd did not drain the buffer, yet
goto done;
if (pipe.dest & pipe_flash && !(fl & FS_Ready))
// flash did not finish successfully
goto done;
if (pipe.source & pipe_adc && !adc_poll(pipe.adc))
if (dest & pipe_flash && ~fs.status & FS_Ready)
// flash did not finish yet, successfully
goto done;
// fpga is OUT when the spi is ready.
// We are done with this buffer
pipe.valid = 0;
if (pipe.source & pipe_flash)
r &=~ PS_OUT;
// Return if next ADC reading is not yet due.
// Come back here, until is is.
if (pipe.source & pipe_adc && !adc_poll(pipe.adc))
goto done;
// Continue the flash stream.
// PS_BLK is the 64-Bytes buffer number in the page
valid = 0;
if (pipe.source & pipe_flash) {
r &= ~ PS_BLK;
r |= flash_current_block() & PS_BLK;
flash_poll(1);
}
#ifdef HAVE_FPGA
else if (pipe.source & pipe_fpga)
fpga_start(0);
// Continue the FPGA stream.
else if (pipe.source & pipe_fpga) {
if (~fpga_start_read())
pipe.source &=~ pipe_fpga;
}
#endif
r &=~ (PS_OUT|4);
r++;
goto done;
}
// Waiting for the buffer to fill
// For 528 bytes pages, the last buffer must be 80 Bytes,
// i.e., five cmd_buffers á 16 Bytes
uint8_t bflgs = 0x0f;
if ((r & PS_5) == PS_5)
bflgs = 0x1f;
// Data from the ADC is in named .bss segments. We send
// 16, 32, or 64 Bytes from the .bss segement for ead ADC reading.
// The first named .bss segment is `magic`. Observe the link order
// in the Makefile.
//
// Fun with assembly.
// `n` is the number of bytes, [16, 32, 64]
// `o` is the addr in the flash_buffer
// `v` are the currently valid cmd_buffer flags
// `f` are the fresh valid cmd_buffer flags
if (pipe.source & pipe_adc) {
uint8_t n = pipe.adc & 0x70;
if (!n)
goto adc_done;
uint8_t f = n-1;
uint8_t o = -16;
uint8_t v = pipe.valid;
uint8_t v = valid;
#if 0
uint8_t c;
do {
@ -84,49 +153,101 @@ uint8_t pipe_poll()
f = 0xff;
n = 64-o;
}
pipe.valid |= f>>4;
valid |= f>>4;
_memcopy(flash_buffer+o, (void*)&magic, n);
}
adc_done:
if (pipe.source & pipe_flash && fl & FS_Ready)
pipe.valid = bflgs;
// When the flash is done, flag the buffer valid,
// including BCH Bytes.
if (pipe.source & pipe_flash && fs.status & FS_Ready)
valid = bflgs;
#ifdef HAVE_FPGA
else if (pipe.source & pipe_fpga)
pipe.valid = 0x0f;
// The FPGA delivers 64 Bytes.
else if (pipe.source & pipe_fpga && fpga_pipe_ready())
valid = 0x0f;
#endif
if (~pipe.valid & 0x0f)
// The buffer is not here, yet. Nothing we can do now.
if (~valid & 0x0f)
goto done;
// The buffer shall include BCH Bytes.
if (r & PS_BCH) {
if (!(r&3))
// First buffer of a page, clear the parity
if (!(r & PS_BLK))
bch4369_init(config.bch_salt);
// Add the current buffer to the parity
uint8_t *bend = bch4369_stri(flash_buffer, 64);
if (!(~r & 3)) {
// reuse Y=bend
_memcopyyz(bend, bch_parity, 16);
pipe.valid = 0x01f;
// Last buffer of the page:
if (!(~r & PS_BLK)) {
bch4369_fini();
valid = 0x01f;
// When the Flash is not the source,
// copy the computed parity into the buffer.
if (~pipe.source & pipe_flash)
_memcopyyz(bend, bch_parity, 16);
// When the page as read from flash has invalid parity,
// stop writing to the FPGA.
else if (_memcmpyz(bch_parity, bend, 16)) {
dest &=~ pipe_fpga;
r |= PS_ERR;
}
}
}
if (~pipe.valid & bflgs)
// We still do not have all data we need
if (~valid & bflgs)
goto done;
#ifdef HAVE_FPGA
if (pipe.dest & pipe_fpga)
fpga_start(1);
#endif
// The buffer is full, send it.
r |= PS_OUT;
#ifdef HAVE_FPGA
// Resume the FPGA stream
if (dest & pipe_fpga & ~fpga_start_write())
dest &=~ pipe_fpga;
else
#endif
// Resume the flash stream
if (dest & pipe_flash) {
r &= ~ PS_BLK;
r |= flash_current_block() & PS_BLK;
flash_poll(1);
if (flash_stream_done())
dest &=~ pipe_flash;
}
done:
pipe.dest = dest;
pipe.valid = valid;
pipe.status = r;
return r;
}
void pipe_config(const struct pipe_config *c)
void pipe_config(const struct pipe_config *c, const struct pipe_fpga_cmd *a)
{
pipe = c->pipe;
if ((pipe.source | pipe.dest) & pipe_flash)
flash_start_stream(c->page, c->npages, c->flash);
/************************************************************
* cmd("B0!", «fpga-cmd») optionally configure an FPGA cmd
* cmd("P0:, «pipe-cfg») with `AS_CMD` in `.status`
***********************************************************/
if (c->pipe.dest) {
pipe = c->pipe;
if ((pipe.source | pipe.dest) & pipe_flash)
flash_start_stream(c->page, c->npages, c->flash);
}
else {
// new source (NOT flash)
pipe.source = c->pipe.source;
}
if (pipe.source & pipe_adc)
adc_start_stream(pipe.adc);
#ifdef HAVE_FPGA
if (pipe.source & pipe_fpga) {
if (a && pipe.fpga.status & AS_CMD)
_memcopyzy((void*)&pipe_fpga_cmd, (void*)a, sizeof(*a));
else
memset(&pipe_fpga_cmd, 0, sizeof(*a));
fpga_start_read();
}
#endif
}

View file

@ -8,10 +8,15 @@ enum pipe_ports {
pipe_fpga = 8,
PS_OUT = 0x80,
PS_BCH = 0x10,
PS_528 = 0x08,
PS_BLK = 0x03,
PS_ERR = 0x40,
PS_BCH = 0x20,
PS_528 = 0x10,
PS_BLK = 0x07,
PS_5 = PS_BLK | PS_528, // need 16 bytes more
AS_CONT = 1,
AS_CONFIG = SPI_CONFIG, // = 2
AS_CMD = 4,
};
extern
@ -23,14 +28,11 @@ struct pipe {
uint8_t adc;
#ifdef HAVE_FPGA
struct {
uint16_t size;
uint8_t cmd[4];
uint8_t csize;
uint8_t zsize;
uint8_t isize;
uint8_t zero;
uint8_t wait;
uint8_t mask;
uint8_t status; // AS_…
uint16_t count; // number of cmds(read), 64 Bytes(write)
uint8_t size; // Words per cmd (read), extra Words (write), 0==256
uint8_t val; // Valid Bytes in Buffer (read)
uint8_t pos; // Words read from current cmd (read)
} fpga;
#endif
} pipe;
@ -42,6 +44,18 @@ struct pipe_config {
uint8_t flash;
};
extern
struct pipe_fpga_cmd {
uint8_t csize;
uint8_t zsize;
uint8_t isize;
uint8_t zero;
uint8_t wait;
uint8_t mask;
uint8_t cmd[10];
} pipe_fpga_cmd;
uint8_t pipe_busy();
uint8_t pipe_poll();
void pipe_cron();
void pipe_config(const struct pipe_config *c);
void pipe_config(const struct pipe_config *c, const struct pipe_fpga_cmd *a);

View file

@ -31,6 +31,11 @@ ISR(SPI0_INT_vect)
d = spi.zero;
}
else if (spi.wsize) {
// TODO:
// spi.csize = spi.wsize;
// spi.wsize = 0;
// spi.cmd = spi.wdata;
// goto repeat;
spi.wsize--;
d = *spi.wdata++;
}

View file

@ -40,8 +40,17 @@ int main()
send_hex_byte_eol(magic.reset_source);
while (1) {
// The pipe may become idle before we reach sleep.
// Make sure we have nothing to do before we sleep.
// The sleep command will execute even when an irq
// is pending. It will wake us immediately.
cli();
if (!command_pending() && (!pipe.dest || pipe_busy())) {
sei();
sleep_cpu();
}
sei();
sleep_cpu();
command();
pipe_poll();
}
}

View file

@ -26,3 +26,10 @@ void send_hex_long(uint32_t b)
extern uint8_t uart_rx_err;
extern uint8_t uart_rx_errors;
extern volatile uint8_t uart_rx_mes;
static inline
uint8_t command_pending()
{
return uart_rx_mes;
}