Compare commits

..

No commits in common. "93e1c3230e70b1b0f38585bf0c1c9619e0322692" and "ecc33261a2890b460764489e3b4f532138c9dcbe" have entirely different histories.

13 changed files with 108 additions and 428 deletions

1
src/.gitignore vendored
View file

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

View file

@ -11,13 +11,22 @@ thhor_all: thhor.eeprom thhor.userrow
CFLAGS_thhor = -Ibch4369 -DHAVE_FPGA -DSEND_HEX -DTHHOR
MCU_thhor = attiny3224
SN_thhor = 1
# 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
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
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
@ -73,24 +82,23 @@ pMCU-attiny824 = t824
pMCU-attiny3224 = t3224
# WDT
fuse0_thhor= 0x00
fuse0_dose= 0x00
# BOD
fuse1_thhor= 0x00
fuse1_dose= 0x00
# OSC, 20 MHz
fuse2_thhor= 0x7e
# Reserved
fuse3_thhor= 0xff
# Reserved
fuse4_thhor= 0xff
fuse2_dose= 0x7e
# ???
fuse4_dose= 0xff
# SYS0 (default 0xf6) RESET, EEPROM erase
fuse5_thhor= 0xf7
fuse5_dose= 0xf7
# SYS1 startup time (64ms)
fuse6_thhor= 0xff
fuse6_dose= 0xff
# APPEND
fuse7_thhor= 0x00
fuse7_dose= 0x00
# BOOTEND
fuse8_thhor= 0x00
fuses_thhor =$(patsubst %, 0x%, 00 00 7e ff ff f7 ff 00 00)
fuse8_dose= 0x00
fuses_dose =$(patsubst %, 0x%, 00 00 7e ff ff f7 ff 00 00)
fuses_thhor = $(fuses_dose)
AVRDUDEPROG = avrdude
AVRDUDE = $(AVRDUDEPROG)
@ -99,6 +107,8 @@ 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,38 +244,17 @@ 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, (void*)bptr);
pipe_config((void*)cmd_buffer);
r = pipe_poll();
break;
#ifdef HAVE_FPGA
case 'O':
r = fpga_power();
if (bflg & 1)
// "O1" power off
if (bflg == 1)
fpga_power_off();
if (bflg >= 2) {
// "O2" power on
else if (bflg == 2)
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,8 +23,6 @@ struct config {
uint16_t pwm_min;
uint16_t pwm_max;
#endif
uint16_t fpga_config_page;
uint16_t fpga_config_count;
};
enum magic_flags {
@ -34,7 +32,7 @@ enum magic_flags {
#endif
#ifdef HAVE_FPGA
MAGIC = 0xC5,
VERSION = 0x00,
VERSION = 0x01,
#endif
};
@ -161,8 +159,6 @@ 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 !
@ -196,43 +192,6 @@ 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 (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
eemap : ORIGIN = 0x1400, LENGTH = 0x100
eedef : ORIGIN = 0x810000, LENGTH = 0x100
uumap : ORIGIN = 0x1300, LENGTH = 0x20
uudef : ORIGIN = 0x850000, LENGTH = 0x20
}
SECTIONS
{
.eemap 0x801400:
.eemap 0x1400:
{
ee1_start = .;
*(.eeprom1)
@ -22,7 +22,7 @@ SECTIONS
ee9_end = .;
ee9_size = ee9_end - ee9_start;
} >eemap AT >eedef
.uumap 0x801300:
.uumap 0x1300:
{
*(.userrow)
} >uumap AT >uudef

View file

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

View file

@ -2,7 +2,6 @@
#include "pipe.h"
#include "fpga.h"
#include "spi.h"
#include <string.h>
uint8_t fpga_reset_poll()
{
@ -13,18 +12,6 @@ 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);
@ -37,8 +24,8 @@ void fpga_cmd(struct fpga_cmd *c)
{
uint8_t n = c->n;
uint8_t z = c->z;
if (fpga_status() != as_configured) {
c->n = n | fpga_dead;
if (fpga_reset_poll()) {
c->n = n | fpga_busy;
return;
}
if (n & fpga_abort && spi_abort()) {
@ -54,7 +41,7 @@ void fpga_cmd(struct fpga_cmd *c)
return;
c->n = n | fpga_submitted;
spi_select(n & fpga_configure ? SPI_CONFIG : 0);
spi_select(n & fpga_config ? SPI_CONFIG : 0);
spi.csize = n &= fpga_size;
spi.zero = z & 0x80; // send 0x0000 or 0x8080
spi.cmd = c->d;
@ -75,106 +62,25 @@ void fpga_cmd(struct fpga_cmd *c)
spi_start();
}
struct pipe_fpga_cmd pipe_fpga_cmd;
uint8_t fpga_start_write()
void fpga_start(uint8_t write)
{
uint8_t mode = pipe.fpga.status & SPI_CONFIG;
uint8_t mode = 0;
if (pipe.fpga.zero == SPI_CONFIG)
mode = SPI_CONFIG;
spi_select(mode);
_memcopy(&spi.csize, &pipe.fpga.csize, 6);
spi.cmd = pipe.fpga.cmd;
spi.wdata = flash_buffer;
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;
spi.rdata = flash_buffer;
uint8_t n = 64;
if (n > pipe.fpga.size)
n = pipe.fpga.size;
pipe.fpga.size -= n;
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;
return;
if (write)
spi.wsize = n;
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);
spi.rsize = n;
spi_start();
}

View file

@ -13,8 +13,7 @@ enum fpga_flags {
fpga_aborted = 0x10,
fpga_busy = 0x20,
fpga_submitted = 0x40,
fpga_configure = 0x80,
fpga_dead = 0x80,
fpga_config = 0x80,
fpga_wait_nonzero = 0x01,
};
@ -22,21 +21,8 @@ enum fpga_flags {
void fpga_reset();
uint8_t fpga_reset_poll();
void fpga_cmd(struct fpga_cmd *c);
uint8_t fpga_start_read();
uint8_t fpga_start_write();
uint8_t fpga_pipe_ready();
void fpga_config(uint16_t page, uint16_t count);
void fpga_start(uint8_t write);
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,120 +11,51 @@
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 dest = pipe.dest;
if (pipe_busy() || !dest)
return r;
uint8_t fl = flash_poll(0);
if (spi_busy_p() || fl & FS_Busy || adc_poll(0))
goto done;
#ifdef HAVE_FPGA
// we need to wait at least until the FPGA raises nCONFIG
if (dest & pipe_fpga && fpga_reset_poll())
return r;
if (fpga_reset_poll())
goto done;
#endif
uint8_t valid = pipe.valid;
if (r & PS_OUT) {
// Sending the buffer to `dest`.
// Return if we are not done sending to all destinations.
if (dest & pipe_cmd && valid & 0x1f)
if (pipe.dest & pipe_cmd && pipe.valid & 0x1f)
// cmd did not drain the buffer, yet
goto done;
if (dest & pipe_flash && ~fs.status & FS_Ready)
// flash did not finish yet, successfully
if (pipe.dest & pipe_flash && !(fl & FS_Ready))
// flash did not finish successfully
goto done;
if (pipe.source & pipe_adc && !adc_poll(pipe.adc))
goto done;
// fpga is OUT when the spi is ready.
// We are done with this buffer
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;
pipe.valid = 0;
if (pipe.source & pipe_flash)
flash_poll(1);
}
#ifdef HAVE_FPGA
// Continue the FPGA stream.
else if (pipe.source & pipe_fpga) {
if (~fpga_start_read())
pipe.source &=~ pipe_fpga;
}
else if (pipe.source & pipe_fpga)
fpga_start(0);
#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 = valid;
uint8_t v = pipe.valid;
#if 0
uint8_t c;
do {
@ -153,101 +84,49 @@ uint8_t pipe_poll()
f = 0xff;
n = 64-o;
}
valid |= f>>4;
pipe.valid |= f>>4;
_memcopy(flash_buffer+o, (void*)&magic, n);
}
adc_done:
// When the flash is done, flag the buffer valid,
// including BCH Bytes.
if (pipe.source & pipe_flash && fs.status & FS_Ready)
valid = bflgs;
if (pipe.source & pipe_flash && fl & FS_Ready)
pipe.valid = bflgs;
#ifdef HAVE_FPGA
// The FPGA delivers 64 Bytes.
else if (pipe.source & pipe_fpga && fpga_pipe_ready())
valid = 0x0f;
else if (pipe.source & pipe_fpga)
pipe.valid = 0x0f;
#endif
// The buffer is not here, yet. Nothing we can do now.
if (~valid & 0x0f)
if (~pipe.valid & 0x0f)
goto done;
// The buffer shall include BCH Bytes.
if (r & PS_BCH) {
// First buffer of a page, clear the parity
if (!(r & PS_BLK))
if (!(r&3))
bch4369_init(config.bch_salt);
// Add the current buffer to the parity
uint8_t *bend = bch4369_stri(flash_buffer, 64);
// 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 (!(~r & 3)) {
// reuse Y=bend
_memcopyyz(bend, bch_parity, 16);
pipe.valid = 0x01f;
}
}
// We still do not have all data we need
if (~valid & bflgs)
if (~pipe.valid & bflgs)
goto done;
// 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
if (pipe.dest & pipe_fpga)
fpga_start(1);
#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;
}
r |= PS_OUT;
done:
pipe.dest = dest;
pipe.valid = valid;
pipe.status = r;
return r;
}
void pipe_config(const struct pipe_config *c, const struct pipe_fpga_cmd *a)
void pipe_config(const struct pipe_config *c)
{
/************************************************************
* 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;
}
pipe = c->pipe;
if ((pipe.source | pipe.dest) & pipe_flash)
flash_start_stream(c->page, c->npages, c->flash);
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,15 +8,10 @@ enum pipe_ports {
pipe_fpga = 8,
PS_OUT = 0x80,
PS_ERR = 0x40,
PS_BCH = 0x20,
PS_528 = 0x10,
PS_BLK = 0x07,
PS_BCH = 0x10,
PS_528 = 0x08,
PS_BLK = 0x03,
PS_5 = PS_BLK | PS_528, // need 16 bytes more
AS_CONT = 1,
AS_CONFIG = SPI_CONFIG, // = 2
AS_CMD = 4,
};
extern
@ -28,11 +23,14 @@ struct pipe {
uint8_t adc;
#ifdef HAVE_FPGA
struct {
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)
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;
} fpga;
#endif
} pipe;
@ -44,18 +42,6 @@ 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, const struct pipe_fpga_cmd *a);
void pipe_config(const struct pipe_config *c);

View file

@ -31,11 +31,6 @@ 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,17 +40,8 @@ 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,10 +26,3 @@ 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;
}