Compare commits
15 Commits
Author | SHA1 | Date | |
---|---|---|---|
63315d9a58 | |||
798a0f167e | |||
9506159380 | |||
89af26047e | |||
9876d92981 | |||
f4297532d0 | |||
4156a37d72 | |||
f2ae56a13e | |||
61151b4618 | |||
d6bff65d0b | |||
ea6c170023 | |||
d2b4ff089d | |||
fb21ad75e2 | |||
5e3ac61921 | |||
|
eb328612b2 |
|
@ -16,7 +16,10 @@ The EVT boards can be attached directly to the Raspberry Pi as a "hat". When bu
|
|||
|
||||
The only pins that are required are 5V, GND, CRESET, SPI_MOSI, SPI_MISO, SPI_CLK, and SPI_CS.
|
||||
|
||||
You can improve performance by attaching SPI_IO2 and SPI_IO3 and running `fomu-flash` in quad/qpi mode by specifying `-t 4` or `-t q`.
|
||||
The Pi's hardware SPI interface must be enabled in the kernel- use
|
||||
`raspi-config` or add `dtparam=spi=on` to `/boot/config.txt` and reboot before
|
||||
using. You can improve performance by attaching SPI_IO2 and SPI_IO3 and running
|
||||
`fomu-flash` in quad/qpi mode by specifying `-t 4` or `-t q`.
|
||||
|
||||
You can get serial interaction by connecting the UART pins, but they are not necessary for flashing.
|
||||
|
||||
|
|
27
fomu-flash.c
27
fomu-flash.c
|
@ -25,6 +25,8 @@
|
|||
static unsigned int F_RESET = 27;
|
||||
#define F_DONE 17
|
||||
|
||||
// #define DEBUG_ICE40_PATCH
|
||||
|
||||
static int spi_irw_readb(void *data) {
|
||||
return spiRx(data);
|
||||
}
|
||||
|
@ -185,6 +187,7 @@ int main(int argc, char **argv) {
|
|||
enum spi_type spi_type = ST_SINGLE;
|
||||
struct irw_file *replacement_rom = NULL;
|
||||
|
||||
#ifndef DEBUG_ICE40_PATCH
|
||||
if (gpioInitialise() < 0) {
|
||||
fprintf(stderr, "Unable to initialize GPIO\n");
|
||||
return 1;
|
||||
|
@ -195,6 +198,7 @@ int main(int argc, char **argv) {
|
|||
// original had it as BCM 21.
|
||||
if ((gpioHardwareRevision() == 2) || (gpioHardwareRevision() == 3))
|
||||
F_RESET = 21;
|
||||
#endif
|
||||
|
||||
spi = spiAlloc();
|
||||
fpga = fpgaAlloc();
|
||||
|
@ -361,6 +365,7 @@ int main(int argc, char **argv) {
|
|||
return 1;
|
||||
}
|
||||
|
||||
#ifndef DEBUG_ICE40_PATCH
|
||||
spiInit(spi);
|
||||
fpgaInit(fpga);
|
||||
|
||||
|
@ -369,6 +374,16 @@ int main(int argc, char **argv) {
|
|||
|
||||
if (spi_flash_bytes != -1)
|
||||
spiOverrideSize(spi, spi_flash_bytes);
|
||||
#else
|
||||
if (op != OP_FPGA_BOOT) {
|
||||
printf("DEBUG_ICE40_PATCH requires you load a bitstream with '-f'\n");
|
||||
return 9;
|
||||
}
|
||||
if (!replacement_rom) {
|
||||
printf("DEBUG_ICE40_PATCH requires you load a replacement rom with '-l'\n");
|
||||
return 10;
|
||||
}
|
||||
#endif
|
||||
|
||||
switch (op) {
|
||||
case OP_SPI_ID: {
|
||||
|
@ -453,7 +468,7 @@ int main(int argc, char **argv) {
|
|||
break;
|
||||
}
|
||||
close(fd);
|
||||
spiWrite(spi, 0, bfr, stat.st_size);
|
||||
ret += spiWrite(spi, 0, bfr, stat.st_size);
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -502,21 +517,25 @@ offset, file_src[offset], spi_src[offset]);
|
|||
|
||||
case OP_FPGA_BOOT: {
|
||||
int count;
|
||||
#ifndef DEBUG_ICE40_PATCH
|
||||
spiHold(spi);
|
||||
spiSwapTxRx(spi);
|
||||
fpgaResetSlave(fpga);
|
||||
|
||||
fprintf(stderr, "FPGA Done? %d\n", fpgaDone(fpga));
|
||||
|
||||
spiBegin(spi);
|
||||
|
||||
#endif
|
||||
if (replacement_rom) {
|
||||
IRW_FILE *bitstream = irw_open(op_filename, "r");
|
||||
if (!bitstream) {
|
||||
perror("unable to open fpga bitstream");
|
||||
break;
|
||||
}
|
||||
#ifdef DEBUG_ICE40_PATCH
|
||||
IRW_FILE *spidev = irw_open("foboot-patched.bin", "w");
|
||||
return ice40_patch(bitstream, replacement_rom, spidev, 8192);
|
||||
#else
|
||||
IRW_FILE *spidev = irw_open_fake(spi, spi_irw_readb, spi_irw_writeb);
|
||||
#endif
|
||||
ice40_patch(bitstream, replacement_rom, spidev, 8192);
|
||||
}
|
||||
else {
|
||||
|
|
188
ice40.c
188
ice40.c
|
@ -3,7 +3,41 @@
|
|||
#include "ice40.h"
|
||||
|
||||
#define MAX(x, y) (x) > (y) ? (x) : (y)
|
||||
#define ARRAY_SIZE(x) ((sizeof(x) / sizeof(*x)))
|
||||
|
||||
#define DEBUG_PRINT(...)
|
||||
// #define DEBUG_PRINT(...) printf(__VA_ARGS__)
|
||||
// #define SCAN_DEBUG
|
||||
|
||||
// Make this a macro so line numbers work correctly
|
||||
#define assert_words_equal(check_word, old_word) \
|
||||
if (check_word != old_word) { \
|
||||
int j; \
|
||||
printf("mismatch in source stream!\n"); \
|
||||
printf("old_word: %04x check_word: %04x\n", old_word, check_word); \
|
||||
printf("rand:"); \
|
||||
for (j = (offset + mapping) - 16; j < (offset + mapping) + 16; j++) { \
|
||||
if (j == (offset + mapping)) \
|
||||
printf(" [%04x]", ora16[j]); \
|
||||
else \
|
||||
printf(" %04x", ora16[j]); \
|
||||
}\
|
||||
printf("\n"); \
|
||||
\
|
||||
printf(" rom:");\
|
||||
for (j = i - 16; j < i + 16; j++) {\
|
||||
printf(" %04x", oro16[j]);\
|
||||
}\
|
||||
printf("\n");\
|
||||
printf("if possible, please email the rom and bitstream to sean@xobs.io\n"); \
|
||||
} \
|
||||
assert(check_word == old_word)
|
||||
|
||||
#ifdef SCAN_DEBUG
|
||||
#define SCAN_DEBUG_PRINT(...) printf(__VA_ARGS__)
|
||||
#else
|
||||
#define SCAN_DEBUG_PRINT(...)
|
||||
#endif
|
||||
|
||||
struct Ice40Bitstream
|
||||
{
|
||||
|
@ -166,7 +200,6 @@ int ice40_patch(struct irw_file *f, struct irw_file *rom,
|
|||
uint16_t *ora16 = (uint16_t *)output_rand;
|
||||
uint16_t *oro16 = (uint16_t *)output_rom;
|
||||
unsigned int ora_ptr = 0;
|
||||
unsigned int ora_matches = 0;
|
||||
unsigned int input_ptr;
|
||||
int b;
|
||||
int errors = 0;
|
||||
|
@ -284,44 +317,136 @@ int ice40_patch(struct irw_file *f, struct irw_file *rom,
|
|||
(bs.current_width * bs.current_height) / 8);
|
||||
bs.bram_width = MAX(bs.bram_width, bs.current_width);
|
||||
bs.bram_height = MAX(bs.bram_height, bs.current_height);
|
||||
for (i = 0; i < ((bs.current_width * bs.current_height) / 8); i += 2)
|
||||
{
|
||||
uint16_t word =
|
||||
((irw_readb(f) << 8) & 0x0000ff00)
|
||||
|
|
||||
((irw_readb(f) << 0) & 0x000000ff)
|
||||
;
|
||||
int i;
|
||||
ora_ptr = 16 * bs.current_offset;
|
||||
|
||||
if (word) {
|
||||
int found = 0;
|
||||
int start = ora_ptr - 64;
|
||||
int end = ora_ptr + 64;
|
||||
if (start < 0)
|
||||
start = 0;
|
||||
if (end > 8192)
|
||||
end = 8192;
|
||||
for (i = start; i < end; i++) {
|
||||
if (ora16[i] == word) {
|
||||
found = 1;
|
||||
word = oro16[i];
|
||||
ora_matches++;
|
||||
ora_ptr = i;
|
||||
break;
|
||||
// Step 1: Find a mapping by scanning through the first 128 words looking for patterns.
|
||||
uint16_t scan_buffer[128];
|
||||
assert(((bs.current_width * bs.current_height)/8)*2 > 128);
|
||||
for (i = 0; i < ARRAY_SIZE(scan_buffer); i++) {
|
||||
scan_buffer[i] = ((irw_readb(f) << 8) & 0xff00) | ((irw_readb(f) << 0) & 0x00ff);
|
||||
}
|
||||
|
||||
#ifdef SCAN_DEBUG
|
||||
SCAN_DEBUG_PRINT("scan:");
|
||||
for (i = 0; i < ARRAY_SIZE(scan_buffer); i++) {
|
||||
SCAN_DEBUG_PRINT(" %04x", scan_buffer[i]);
|
||||
}
|
||||
SCAN_DEBUG_PRINT("\n");
|
||||
|
||||
SCAN_DEBUG_PRINT("rand:");
|
||||
for (i = 0; i < ARRAY_SIZE(scan_buffer); i++) {
|
||||
SCAN_DEBUG_PRINT(" %04x", ora16[ora_ptr + i]);
|
||||
}
|
||||
SCAN_DEBUG_PRINT("\n");
|
||||
|
||||
SCAN_DEBUG_PRINT(" rom:");
|
||||
for (i = 0; i < ARRAY_SIZE(scan_buffer); i++) {
|
||||
SCAN_DEBUG_PRINT(" %04x", oro16[ora_ptr + i]);
|
||||
}
|
||||
SCAN_DEBUG_PRINT("\n");
|
||||
#endif /* SCAN_DEBUG */
|
||||
|
||||
int outer_word = 0;
|
||||
static struct {
|
||||
int bitstream;
|
||||
int random;
|
||||
int stride;
|
||||
} word_mappings[16];
|
||||
int word_stride = -1;
|
||||
for (outer_word = 0; outer_word < 16; outer_word++) {
|
||||
int inner_word;
|
||||
word_mappings[outer_word].bitstream = -1;
|
||||
word_mappings[outer_word].random = -1;
|
||||
word_mappings[outer_word].stride = -1;
|
||||
for (inner_word = 0; inner_word < 16; inner_word++) {
|
||||
// We have a candidate offset. Figure out what its stride is,
|
||||
// and validate that we have multiple matches.
|
||||
if (scan_buffer[outer_word] == ora16[ora_ptr + inner_word]) {
|
||||
SCAN_DEBUG_PRINT("Candidate %04x @ %d/%d\n", scan_buffer[outer_word], outer_word, inner_word);
|
||||
int scan_offset = 0;
|
||||
for (scan_offset = 0; scan_offset < 30; scan_offset++) {
|
||||
if ((scan_buffer[outer_word + scan_offset] == ora16[ora_ptr + inner_word + 16])
|
||||
&& (scan_buffer[outer_word + (scan_offset*2)] == ora16[ora_ptr + inner_word + 32])) {
|
||||
SCAN_DEBUG_PRINT("Scan offset: %d\n", scan_offset);
|
||||
word_mappings[outer_word].bitstream = outer_word;
|
||||
word_mappings[outer_word].random = inner_word;
|
||||
word_mappings[outer_word].stride = scan_offset;
|
||||
}
|
||||
}
|
||||
if (!found) {
|
||||
printf("couldn't find word %04x (ora_ptr: %d matches: %d)\n", word, ora_ptr, ora_matches);
|
||||
errors++;
|
||||
}
|
||||
}
|
||||
}
|
||||
for (i = 0; i < ARRAY_SIZE(word_mappings); i++) {
|
||||
if (word_mappings[i].stride != -1) {
|
||||
if (word_stride != -1) {
|
||||
if (word_mappings[i].stride != word_stride) {
|
||||
printf("This stride is different (%d vs expected %d)\n", word_mappings[i].stride, word_stride);
|
||||
}
|
||||
}
|
||||
word_stride = word_mappings[i].stride;
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef SCAN_DEBUG
|
||||
for (outer_word = 0; outer_word < ARRAY_SIZE(word_mappings); outer_word++) {
|
||||
SCAN_DEBUG_PRINT("word_mappings[%2d]: bitstream: %2d random: %2d stride: %2d\n",
|
||||
outer_word, word_mappings[outer_word].bitstream,
|
||||
word_mappings[outer_word].random, word_mappings[outer_word].stride);
|
||||
}
|
||||
#endif /* SCAN_DEBUG */
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(scan_buffer); i++) {
|
||||
int offset = -1;
|
||||
int mapping = -1;
|
||||
if (word_stride != -1) {
|
||||
offset = (i / word_stride) * 16;
|
||||
mapping = word_mappings[i % word_stride].random;
|
||||
}
|
||||
uint16_t old_word = scan_buffer[i];
|
||||
uint16_t check_word;
|
||||
uint16_t new_word;
|
||||
if (mapping == -1) {
|
||||
new_word = check_word = old_word;
|
||||
}
|
||||
else {
|
||||
ora_ptr++;
|
||||
// DEBUG_PRINT("word is 0, ptr is %d\n", ora_ptr);
|
||||
check_word = ora16[ora_ptr + offset + mapping];
|
||||
new_word = oro16[ora_ptr + offset + mapping];
|
||||
}
|
||||
irw_writeb(o, word >> 8);
|
||||
irw_writeb(o, word);
|
||||
irw_writeb(o, new_word >> 8);
|
||||
irw_writeb(o, new_word);
|
||||
SCAN_DEBUG_PRINT("%4d %4d %2d %2d %04x <-> %04x -> %04x\n", i, sizeof(scan_buffer), offset, mapping, old_word, check_word, new_word);
|
||||
assert_words_equal(check_word, old_word);
|
||||
}
|
||||
SCAN_DEBUG_PRINT("---\n");
|
||||
|
||||
// Finish reading the page
|
||||
for (i = ARRAY_SIZE(scan_buffer); i < ((bs.current_width * bs.current_height) / 8)/2; i++) {
|
||||
int offset = -1;
|
||||
int mapping = -1;
|
||||
if (word_stride != -1) {
|
||||
offset = (i / word_stride) * 16;
|
||||
mapping = word_mappings[i % word_stride].random;
|
||||
}
|
||||
uint16_t old_word =
|
||||
((irw_readb(f) << 8) & 0xff00)
|
||||
|
|
||||
((irw_readb(f) << 0) & 0x00ff)
|
||||
;
|
||||
uint16_t check_word;
|
||||
uint16_t new_word;
|
||||
if (mapping == -1) {
|
||||
new_word = check_word = old_word;
|
||||
}
|
||||
else {
|
||||
check_word = ora16[ora_ptr + offset + mapping];
|
||||
new_word = oro16[ora_ptr + offset + mapping];
|
||||
}
|
||||
irw_writeb(o, new_word >> 8);
|
||||
irw_writeb(o, new_word);
|
||||
SCAN_DEBUG_PRINT("%4d %4d %2d %2d %04x <-> %04x -> %04x\n", i, sizeof(scan_buffer), offset, mapping, old_word, check_word, new_word);
|
||||
assert_words_equal(check_word, old_word);
|
||||
}
|
||||
|
||||
last0 = irw_readb(f);
|
||||
last1 = irw_readb(f);
|
||||
if (last0 || last1)
|
||||
|
@ -353,7 +478,6 @@ int ice40_patch(struct irw_file *f, struct irw_file *rom,
|
|||
case 1:
|
||||
bs.current_bank = payload;
|
||||
ora_ptr = 0;
|
||||
ora_matches = 0;
|
||||
// printf("setting bank number to %d\n", bs.current_bank);
|
||||
break;
|
||||
|
||||
|
|
Loading…
Reference in New Issue
Block a user