diff --git a/src/.gitignore b/src/.gitignore index 2f8c9ea..2921dc0 100644 --- a/src/.gitignore +++ b/src/.gitignore @@ -1 +1,2 @@ thhor.userrow +*.s diff --git a/src/Makefile b/src/Makefile index 5d8d444..60c61e7 100644 --- a/src/Makefile +++ b/src/Makefile @@ -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 diff --git a/src/cmd.c b/src/cmd.c index dd3edc5..b687341 100644 --- a/src/cmd.c +++ b/src/cmd.c @@ -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('@')) { diff --git a/src/config.h b/src/config.h index 39d346d..3412198 100644 --- a/src/config.h +++ b/src/config.h @@ -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 diff --git a/src/eeprom.ld b/src/eeprom.ld index 0654d25..b72db90 100644 --- a/src/eeprom.ld +++ b/src/eeprom.ld @@ -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 diff --git a/src/flash.h b/src/flash.h index c509eee..46af975 100644 --- a/src/flash.h +++ b/src/flash.h @@ -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 diff --git a/src/fpga.c b/src/fpga.c index 07d549d..8b7a770 100644 --- a/src/fpga.c +++ b/src/fpga.c @@ -2,6 +2,7 @@ #include "pipe.h" #include "fpga.h" #include "spi.h" +#include 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<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); } diff --git a/src/fpga.h b/src/fpga.h index 109d992..7576d36 100644 --- a/src/fpga.h +++ b/src/fpga.h @@ -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<>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 } - diff --git a/src/pipe.h b/src/pipe.h index bb23d65..9d9c3f7 100644 --- a/src/pipe.h +++ b/src/pipe.h @@ -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); diff --git a/src/spi.c b/src/spi.c index b1425ac..85cf3fa 100644 --- a/src/spi.c +++ b/src/spi.c @@ -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++; } diff --git a/src/thhor.c b/src/thhor.c index 32f291a..e6a2869 100644 --- a/src/thhor.c +++ b/src/thhor.c @@ -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(); } } diff --git a/src/uart.h b/src/uart.h index 6438874..f06a422 100644 --- a/src/uart.h +++ b/src/uart.h @@ -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; +}