Compare commits
No commits in common. "93e1c3230e70b1b0f38585bf0c1c9619e0322692" and "ecc33261a2890b460764489e3b4f532138c9dcbe" have entirely different histories.
93e1c3230e
...
ecc33261a2
13 changed files with 108 additions and 428 deletions
1
src/.gitignore
vendored
1
src/.gitignore
vendored
|
|
@ -1,2 +1 @@
|
||||||
thhor.userrow
|
thhor.userrow
|
||||||
*.s
|
|
||||||
|
|
|
||||||
44
src/Makefile
44
src/Makefile
|
|
@ -11,13 +11,22 @@ thhor_all: thhor.eeprom thhor.userrow
|
||||||
CFLAGS_thhor = -Ibch4369 -DHAVE_FPGA -DSEND_HEX -DTHHOR
|
CFLAGS_thhor = -Ibch4369 -DHAVE_FPGA -DSEND_HEX -DTHHOR
|
||||||
MCU_thhor = attiny3224
|
MCU_thhor = attiny3224
|
||||||
SN_thhor = 1
|
SN_thhor = 1
|
||||||
# Link order defines ADC packet composition
|
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
|
||||||
# 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
|
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))
|
MCU = $(MCU_$(PROJ))
|
||||||
OPT = -Os
|
OPT = -Os
|
||||||
|
|
||||||
|
|
@ -73,24 +82,23 @@ pMCU-attiny824 = t824
|
||||||
pMCU-attiny3224 = t3224
|
pMCU-attiny3224 = t3224
|
||||||
|
|
||||||
# WDT
|
# WDT
|
||||||
fuse0_thhor= 0x00
|
fuse0_dose= 0x00
|
||||||
# BOD
|
# BOD
|
||||||
fuse1_thhor= 0x00
|
fuse1_dose= 0x00
|
||||||
# OSC, 20 MHz
|
# OSC, 20 MHz
|
||||||
fuse2_thhor= 0x7e
|
fuse2_dose= 0x7e
|
||||||
# Reserved
|
# ???
|
||||||
fuse3_thhor= 0xff
|
fuse4_dose= 0xff
|
||||||
# Reserved
|
|
||||||
fuse4_thhor= 0xff
|
|
||||||
# SYS0 (default 0xf6) RESET, EEPROM erase
|
# SYS0 (default 0xf6) RESET, EEPROM erase
|
||||||
fuse5_thhor= 0xf7
|
fuse5_dose= 0xf7
|
||||||
# SYS1 startup time (64ms)
|
# SYS1 startup time (64ms)
|
||||||
fuse6_thhor= 0xff
|
fuse6_dose= 0xff
|
||||||
# APPEND
|
# APPEND
|
||||||
fuse7_thhor= 0x00
|
fuse7_dose= 0x00
|
||||||
# BOOTEND
|
# BOOTEND
|
||||||
fuse8_thhor= 0x00
|
fuse8_dose= 0x00
|
||||||
fuses_thhor =$(patsubst %, 0x%, 00 00 7e ff ff f7 ff 00 00)
|
fuses_dose =$(patsubst %, 0x%, 00 00 7e ff ff f7 ff 00 00)
|
||||||
|
fuses_thhor = $(fuses_dose)
|
||||||
|
|
||||||
AVRDUDEPROG = avrdude
|
AVRDUDEPROG = avrdude
|
||||||
AVRDUDE = $(AVRDUDEPROG)
|
AVRDUDE = $(AVRDUDEPROG)
|
||||||
|
|
@ -99,6 +107,8 @@ AVRDUDE_PORT = /dev/ttyUSB0
|
||||||
|
|
||||||
AD = $(AVRDUDE) -p $(pMCU-$(MCU)) -P $(AVRDUDE_PORT) -c $(AVRDUDE_PROGRAMMER)
|
AD = $(AVRDUDE) -p $(pMCU-$(MCU)) -P $(AVRDUDE_PORT) -c $(AVRDUDE_PROGRAMMER)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
sig_dose = 0x1e 0x92 0x2c
|
sig_dose = 0x1e 0x92 0x2c
|
||||||
sig_thhor = 0x1e 0x95 0x28
|
sig_thhor = 0x1e 0x95 0x28
|
||||||
|
|
||||||
|
|
|
||||||
27
src/cmd.c
27
src/cmd.c
|
|
@ -244,38 +244,17 @@ void parse_command(const uint8_t *s, uint8_t n)
|
||||||
goto send_buffer;
|
goto send_buffer;
|
||||||
break;
|
break;
|
||||||
case 'P':
|
case 'P':
|
||||||
if (cmd_flag('!'))
|
|
||||||
pipe.dest = 0;
|
|
||||||
if (have_b)
|
if (have_b)
|
||||||
pipe_config((void*)cmd_buffer, (void*)bptr);
|
pipe_config((void*)cmd_buffer);
|
||||||
r = pipe_poll();
|
r = pipe_poll();
|
||||||
break;
|
break;
|
||||||
#ifdef HAVE_FPGA
|
#ifdef HAVE_FPGA
|
||||||
case 'O':
|
case 'O':
|
||||||
r = fpga_power();
|
r = fpga_power();
|
||||||
if (bflg & 1)
|
if (bflg == 1)
|
||||||
// "O1" power off
|
|
||||||
fpga_power_off();
|
fpga_power_off();
|
||||||
if (bflg >= 2) {
|
else if (bflg == 2)
|
||||||
// "O2" power on
|
|
||||||
fpga_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;
|
break;
|
||||||
case 'C':
|
case 'C':
|
||||||
if (cmd_flag('@')) {
|
if (cmd_flag('@')) {
|
||||||
|
|
|
||||||
43
src/config.h
43
src/config.h
|
|
@ -23,8 +23,6 @@ struct config {
|
||||||
uint16_t pwm_min;
|
uint16_t pwm_min;
|
||||||
uint16_t pwm_max;
|
uint16_t pwm_max;
|
||||||
#endif
|
#endif
|
||||||
uint16_t fpga_config_page;
|
|
||||||
uint16_t fpga_config_count;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
enum magic_flags {
|
enum magic_flags {
|
||||||
|
|
@ -34,7 +32,7 @@ enum magic_flags {
|
||||||
#endif
|
#endif
|
||||||
#ifdef HAVE_FPGA
|
#ifdef HAVE_FPGA
|
||||||
MAGIC = 0xC5,
|
MAGIC = 0xC5,
|
||||||
VERSION = 0x00,
|
VERSION = 0x01,
|
||||||
#endif
|
#endif
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -161,8 +159,6 @@ extern struct magic {
|
||||||
#if 0
|
#if 0
|
||||||
#define _memcopy memcpy
|
#define _memcopy memcpy
|
||||||
#define _memcopyyz memcpy
|
#define _memcopyyz memcpy
|
||||||
#define _memcopyzy memcpy
|
|
||||||
#define _memcmpyz memcmp
|
|
||||||
#else
|
#else
|
||||||
// avoid the avr-libc memcpy.
|
// avoid the avr-libc memcpy.
|
||||||
// ¡ n must not be zero !
|
// ¡ n must not be zero !
|
||||||
|
|
@ -196,43 +192,6 @@ void _memcopyyz(uint8_t *d, uint8_t *s, uint8_t n)
|
||||||
:: "r0", "memory");
|
:: "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
|
#endif
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -4,14 +4,14 @@
|
||||||
*/
|
*/
|
||||||
MEMORY
|
MEMORY
|
||||||
{
|
{
|
||||||
eemap (rw!x) : ORIGIN = 0x801400, LENGTH = 0x100
|
eemap : ORIGIN = 0x1400, LENGTH = 0x100
|
||||||
eedef (rw!x) : ORIGIN = 0x810000, LENGTH = 0x100
|
eedef : ORIGIN = 0x810000, LENGTH = 0x100
|
||||||
uumap (rw!x) : ORIGIN = 0x801300, LENGTH = 0x020
|
uumap : ORIGIN = 0x1300, LENGTH = 0x20
|
||||||
uudef (rw!x) : ORIGIN = 0x850000, LENGTH = 0x020
|
uudef : ORIGIN = 0x850000, LENGTH = 0x20
|
||||||
}
|
}
|
||||||
SECTIONS
|
SECTIONS
|
||||||
{
|
{
|
||||||
.eemap 0x801400:
|
.eemap 0x1400:
|
||||||
{
|
{
|
||||||
ee1_start = .;
|
ee1_start = .;
|
||||||
*(.eeprom1)
|
*(.eeprom1)
|
||||||
|
|
@ -22,7 +22,7 @@ SECTIONS
|
||||||
ee9_end = .;
|
ee9_end = .;
|
||||||
ee9_size = ee9_end - ee9_start;
|
ee9_size = ee9_end - ee9_start;
|
||||||
} >eemap AT >eedef
|
} >eemap AT >eedef
|
||||||
.uumap 0x801300:
|
.uumap 0x1300:
|
||||||
{
|
{
|
||||||
*(.userrow)
|
*(.userrow)
|
||||||
} >uumap AT >uudef
|
} >uumap AT >uudef
|
||||||
|
|
|
||||||
|
|
@ -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];
|
extern uint8_t flash_buffer[FB_SIZE];
|
||||||
uint8_t flash_submit_command(uint8_t *cmd);
|
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_start_stream(uint16_t page, uint16_t npages, uint8_t flags);
|
||||||
uint8_t flash_stream_done();
|
|
||||||
uint8_t flash_poll(uint8_t rr);
|
uint8_t flash_poll(uint8_t rr);
|
||||||
uint16_t flash_find_free();
|
uint16_t flash_find_free();
|
||||||
|
|
||||||
|
|
@ -54,6 +53,4 @@ enum {
|
||||||
FS_528 = 128, // do 528 byte pages.
|
FS_528 = 128, // do 528 byte pages.
|
||||||
};
|
};
|
||||||
|
|
||||||
static inline uint8_t flash_current_block() { return fs.block; }
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
||||||
132
src/fpga.c
132
src/fpga.c
|
|
@ -2,7 +2,6 @@
|
||||||
#include "pipe.h"
|
#include "pipe.h"
|
||||||
#include "fpga.h"
|
#include "fpga.h"
|
||||||
#include "spi.h"
|
#include "spi.h"
|
||||||
#include <string.h>
|
|
||||||
|
|
||||||
uint8_t fpga_reset_poll()
|
uint8_t fpga_reset_poll()
|
||||||
{
|
{
|
||||||
|
|
@ -13,18 +12,6 @@ uint8_t fpga_reset_poll()
|
||||||
return r;
|
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()
|
void fpga_reset()
|
||||||
{
|
{
|
||||||
nCONFIG_VPORT.OUT &=~ (1<<nCONFIG_PIN);
|
nCONFIG_VPORT.OUT &=~ (1<<nCONFIG_PIN);
|
||||||
|
|
@ -37,8 +24,8 @@ void fpga_cmd(struct fpga_cmd *c)
|
||||||
{
|
{
|
||||||
uint8_t n = c->n;
|
uint8_t n = c->n;
|
||||||
uint8_t z = c->z;
|
uint8_t z = c->z;
|
||||||
if (fpga_status() != as_configured) {
|
if (fpga_reset_poll()) {
|
||||||
c->n = n | fpga_dead;
|
c->n = n | fpga_busy;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (n & fpga_abort && spi_abort()) {
|
if (n & fpga_abort && spi_abort()) {
|
||||||
|
|
@ -54,7 +41,7 @@ void fpga_cmd(struct fpga_cmd *c)
|
||||||
return;
|
return;
|
||||||
c->n = n | fpga_submitted;
|
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.csize = n &= fpga_size;
|
||||||
spi.zero = z & 0x80; // send 0x0000 or 0x8080
|
spi.zero = z & 0x80; // send 0x0000 or 0x8080
|
||||||
spi.cmd = c->d;
|
spi.cmd = c->d;
|
||||||
|
|
@ -75,106 +62,25 @@ void fpga_cmd(struct fpga_cmd *c)
|
||||||
spi_start();
|
spi_start();
|
||||||
}
|
}
|
||||||
|
|
||||||
struct pipe_fpga_cmd pipe_fpga_cmd;
|
void fpga_start(uint8_t write)
|
||||||
|
|
||||||
uint8_t fpga_start_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);
|
spi_select(mode);
|
||||||
|
_memcopy(&spi.csize, &pipe.fpga.csize, 6);
|
||||||
|
spi.cmd = pipe.fpga.cmd;
|
||||||
spi.wdata = flash_buffer;
|
spi.wdata = flash_buffer;
|
||||||
uint8_t n = 32;
|
spi.rdata = flash_buffer;
|
||||||
if (pipe.fpga.count)
|
uint8_t n = 64;
|
||||||
pipe.fpga.count--;
|
if (n > pipe.fpga.size)
|
||||||
else {
|
n = pipe.fpga.size;
|
||||||
uint8_t nn = pipe.fpga.size;
|
pipe.fpga.size -= n;
|
||||||
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)
|
if (!n)
|
||||||
return n;
|
return;
|
||||||
spi.rdata = flash_buffer + pipe.fpga.val;
|
if (write)
|
||||||
n >>= 1;
|
spi.wsize = n;
|
||||||
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
|
else
|
||||||
r = 0;
|
spi.rsize = n;
|
||||||
return r;
|
spi_start();
|
||||||
}
|
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
18
src/fpga.h
18
src/fpga.h
|
|
@ -13,8 +13,7 @@ enum fpga_flags {
|
||||||
fpga_aborted = 0x10,
|
fpga_aborted = 0x10,
|
||||||
fpga_busy = 0x20,
|
fpga_busy = 0x20,
|
||||||
fpga_submitted = 0x40,
|
fpga_submitted = 0x40,
|
||||||
fpga_configure = 0x80,
|
fpga_config = 0x80,
|
||||||
fpga_dead = 0x80,
|
|
||||||
|
|
||||||
fpga_wait_nonzero = 0x01,
|
fpga_wait_nonzero = 0x01,
|
||||||
};
|
};
|
||||||
|
|
@ -22,21 +21,8 @@ enum fpga_flags {
|
||||||
void fpga_reset();
|
void fpga_reset();
|
||||||
uint8_t fpga_reset_poll();
|
uint8_t fpga_reset_poll();
|
||||||
void fpga_cmd(struct fpga_cmd *c);
|
void fpga_cmd(struct fpga_cmd *c);
|
||||||
uint8_t fpga_start_read();
|
void fpga_start(uint8_t write);
|
||||||
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 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_on() { PEN_VPORT.OUT |= 1<<PEN_PIN; }
|
||||||
static inline void fpga_power_off() { 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();
|
|
||||||
|
|
|
||||||
195
src/pipe.c
195
src/pipe.c
|
|
@ -11,120 +11,51 @@
|
||||||
|
|
||||||
section_status(pipe) struct pipe pipe;
|
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()
|
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 r = pipe.status;
|
||||||
uint8_t dest = pipe.dest;
|
uint8_t fl = flash_poll(0);
|
||||||
|
if (spi_busy_p() || fl & FS_Busy || adc_poll(0))
|
||||||
if (pipe_busy() || !dest)
|
goto done;
|
||||||
return r;
|
|
||||||
#ifdef HAVE_FPGA
|
#ifdef HAVE_FPGA
|
||||||
// we need to wait at least until the FPGA raises nCONFIG
|
if (fpga_reset_poll())
|
||||||
if (dest & pipe_fpga && fpga_reset_poll())
|
goto done;
|
||||||
return r;
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
uint8_t valid = pipe.valid;
|
|
||||||
|
|
||||||
if (r & PS_OUT) {
|
if (r & PS_OUT) {
|
||||||
// Sending the buffer to `dest`.
|
if (pipe.dest & pipe_cmd && pipe.valid & 0x1f)
|
||||||
// Return if we are not done sending to all destinations.
|
|
||||||
|
|
||||||
if (dest & pipe_cmd && valid & 0x1f)
|
|
||||||
// cmd did not drain the buffer, yet
|
// cmd did not drain the buffer, yet
|
||||||
goto done;
|
goto done;
|
||||||
if (dest & pipe_flash && ~fs.status & FS_Ready)
|
if (pipe.dest & pipe_flash && !(fl & FS_Ready))
|
||||||
// flash did not finish yet, successfully
|
// flash did not finish successfully
|
||||||
|
goto done;
|
||||||
|
if (pipe.source & pipe_adc && !adc_poll(pipe.adc))
|
||||||
goto done;
|
goto done;
|
||||||
|
|
||||||
// fpga is OUT when the spi is ready.
|
// fpga is OUT when the spi is ready.
|
||||||
// We are done with this buffer
|
// We are done with this buffer
|
||||||
|
pipe.valid = 0;
|
||||||
r &=~ PS_OUT;
|
if (pipe.source & pipe_flash)
|
||||||
|
|
||||||
// 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);
|
flash_poll(1);
|
||||||
}
|
|
||||||
#ifdef HAVE_FPGA
|
#ifdef HAVE_FPGA
|
||||||
// Continue the FPGA stream.
|
else if (pipe.source & pipe_fpga)
|
||||||
|
fpga_start(0);
|
||||||
else if (pipe.source & pipe_fpga) {
|
|
||||||
if (~fpga_start_read())
|
|
||||||
pipe.source &=~ pipe_fpga;
|
|
||||||
}
|
|
||||||
#endif
|
#endif
|
||||||
|
r &=~ (PS_OUT|4);
|
||||||
|
r++;
|
||||||
goto done;
|
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;
|
uint8_t bflgs = 0x0f;
|
||||||
if ((r & PS_5) == PS_5)
|
if ((r & PS_5) == PS_5)
|
||||||
bflgs = 0x1f;
|
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) {
|
if (pipe.source & pipe_adc) {
|
||||||
uint8_t n = pipe.adc & 0x70;
|
uint8_t n = pipe.adc & 0x70;
|
||||||
if (!n)
|
if (!n)
|
||||||
goto adc_done;
|
goto adc_done;
|
||||||
uint8_t f = n-1;
|
uint8_t f = n-1;
|
||||||
uint8_t o = -16;
|
uint8_t o = -16;
|
||||||
uint8_t v = valid;
|
uint8_t v = pipe.valid;
|
||||||
#if 0
|
#if 0
|
||||||
uint8_t c;
|
uint8_t c;
|
||||||
do {
|
do {
|
||||||
|
|
@ -153,101 +84,49 @@ uint8_t pipe_poll()
|
||||||
f = 0xff;
|
f = 0xff;
|
||||||
n = 64-o;
|
n = 64-o;
|
||||||
}
|
}
|
||||||
valid |= f>>4;
|
pipe.valid |= f>>4;
|
||||||
_memcopy(flash_buffer+o, (void*)&magic, n);
|
_memcopy(flash_buffer+o, (void*)&magic, n);
|
||||||
}
|
}
|
||||||
adc_done:
|
adc_done:
|
||||||
|
|
||||||
// When the flash is done, flag the buffer valid,
|
if (pipe.source & pipe_flash && fl & FS_Ready)
|
||||||
// including BCH Bytes.
|
pipe.valid = bflgs;
|
||||||
if (pipe.source & pipe_flash && fs.status & FS_Ready)
|
|
||||||
valid = bflgs;
|
|
||||||
#ifdef HAVE_FPGA
|
#ifdef HAVE_FPGA
|
||||||
// The FPGA delivers 64 Bytes.
|
else if (pipe.source & pipe_fpga)
|
||||||
else if (pipe.source & pipe_fpga && fpga_pipe_ready())
|
pipe.valid = 0x0f;
|
||||||
valid = 0x0f;
|
|
||||||
#endif
|
#endif
|
||||||
// The buffer is not here, yet. Nothing we can do now.
|
if (~pipe.valid & 0x0f)
|
||||||
if (~valid & 0x0f)
|
|
||||||
goto done;
|
goto done;
|
||||||
|
|
||||||
// The buffer shall include BCH Bytes.
|
|
||||||
if (r & PS_BCH) {
|
if (r & PS_BCH) {
|
||||||
// First buffer of a page, clear the parity
|
if (!(r&3))
|
||||||
if (!(r & PS_BLK))
|
|
||||||
bch4369_init(config.bch_salt);
|
bch4369_init(config.bch_salt);
|
||||||
// Add the current buffer to the parity
|
|
||||||
uint8_t *bend = bch4369_stri(flash_buffer, 64);
|
uint8_t *bend = bch4369_stri(flash_buffer, 64);
|
||||||
// Last buffer of the page:
|
if (!(~r & 3)) {
|
||||||
if (!(~r & PS_BLK)) {
|
// reuse Y=bend
|
||||||
bch4369_fini();
|
_memcopyyz(bend, bch_parity, 16);
|
||||||
valid = 0x01f;
|
pipe.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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// We still do not have all data we need
|
if (~pipe.valid & bflgs)
|
||||||
if (~valid & bflgs)
|
|
||||||
goto done;
|
goto done;
|
||||||
|
|
||||||
// The buffer is full, send it.
|
|
||||||
r |= PS_OUT;
|
|
||||||
|
|
||||||
#ifdef HAVE_FPGA
|
#ifdef HAVE_FPGA
|
||||||
// Resume the FPGA stream
|
if (pipe.dest & pipe_fpga)
|
||||||
if (dest & pipe_fpga & ~fpga_start_write())
|
fpga_start(1);
|
||||||
dest &=~ pipe_fpga;
|
|
||||||
else
|
|
||||||
#endif
|
#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:
|
done:
|
||||||
pipe.dest = dest;
|
|
||||||
pipe.valid = valid;
|
|
||||||
pipe.status = r;
|
pipe.status = r;
|
||||||
return 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)
|
||||||
{
|
{
|
||||||
/************************************************************
|
pipe = c->pipe;
|
||||||
* cmd("B0!", «fpga-cmd») optionally configure an FPGA cmd
|
if ((pipe.source | pipe.dest) & pipe_flash)
|
||||||
* cmd("P0:, «pipe-cfg») with `AS_CMD` in `.status`
|
flash_start_stream(c->page, c->npages, c->flash);
|
||||||
***********************************************************/
|
|
||||||
|
|
||||||
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)
|
if (pipe.source & pipe_adc)
|
||||||
adc_start_stream(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
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
38
src/pipe.h
38
src/pipe.h
|
|
@ -8,15 +8,10 @@ enum pipe_ports {
|
||||||
pipe_fpga = 8,
|
pipe_fpga = 8,
|
||||||
|
|
||||||
PS_OUT = 0x80,
|
PS_OUT = 0x80,
|
||||||
PS_ERR = 0x40,
|
PS_BCH = 0x10,
|
||||||
PS_BCH = 0x20,
|
PS_528 = 0x08,
|
||||||
PS_528 = 0x10,
|
PS_BLK = 0x03,
|
||||||
PS_BLK = 0x07,
|
|
||||||
PS_5 = PS_BLK | PS_528, // need 16 bytes more
|
PS_5 = PS_BLK | PS_528, // need 16 bytes more
|
||||||
|
|
||||||
AS_CONT = 1,
|
|
||||||
AS_CONFIG = SPI_CONFIG, // = 2
|
|
||||||
AS_CMD = 4,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
extern
|
extern
|
||||||
|
|
@ -28,11 +23,14 @@ struct pipe {
|
||||||
uint8_t adc;
|
uint8_t adc;
|
||||||
#ifdef HAVE_FPGA
|
#ifdef HAVE_FPGA
|
||||||
struct {
|
struct {
|
||||||
uint8_t status; // AS_…
|
uint16_t size;
|
||||||
uint16_t count; // number of cmds(read), 64 Bytes(write)
|
uint8_t cmd[4];
|
||||||
uint8_t size; // Words per cmd (read), extra Words (write), 0==256
|
uint8_t csize;
|
||||||
uint8_t val; // Valid Bytes in Buffer (read)
|
uint8_t zsize;
|
||||||
uint8_t pos; // Words read from current cmd (read)
|
uint8_t isize;
|
||||||
|
uint8_t zero;
|
||||||
|
uint8_t wait;
|
||||||
|
uint8_t mask;
|
||||||
} fpga;
|
} fpga;
|
||||||
#endif
|
#endif
|
||||||
} pipe;
|
} pipe;
|
||||||
|
|
@ -44,18 +42,6 @@ struct pipe_config {
|
||||||
uint8_t flash;
|
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();
|
uint8_t pipe_poll();
|
||||||
void pipe_cron();
|
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);
|
||||||
|
|
|
||||||
|
|
@ -31,11 +31,6 @@ ISR(SPI0_INT_vect)
|
||||||
d = spi.zero;
|
d = spi.zero;
|
||||||
}
|
}
|
||||||
else if (spi.wsize) {
|
else if (spi.wsize) {
|
||||||
// TODO:
|
|
||||||
// spi.csize = spi.wsize;
|
|
||||||
// spi.wsize = 0;
|
|
||||||
// spi.cmd = spi.wdata;
|
|
||||||
// goto repeat;
|
|
||||||
spi.wsize--;
|
spi.wsize--;
|
||||||
d = *spi.wdata++;
|
d = *spi.wdata++;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
11
src/thhor.c
11
src/thhor.c
|
|
@ -40,17 +40,8 @@ int main()
|
||||||
send_hex_byte_eol(magic.reset_source);
|
send_hex_byte_eol(magic.reset_source);
|
||||||
|
|
||||||
while (1) {
|
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();
|
sei();
|
||||||
|
sleep_cpu();
|
||||||
command();
|
command();
|
||||||
pipe_poll();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -26,10 +26,3 @@ void send_hex_long(uint32_t b)
|
||||||
|
|
||||||
extern uint8_t uart_rx_err;
|
extern uint8_t uart_rx_err;
|
||||||
extern uint8_t uart_rx_errors;
|
extern uint8_t uart_rx_errors;
|
||||||
extern volatile uint8_t uart_rx_mes;
|
|
||||||
|
|
||||||
static inline
|
|
||||||
uint8_t command_pending()
|
|
||||||
{
|
|
||||||
return uart_rx_mes;
|
|
||||||
}
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue