Compare commits

...

18 Commits
v1.1 ... master

Author SHA1 Message Date
Sean Cross 63315d9a58 fomu-flash: exit nonzero on write error
Signed-off-by: Sean Cross <sean@xobs.io>
2021-04-14 14:11:06 +08:00
Sean Cross 798a0f167e ice40: hardcode ora_ptr
The previous approach of attempting to derive it didn't work.

It turns out that the ora_ptr is 16 * the current offset.

Signed-off-by: Sean Cross <sean@xobs.io>
2019-02-26 15:12:45 +08:00
Sean Cross 9506159380 fomu-flash: disable ICE40_DEBUG_PATCH
Since we've got ice40 working, we don't need to debug it anymore.

Signed-off-by: Sean Cross <sean@xobs.io>
2019-02-26 14:49:11 +08:00
Sean Cross 89af26047e ice40: rewrite matching heuristic to increase reliability
Instead of looping through a small window, figure out the various bit
positions and offsets.  Then, as we scan through, validate that we've
found the proper offsets.

This should get us to 100% reliability.

Signed-off-by: Sean Cross <sean@xobs.io>
2019-02-26 14:48:05 +08:00
Sean Cross 9876d92981 fpga: convert tabs to spaces
Signed-off-by: Sean Cross <sean@xobs.io>
2019-02-26 13:10:19 +08:00
Sean Cross f4297532d0 fomu-flash: make DEBUG_ICE40_PATCH work better
Signed-off-by: Sean Cross <sean@xobs.io>
2019-02-26 13:08:13 +08:00
Sean Cross 4156a37d72 fomu-flash: convert tabs to spaces
Signed-off-by: Sean Cross <sean@xobs.io>
2019-02-26 13:03:09 +08:00
Sean Cross f2ae56a13e ice40: convert tabs to spaces
Signed-off-by: Sean Cross <sean@xobs.io>
2019-02-26 13:02:41 +08:00
Sean Cross 61151b4618 fomu-flash: add debug hack to fix ice40 stuff
This will write data to a file, rather than to SPI.

Signed-off-by: Sean Cross <sean@xobs.io>
2019-02-26 13:01:04 +08:00
Sean Cross d6bff65d0b Merge branch 'master' of github.com:im-tomu/fomu-flash 2019-02-25 22:17:38 +08:00
Sean Cross ea6c170023 ice40: lower fuzz size to improve patcher reliability
The fuzz size should be made smaller, because we're getting too many false positives.

Signed-off-by: Sean Cross <sean@xobs.io>
2019-02-25 22:16:31 +08:00
Sean Cross d2b4ff089d ice40: convert from dos to unix line endings
Signed-off-by: Sean Cross <sean@xobs.io>
2019-02-25 18:38:53 +08:00
Sean Cross fb21ad75e2
Merge pull request #3 from tari/hardware-spi
Document the requirement for hardware SPI
2019-02-25 17:11:10 +08:00
Sean Cross 5e3ac61921
Merge branch 'master' into hardware-spi 2019-02-25 17:10:11 +08:00
Sean Cross 7fb6e4bb7b README: mention the existence of the `-l` command
Signed-off-by: Sean Cross <sean@xobs.io>
2019-02-25 13:56:22 +08:00
Sean Cross bcbc7319c1 fomu-flash: add `-l (rom)` option to load a rom
This can be used to quickly develop a bootloader, or other boot
rom for an ICE40.

Signed-off-by: Sean Cross <sean@xobs.io>
2019-02-25 13:51:27 +08:00
Sean Cross 4ca2218403 ice40: experimental rom patching support
We can now do away with the `iceunpack`/`icebram`/`icepack` dance and
simply patch files on the fly.  This should simplify development,
since you only need two files (the bitstream and the replacement rom),
rather than four files (the bitstream, the replacement rom, the
random contents, and the output file).

Signed-off-by: Sean Cross <sean@xobs.io>
2019-02-25 13:49:38 +08:00
Peter Marheine eb328612b2 Document the requirement for hardware SPI
I spent a while fighting with my hardware before working this out- if
SPI is disabled in the kernel reads will return all 1s which is very
confusing and looks more like something is wired up wrong when it's
actually correct.
2019-02-25 14:56:02 +11:00
5 changed files with 1189 additions and 481 deletions

View File

@ -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.
@ -55,4 +58,67 @@ You can verify the SPI flash was programmed with the `-v` command:
## Checking SPI Flash was Written
You can "peek" at 256 bytes of SPI with `-p [offset]`. This can be used to quickly verify that something was written.
You can "peek" at 256 bytes of SPI with `-p [offset]`. This can be used to quickly verify that something was written.
## Patching ROM
`fomu-flash` supports patching ROM. To do this, you must synthesize your bitstream with a fixed random ROM contents. This is so `fomu-flash` has something to look for.
The Python code for this would look like:
```python
def xorshift32(x):
x = x ^ (x << 13) & 0xffffffff
x = x ^ (x >> 17) & 0xffffffff
x = x ^ (x << 5) & 0xffffffff
return x & 0xffffffff
def get_rand(x):
out = 0
for i in range(32):
x = xorshift32(x)
if (x & 1) == 1:
out = out | (1 << i)
return out & 0xffffffff
def get_bit(x):
return (256 * (x & 7)) + (x >> 3)
```
And the corresponding C code looks like:
```c
uint32_t xorshift32(uint32_t x)
{
/* Algorithm "xor" from p. 4 of Marsaglia, "Xorshift RNGs" */
x = x ^ (x << 13);
x = x ^ (x >> 17);
x = x ^ (x << 5);
return x;
}
uint32_t get_rand(uint32_t x) {
uint32_t out = 0;
int i;
for (i = 0; i < 32; i++) {
x = xorshift32(x);
if ((x & 1) == 1)
out = out | (1 << i);
}
return out;
}
static uint32_t fill_rand(uint32_t *bfr, int count) {
int i;
uint32_t last = 1;
for (i = 0; i < count / 4; i++) {
last = get_rand(last);
bfr[i] = last;
}
return i;
}
```
Currently, `fomu-flash` only supports 8192-byte ROMs, though there is no reason why it can't be extended to other sizes.
Specify a ROM to load on the command line with `-l`.

View File

@ -10,6 +10,7 @@
#include "rpi.h"
#include "spi.h"
#include "fpga.h"
#include "ice40.h"
#define S_MOSI 10
#define S_MISO 9
@ -24,508 +25,559 @@
static unsigned int F_RESET = 27;
#define F_DONE 17
// #define DEBUG_ICE40_PATCH
static int spi_irw_readb(void *data) {
return spiRx(data);
}
static int spi_irw_writeb(void *data, uint8_t b) {
spiTx(data, b);
return b;
}
static inline int isprint(int c)
{
return c > 32 && c < 127;
return c > 32 && c < 127;
}
int print_hex_offset(FILE *stream,
const void *block, int count, int offset, uint32_t start)
{
int byte;
const uint8_t *b = block;
int byte;
const uint8_t *b = block;
count += offset;
b -= offset;
for ( ; offset < count; offset += 16) {
fprintf(stream, "%08x", start + offset);
count += offset;
b -= offset;
for ( ; offset < count; offset += 16) {
fprintf(stream, "%08x", start + offset);
for (byte = 0; byte < 16; byte++) {
if (byte == 8)
fprintf(stream, " ");
fprintf(stream, " ");
if (offset + byte < count)
fprintf(stream, "%02x", b[offset + byte] & 0xff);
else
fprintf(stream, " ");
}
for (byte = 0; byte < 16; byte++) {
if (byte == 8)
fprintf(stream, " ");
fprintf(stream, " ");
if (offset + byte < count)
fprintf(stream, "%02x", b[offset + byte] & 0xff);
else
fprintf(stream, " ");
}
fprintf(stream, " |");
for (byte = 0; byte < 16 && byte + offset < count; byte++)
fprintf(stream, "%c", isprint(b[offset + byte]) ? b[offset + byte] : '.');
fprintf(stream, "|\r\n");
}
return 0;
fprintf(stream, " |");
for (byte = 0; byte < 16 && byte + offset < count; byte++)
fprintf(stream, "%c", isprint(b[offset + byte]) ? b[offset + byte] : '.');
fprintf(stream, "|\r\n");
}
return 0;
}
int print_hex(const void *block, int count, uint32_t start)
{
FILE *stream = stdout;
return print_hex_offset(stream, block, count, 0, start);
FILE *stream = stdout;
return print_hex_offset(stream, block, count, 0, start);
}
enum op {
OP_SPI_READ,
OP_SPI_WRITE,
OP_SPI_VERIFY,
OP_SPI_PEEK,
OP_SPI_ID,
OP_SPI_SECURITY_READ,
OP_SPI_SECURITY_WRITE,
OP_FPGA_BOOT,
OP_FPGA_RESET,
OP_UNKNOWN,
OP_SPI_READ,
OP_SPI_WRITE,
OP_SPI_VERIFY,
OP_SPI_PEEK,
OP_SPI_ID,
OP_SPI_SECURITY_READ,
OP_SPI_SECURITY_WRITE,
OP_FPGA_BOOT,
OP_FPGA_RESET,
OP_UNKNOWN,
};
static int pinspec_to_pinname(char code) {
switch (code) {
case '0': return SP_D0;
case '1': return SP_D1;
case '2': return SP_D2;
case '3': return SP_D3;
case 'o': return SP_MOSI;
case 'i': return SP_MISO;
case 'w': return SP_WP;
case 'h': return SP_HOLD;
case 'c': return SP_CLK;
case 's': return SP_CS;
case 'r': return FP_RESET;
case 'd': return FP_DONE;
default: return -1;
}
switch (code) {
case '0': return SP_D0;
case '1': return SP_D1;
case '2': return SP_D2;
case '3': return SP_D3;
case 'o': return SP_MOSI;
case 'i': return SP_MISO;
case 'w': return SP_WP;
case 'h': return SP_HOLD;
case 'c': return SP_CLK;
case 's': return SP_CS;
case 'r': return FP_RESET;
case 'd': return FP_DONE;
default: return -1;
}
}
static int print_pinspec(FILE *stream) {
fprintf(stream, "Pinspec:\n");
fprintf(stream, " Name Description Default (BCM pin number)\n");
fprintf(stream, " 0 SPI D0 %d\n", S_D0);
fprintf(stream, " 1 SPI D1 %d\n", S_D1);
fprintf(stream, " 2 SPI D2 %d\n", S_D2);
fprintf(stream, " 3 SPI D3 %d\n", S_D3);
fprintf(stream, " o SPI MOSI %d\n", S_MOSI);
fprintf(stream, " i SPI MISO %d\n", S_MISO);
fprintf(stream, " w SPI WP %d\n", S_WP);
fprintf(stream, " h SPI HOLD %d\n", S_HOLD);
fprintf(stream, " c SPI CLK %d\n", S_CLK);
fprintf(stream, " s SPI CS %d\n", S_CE0);
fprintf(stream, " r FPGA Reset %d\n", F_RESET);
fprintf(stream, " d FPGA Done %d\n", F_DONE);
fprintf(stream, "For example: -g i:23 or -g d:27\n");
return 0;
fprintf(stream, "Pinspec:\n");
fprintf(stream, " Name Description Default (BCM pin number)\n");
fprintf(stream, " 0 SPI D0 %d\n", S_D0);
fprintf(stream, " 1 SPI D1 %d\n", S_D1);
fprintf(stream, " 2 SPI D2 %d\n", S_D2);
fprintf(stream, " 3 SPI D3 %d\n", S_D3);
fprintf(stream, " o SPI MOSI %d\n", S_MOSI);
fprintf(stream, " i SPI MISO %d\n", S_MISO);
fprintf(stream, " w SPI WP %d\n", S_WP);
fprintf(stream, " h SPI HOLD %d\n", S_HOLD);
fprintf(stream, " c SPI CLK %d\n", S_CLK);
fprintf(stream, " s SPI CS %d\n", S_CE0);
fprintf(stream, " r FPGA Reset %d\n", F_RESET);
fprintf(stream, " d FPGA Done %d\n", F_DONE);
fprintf(stream, "For example: -g i:23 or -g d:27\n");
return 0;
}
static int print_program_modes(FILE *stream) {
fprintf(stream, " -h This help page\n");
fprintf(stream, " -r Reset the FPGA and have it boot from SPI\n");
fprintf(stream, " -i Print out the SPI ID code\n");
fprintf(stream, " -p offset Peek at 256 bytes of SPI flash at the specified offset\n");
fprintf(stream, " -f bin Load this binary directly into the FPGA\n");
fprintf(stream, " -w bin Write this binary into the SPI flash chip\n");
fprintf(stream, " -v bin Verify the SPI flash contains this data\n");
fprintf(stream, " -s out Save the SPI flash contents to this file\n");
fprintf(stream, " -k n[:f] Read security register [n], or update it with the contents of file [f]\n");
return 0;
fprintf(stream, " -h This help page\n");
fprintf(stream, " -r Reset the FPGA and have it boot from SPI\n");
fprintf(stream, " -i Print out the SPI ID code\n");
fprintf(stream, " -p offset Peek at 256 bytes of SPI flash at the specified offset\n");
fprintf(stream, " -f bin Load this bitstream directly into the FPGA\n");
fprintf(stream, " -l rom Replace the ROM in the bitstream with this file\n");
fprintf(stream, " -w bin Write this binary into the SPI flash chip\n");
fprintf(stream, " -v bin Verify the SPI flash contains this data\n");
fprintf(stream, " -s out Save the SPI flash contents to this file\n");
fprintf(stream, " -k n[:f] Read security register [n], or update it with the contents of file [f]\n");
return 0;
}
static int print_help(FILE *stream, const char *progname) {
fprintf(stream, "Fomu Raspberry Pi Flash Utilities\n");
fprintf(stream, "Usage:\n");
fprintf(stream, "%15s (-[hri] | [-p offset] | [-f bin] | [-w bin] | [-v bin] | [-s out] | [-k n[:f]])\n", progname);
fprintf(stream, " [-g pinspec] [-t spitype] [-b bytes]\n");
fprintf(stream, "Program mode (pick one):\n");
print_program_modes(stream);
fprintf(stream, "Configuration options:\n");
fprintf(stream, " -g ps Set the pin assignment with the given pinspec\n");
fprintf(stream, " -t type Set the number of bits to use for SPI (1, 2, 4, or Q)\n");
fprintf(stream, " -b bytes Override the size of the SPI flash, in bytes\n");
fprintf(stream, "You can remap various pins with -g. The format is [name]:[number].\n");
fprintf(stream, "\n");
fprintf(stream, "The width of SPI can be set with 't [width]'. Valid widths are:\n");
fprintf(stream, " 1 - standard 1-bit spi\n");
fprintf(stream, " 2 - standard 2-bit spi\n");
fprintf(stream, " 4 - standard 4-bit spi (with 1-bit commands)\n");
fprintf(stream, " q - 4-bit qspi (with 4-bit commands)\n");
fprintf(stream, "\n");
print_pinspec(stream);
return 0;
fprintf(stream, "Fomu Raspberry Pi Flash Utilities\n");
fprintf(stream, "Usage:\n");
fprintf(stream, "%15s (-[hri] | [-p offset] | [-f bitstream] | \n", progname);
fprintf(stream, "%15s [-w bin] | [-v bin] | [-s out] | [-k n[:f]])\n", "");
fprintf(stream, " [-g pinspec] [-t spitype] [-b bytes]\n");
fprintf(stream, "\n");
fprintf(stream, "Program mode (pick one):\n");
print_program_modes(stream);
fprintf(stream, "\n");
fprintf(stream, "Configuration options:\n");
fprintf(stream, " -g ps Set the pin assignment with the given pinspec\n");
fprintf(stream, " -t type Set the number of bits to use for SPI (1, 2, 4, or Q)\n");
fprintf(stream, " -b bytes Override the size of the SPI flash, in bytes\n");
fprintf(stream, "You can remap various pins with -g. The format is [name]:[number].\n");
fprintf(stream, "\n");
fprintf(stream, "The width of SPI can be set with 't [width]'. Valid widths are:\n");
fprintf(stream, " 1 - standard 1-bit spi\n");
fprintf(stream, " 2 - standard 2-bit spi\n");
fprintf(stream, " 4 - standard 4-bit spi (with 1-bit commands)\n");
fprintf(stream, " q - 4-bit qspi (with 4-bit commands)\n");
fprintf(stream, "\n");
print_pinspec(stream);
return 0;
}
static int print_usage_error(FILE *stream) {
fprintf(stream, "Error: You must only specify one program mode:\n");
print_program_modes(stream);
return 1;
fprintf(stream, "Error: You must only specify one program mode:\n");
print_program_modes(stream);
return 1;
}
int main(int argc, char **argv) {
int opt;
int fd;
char *op_filename = NULL;
struct ff_spi *spi;
struct ff_fpga *fpga;
int peek_offset = 0;
int spi_flash_bytes = -1;
uint8_t security_reg;
uint8_t security_val[256];
enum op op = OP_UNKNOWN;
enum spi_type spi_type = ST_SINGLE;
int opt;
int fd;
char *op_filename = NULL;
struct ff_spi *spi;
struct ff_fpga *fpga;
int peek_offset = 0;
int spi_flash_bytes = -1;
uint8_t security_reg;
uint8_t security_val[256];
enum op op = OP_UNKNOWN;
enum spi_type spi_type = ST_SINGLE;
struct irw_file *replacement_rom = NULL;
if (gpioInitialise() < 0) {
fprintf(stderr, "Unable to initialize GPIO\n");
return 1;
}
#ifndef DEBUG_ICE40_PATCH
if (gpioInitialise() < 0) {
fprintf(stderr, "Unable to initialize GPIO\n");
return 1;
}
// The original Raspberry Pi boards had a different assignment
// of pin 13. All other boards assign it to BCM 27, but the
// original had it as BCM 21.
if ((gpioHardwareRevision() == 2) || (gpioHardwareRevision() == 3))
F_RESET = 21;
// The original Raspberry Pi boards had a different assignment
// of pin 13. All other boards assign it to BCM 27, but the
// original had it as BCM 21.
if ((gpioHardwareRevision() == 2) || (gpioHardwareRevision() == 3))
F_RESET = 21;
#endif
spi = spiAlloc();
fpga = fpgaAlloc();
spi = spiAlloc();
fpga = fpgaAlloc();
spiSetPin(spi, SP_CLK, S_CLK);
spiSetPin(spi, SP_D0, S_D0);
spiSetPin(spi, SP_D1, S_D1);
spiSetPin(spi, SP_D2, S_D2);
spiSetPin(spi, SP_D3, S_D3);
spiSetPin(spi, SP_MISO, S_MISO);
spiSetPin(spi, SP_MOSI, S_MOSI);
spiSetPin(spi, SP_HOLD, S_HOLD);
spiSetPin(spi, SP_WP, S_WP);
spiSetPin(spi, SP_CS, S_CE0);
spiSetPin(spi, SP_CLK, S_CLK);
spiSetPin(spi, SP_D0, S_D0);
spiSetPin(spi, SP_D1, S_D1);
spiSetPin(spi, SP_D2, S_D2);
spiSetPin(spi, SP_D3, S_D3);
spiSetPin(spi, SP_MISO, S_MISO);
spiSetPin(spi, SP_MOSI, S_MOSI);
spiSetPin(spi, SP_HOLD, S_HOLD);
spiSetPin(spi, SP_WP, S_WP);
spiSetPin(spi, SP_CS, S_CE0);
fpgaSetPin(fpga, FP_RESET, F_RESET);
fpgaSetPin(fpga, FP_DONE, F_DONE);
fpgaSetPin(fpga, FP_CS, S_CE0);
fpgaSetPin(fpga, FP_RESET, F_RESET);
fpgaSetPin(fpga, FP_DONE, F_DONE);
fpgaSetPin(fpga, FP_CS, S_CE0);
while ((opt = getopt(argc, argv, "hip:rf:b:w:s:2:3:v:g:t:k:")) != -1) {
switch (opt) {
while ((opt = getopt(argc, argv, "hip:rf:b:w:s:2:3:v:g:t:k:l:")) != -1) {
switch (opt) {
case 'r':
if (op != OP_UNKNOWN)
return print_usage_error(stdout);
op = OP_FPGA_RESET;
break;
case 'r':
if (op != OP_UNKNOWN)
return print_usage_error(stdout);
op = OP_FPGA_RESET;
break;
case 'b':
spi_flash_bytes = strtoul(optarg, NULL, 0);
break;
case 'b':
spi_flash_bytes = strtoul(optarg, NULL, 0);
break;
case 'k': {
if (op != OP_UNKNOWN)
return print_usage_error(stdout);
char *security_filename = strchr(optarg, ':');
case 'l':
replacement_rom = irw_open(optarg, "r");
if (!replacement_rom) {
perror("couldn't open replacement rom file");
return 10;
}
break;
security_reg = strtoul(optarg, NULL, 0);
if (security_filename) {
security_filename++;
op = OP_SPI_SECURITY_WRITE;
int fd;
fd = open(security_filename, O_RDONLY);
if (fd == -1) {
perror("couldn't open security file");
return 1;
}
memset(security_val, 0, sizeof(security_val));
if (-1 == read(fd, security_val, sizeof(security_val))) {
perror("couldn't read from security file");
return 2;
}
close(fd);
}
else {
op = OP_SPI_SECURITY_READ;
}
break;
}
case 'k': {
if (op != OP_UNKNOWN)
return print_usage_error(stdout);
char *security_filename = strchr(optarg, ':');
case 'i':
if (op != OP_UNKNOWN)
return print_usage_error(stdout);
op = OP_SPI_ID;
break;
security_reg = strtoul(optarg, NULL, 0);
if (security_filename) {
security_filename++;
op = OP_SPI_SECURITY_WRITE;
int fd;
fd = open(security_filename, O_RDONLY);
if (fd == -1) {
perror("couldn't open security file");
return 1;
}
memset(security_val, 0, sizeof(security_val));
if (-1 == read(fd, security_val, sizeof(security_val))) {
perror("couldn't read from security file");
return 2;
}
close(fd);
}
else {
op = OP_SPI_SECURITY_READ;
}
break;
}
case 'p':
if (op != OP_UNKNOWN)
return print_usage_error(stdout);
op = OP_SPI_PEEK;
peek_offset = strtoul(optarg, NULL, 0);
break;
case 'i':
if (op != OP_UNKNOWN)
return print_usage_error(stdout);
op = OP_SPI_ID;
break;
case 't':
switch (*optarg) {
case '1':
spi_type = ST_SINGLE;
break;
case '2':
spi_type = ST_DUAL;
break;
case '4':
spi_type = ST_QUAD;
break;
case 'q':
spi_type = ST_QPI;
break;
default:
fprintf(stderr, "Unrecognized SPI speed '%c'. Valid types are: 1, 2, 4, or q\n", *optarg);
return 1;
}
break;
case 'p':
if (op != OP_UNKNOWN)
return print_usage_error(stdout);
op = OP_SPI_PEEK;
peek_offset = strtoul(optarg, NULL, 0);
break;
case 'g':
if ((optarg[0] == '\0') || (optarg[1] != ':')) {
fprintf(stderr, "-g requires a pinspec. Usage:\n");
print_pinspec(stderr);
return 1;
}
case 't':
switch (*optarg) {
case '1':
spi_type = ST_SINGLE;
break;
case '2':
spi_type = ST_DUAL;
break;
case '4':
spi_type = ST_QUAD;
break;
case 'q':
spi_type = ST_QPI;
break;
default:
fprintf(stderr, "Unrecognized SPI speed '%c'. Valid types are: 1, 2, 4, or q\n", *optarg);
return 1;
}
break;
spiSetPin(spi, pinspec_to_pinname(optarg[0]), strtoul(optarg+2, NULL, 0));
break;
case 'g':
if ((optarg[0] == '\0') || (optarg[1] != ':')) {
fprintf(stderr, "-g requires a pinspec. Usage:\n");
print_pinspec(stderr);
return 1;
}
case '2':
spiSetPin(spi, SP_D2, strtoul(optarg, NULL, 0));
break;
spiSetPin(spi, pinspec_to_pinname(optarg[0]), strtoul(optarg+2, NULL, 0));
break;
case '3':
spiSetPin(spi, SP_D3, strtoul(optarg, NULL, 0));
break;
case '2':
spiSetPin(spi, SP_D2, strtoul(optarg, NULL, 0));
break;
case 'f':
if (op != OP_UNKNOWN)
return print_usage_error(stdout);
op = OP_FPGA_BOOT;
if (op_filename)
free(op_filename);
op_filename = strdup(optarg);
break;
case '3':
spiSetPin(spi, SP_D3, strtoul(optarg, NULL, 0));
break;
case 'w':
if (op != OP_UNKNOWN)
return print_usage_error(stdout);
op = OP_SPI_WRITE;
if (op_filename)
free(op_filename);
op_filename = strdup(optarg);
break;
case 'f':
if (op != OP_UNKNOWN)
return print_usage_error(stdout);
op = OP_FPGA_BOOT;
if (op_filename)
free(op_filename);
op_filename = strdup(optarg);
break;
case 'v':
if (op != OP_UNKNOWN)
return print_usage_error(stdout);
op = OP_SPI_VERIFY;
if (op_filename)
free(op_filename);
op_filename = strdup(optarg);
break;
case 'w':
if (op != OP_UNKNOWN)
return print_usage_error(stdout);
op = OP_SPI_WRITE;
if (op_filename)
free(op_filename);
op_filename = strdup(optarg);
break;
case 's':
if (op != OP_UNKNOWN)
return print_usage_error(stdout);
op = OP_SPI_READ;
if (op_filename)
free(op_filename);
op_filename = strdup(optarg);
break;
case 'v':
if (op != OP_UNKNOWN)
return print_usage_error(stdout);
op = OP_SPI_VERIFY;
if (op_filename)
free(op_filename);
op_filename = strdup(optarg);
break;
default:
print_help(stdout, argv[0]);
return 1;
}
}
case 's':
if (op != OP_UNKNOWN)
return print_usage_error(stdout);
op = OP_SPI_READ;
if (op_filename)
free(op_filename);
op_filename = strdup(optarg);
break;
if (op == OP_UNKNOWN) {
print_help(stdout, argv[0]);
return 1;
}
default:
print_help(stdout, argv[0]);
return 1;
}
}
spiInit(spi);
fpgaInit(fpga);
if (op == OP_UNKNOWN) {
print_help(stdout, argv[0]);
return 1;
}
spiSetType(spi, spi_type);
fpgaReset(fpga);
#ifndef DEBUG_ICE40_PATCH
spiInit(spi);
fpgaInit(fpga);
if (spi_flash_bytes != -1)
spiOverrideSize(spi, spi_flash_bytes);
spiSetType(spi, spi_type);
fpgaReset(fpga);
switch (op) {
case OP_SPI_ID: {
struct spi_id id = spiId(spi);
printf("Manufacturer ID: %s (%02x)\n", id.manufacturer, id.manufacturer_id);
if (id.manufacturer_id != id._manufacturer_id)
printf("!! JEDEC Manufacturer ID: %02x\n",
id._manufacturer_id);
printf("Memory model: %s (%02x)\n", id.model, id.memory_type);
printf("Memory size: %s (%02x)\n", id.capacity, id.memory_size);
printf("Device ID: %02x\n", id.device_id);
if (id.device_id != id.signature)
printf("!! Electronic Signature: %02x\n", id.signature);
printf("Serial number: %02x %02x %02x %02x\n", id.serial[0], id.serial[1], id.serial[2], id.serial[3]);
printf("Status 1: %02x\n", spiReadStatus(spi, 1));
printf("Status 2: %02x\n", spiReadStatus(spi, 2));
printf("Status 3: %02x\n", spiReadStatus(spi, 3));
break;
}
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
case OP_SPI_SECURITY_WRITE: {
printf("Updating security register %d.\n", security_reg);
spiWriteSecurity(spi, security_reg, security_val);
break;
}
switch (op) {
case OP_SPI_ID: {
struct spi_id id = spiId(spi);
printf("Manufacturer ID: %s (%02x)\n", id.manufacturer, id.manufacturer_id);
if (id.manufacturer_id != id._manufacturer_id)
printf("!! JEDEC Manufacturer ID: %02x\n",
id._manufacturer_id);
printf("Memory model: %s (%02x)\n", id.model, id.memory_type);
printf("Memory size: %s (%02x)\n", id.capacity, id.memory_size);
printf("Device ID: %02x\n", id.device_id);
if (id.device_id != id.signature)
printf("!! Electronic Signature: %02x\n", id.signature);
printf("Serial number: %02x %02x %02x %02x\n", id.serial[0], id.serial[1], id.serial[2], id.serial[3]);
printf("Status 1: %02x\n", spiReadStatus(spi, 1));
printf("Status 2: %02x\n", spiReadStatus(spi, 2));
printf("Status 3: %02x\n", spiReadStatus(spi, 3));
break;
}
case OP_SPI_SECURITY_READ: {
uint8_t security[256];
printf("Security register %d contents:\n", security_reg);
spiReadSecurity(spi, security_reg, security);
print_hex(security, sizeof(security), 0);
break;
}
case OP_SPI_SECURITY_WRITE: {
printf("Updating security register %d.\n", security_reg);
spiWriteSecurity(spi, security_reg, security_val);
break;
}
case OP_SPI_READ: {
struct spi_id id = spiId(spi);
if (id.bytes == -1) {
fprintf(stderr, "unknown spi flash size -- specify with -b\n");
return 1;
}
case OP_SPI_SECURITY_READ: {
uint8_t security[256];
printf("Security register %d contents:\n", security_reg);
spiReadSecurity(spi, security_reg, security);
print_hex(security, sizeof(security), 0);
break;
}
fd = open(op_filename, O_WRONLY | O_CREAT | O_TRUNC, 0777);
if (fd == -1) {
perror("unable to open output file");
break;
}
uint8_t *bfr = malloc(id.bytes);
if (!bfr) {
perror("unable to allocate memory for spi");
return 1;
}
spiRead(spi, 0, bfr, id.bytes);
if (write(fd, bfr, id.bytes) != id.bytes) {
perror("unable to write SPI flash image to disk");
break;
}
close(fd);
free(bfr);
break;
}
case OP_SPI_READ: {
struct spi_id id = spiId(spi);
if (id.bytes == -1) {
fprintf(stderr, "unknown spi flash size -- specify with -b\n");
return 1;
}
case OP_SPI_WRITE: {
fd = open(op_filename, O_RDONLY);
if (fd == -1) {
perror("unable to open input file");
break;
}
struct stat stat;
if (fstat(fd, &stat) == -1) {
perror("unable to get bitstream file size");
break;
}
fd = open(op_filename, O_WRONLY | O_CREAT | O_TRUNC, 0777);
if (fd == -1) {
perror("unable to open output file");
break;
}
uint8_t *bfr = malloc(id.bytes);
if (!bfr) {
perror("unable to allocate memory for spi");
return 1;
}
spiRead(spi, 0, bfr, id.bytes);
if (write(fd, bfr, id.bytes) != id.bytes) {
perror("unable to write SPI flash image to disk");
break;
}
close(fd);
free(bfr);
break;
}
uint8_t *bfr = malloc(stat.st_size);
if (!bfr) {
perror("unable to alloc memory for buffer");
break;
}
if (read(fd, bfr, stat.st_size) != stat.st_size) {
perror("unable to read from file");
free(bfr);
break;
}
close(fd);
spiWrite(spi, 0, bfr, stat.st_size);
break;
}
case OP_SPI_WRITE: {
fd = open(op_filename, O_RDONLY);
if (fd == -1) {
perror("unable to open input file");
break;
}
struct stat stat;
if (fstat(fd, &stat) == -1) {
perror("unable to get bitstream file size");
break;
}
case OP_SPI_VERIFY: {
fd = open(op_filename, O_RDONLY);
if (fd == -1) {
perror("unable to open input file");
break;
}
struct stat stat;
if (fstat(fd, &stat) == -1) {
perror("unable to get bitstream file size");
break;
}
uint8_t *bfr = malloc(stat.st_size);
if (!bfr) {
perror("unable to alloc memory for buffer");
break;
}
if (read(fd, bfr, stat.st_size) != stat.st_size) {
perror("unable to read from file");
free(bfr);
break;
}
close(fd);
ret += spiWrite(spi, 0, bfr, stat.st_size);
break;
}
uint8_t *file_src = malloc(stat.st_size);
uint8_t *spi_src = malloc(stat.st_size);
if (!file_src) {
perror("unable to alloc memory for buffer");
break;
}
if (read(fd, file_src, stat.st_size) != stat.st_size) {
perror("unable to read from file");
free(file_src);
break;
}
close(fd);
case OP_SPI_VERIFY: {
fd = open(op_filename, O_RDONLY);
if (fd == -1) {
perror("unable to open input file");
break;
}
struct stat stat;
if (fstat(fd, &stat) == -1) {
perror("unable to get bitstream file size");
break;
}
spiRead(spi, 0, spi_src, stat.st_size);
uint8_t *file_src = malloc(stat.st_size);
uint8_t *spi_src = malloc(stat.st_size);
if (!file_src) {
perror("unable to alloc memory for buffer");
break;
}
if (read(fd, file_src, stat.st_size) != stat.st_size) {
perror("unable to read from file");
free(file_src);
break;
}
close(fd);
int offset;
for (offset = 0; offset < stat.st_size; offset++) {
if (file_src[offset] != spi_src[offset])
printf("%9d: file: %02x spi: %02x\n",
spiRead(spi, 0, spi_src, stat.st_size);
int offset;
for (offset = 0; offset < stat.st_size; offset++) {
if (file_src[offset] != spi_src[offset])
printf("%9d: file: %02x spi: %02x\n",
offset, file_src[offset], spi_src[offset]);
}
break;
}
}
break;
}
case OP_SPI_PEEK: {
uint8_t page[256];
spiRead(spi, peek_offset, page, sizeof(page));
print_hex_offset(stdout, page, sizeof(page), 0, 0);
break;
}
case OP_SPI_PEEK: {
uint8_t page[256];
spiRead(spi, peek_offset, page, sizeof(page));
print_hex_offset(stdout, page, sizeof(page), 0, 0);
break;
}
case OP_FPGA_BOOT: {
spiHold(spi);
spiSwapTxRx(spi);
fpgaResetSlave(fpga);
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 {
uint8_t bfr[32768];
int fd = open(op_filename, O_RDONLY);
if (fd == -1) {
perror("unable to open fpga bitstream");
break;
}
while ((count = read(fd, bfr, sizeof(bfr))) > 0) {
int i;
for (i = 0; i < count; i++)
spiTx(spi, bfr[i]);
}
if (count < 0) {
perror("unable to read from fpga bitstream file");
break;
}
close(fd);
}
for (count = 0; count < 500; count++)
spiTx(spi, 0xff);
fprintf(stderr, "FPGA Done? %d\n", fpgaDone(fpga));
spiEnd(spi);
fprintf(stderr, "FPGA Done? %d\n", fpgaDone(fpga));
spiSwapTxRx(spi);
spiUnhold(spi);
break;
}
int fd = open(op_filename, O_RDONLY);
if (fd == -1) {
perror("unable to open fpga bitstream");
break;
}
case OP_FPGA_RESET:
printf("resetting fpga\n");
fpgaResetMaster(fpga);
break;
spiBegin(spi);
default:
fprintf(stderr, "error: unknown operation\n");
break;
}
uint8_t bfr[32768];
int count;
while ((count = read(fd, bfr, sizeof(bfr))) > 0) {
int i;
for (i = 0; i < count; i++)
spiTx(spi, bfr[i]);
}
if (count < 0) {
perror("unable to read from fpga bitstream file");
break;
}
close(fd);
for (count = 0; count < 500; count++)
spiTx(spi, 0xff);
fprintf(stderr, "FPGA Done? %d\n", fpgaDone(fpga));
spiEnd(spi);
fpgaFree(&fpga);
spiFree(&spi);
spiSwapTxRx(spi);
spiUnhold(spi);
break;
}
case OP_FPGA_RESET:
printf("resetting fpga\n");
fpgaResetMaster(fpga);
break;
default:
fprintf(stderr, "error: unknown operation\n");
break;
}
fpgaFree(&fpga);
spiFree(&spi);
return 0;
return 0;
}

112
fpga.c
View File

@ -11,11 +11,11 @@
#include "fpga.h"
struct ff_fpga {
struct {
int reset;
int done;
int cs;
} pins;
struct {
int reset;
int done;
int cs;
} pins;
};
int fpgaDone(struct ff_fpga *fpga) {
@ -23,90 +23,90 @@ int fpgaDone(struct ff_fpga *fpga) {
}
int fpgaResetSlave(struct ff_fpga *fpga) {
// Put the FPGA into reset
gpioSetMode(fpga->pins.reset, PI_OUTPUT);
gpioWrite(fpga->pins.reset, 0);
// Put the FPGA into reset
gpioSetMode(fpga->pins.reset, PI_OUTPUT);
gpioWrite(fpga->pins.reset, 0);
// Set the CS pin to a GPIO, which will let us control it
gpioSetMode(fpga->pins.cs, PI_OUTPUT);
// Set the CS pin to a GPIO, which will let us control it
gpioSetMode(fpga->pins.cs, PI_OUTPUT);
// Set CS to 0, which will put the FPGA into slave mode
gpioWrite(fpga->pins.cs, 0);
// Set CS to 0, which will put the FPGA into slave mode
gpioWrite(fpga->pins.cs, 0);
usleep(10000); // XXX figure out correct sleep length here
usleep(10000); // XXX figure out correct sleep length here
// Bring the FPGA out of reset
gpioWrite(fpga->pins.reset, 1);
// Bring the FPGA out of reset
gpioWrite(fpga->pins.reset, 1);
usleep(1200); // 13.2.SPI Slave Configuration Process
usleep(1200); // 13.2.SPI Slave Configuration Process
// Release the CS pin
gpioWrite(fpga->pins.cs, 1);
// Release the CS pin
gpioWrite(fpga->pins.cs, 1);
return 0;
return 0;
}
int fpgaResetMaster(struct ff_fpga *fpga) {
// Put the FPGA into reset
gpioSetMode(fpga->pins.reset, PI_OUTPUT);
gpioWrite(fpga->pins.reset, 0);
// Put the FPGA into reset
gpioSetMode(fpga->pins.reset, PI_OUTPUT);
gpioWrite(fpga->pins.reset, 0);
// Set the CS pin to a GPIO, which will let us control it
gpioSetMode(fpga->pins.cs, PI_OUTPUT);
// Set the CS pin to a GPIO, which will let us control it
gpioSetMode(fpga->pins.cs, PI_OUTPUT);
// Set CS to 1, which will put the FPGA into "self boot" mode
gpioWrite(fpga->pins.cs, 1);
// Set CS to 1, which will put the FPGA into "self boot" mode
gpioWrite(fpga->pins.cs, 1);
usleep(10000); // XXX figure out correct sleep length here
usleep(10000); // XXX figure out correct sleep length here
// Bring the FPGA out of reset
gpioWrite(fpga->pins.reset, 1);
// Bring the FPGA out of reset
gpioWrite(fpga->pins.reset, 1);
usleep(1200); // 13.2.SPI Slave Configuration Process
usleep(1200); // 13.2.SPI Slave Configuration Process
return 0;
return 0;
}
int fpgaReset(struct ff_fpga *fpga) {
// Put the FPGA into reset
gpioSetMode(fpga->pins.reset, PI_OUTPUT);
gpioWrite(fpga->pins.reset, 0);
return 0;
// Put the FPGA into reset
gpioSetMode(fpga->pins.reset, PI_OUTPUT);
gpioWrite(fpga->pins.reset, 0);
return 0;
}
int fpgaInit(struct ff_fpga *fpga) {
// Put the FPGA into reset
gpioSetMode(fpga->pins.reset, PI_OUTPUT);
gpioWrite(fpga->pins.reset, 0);
// Put the FPGA into reset
gpioSetMode(fpga->pins.reset, PI_OUTPUT);
gpioWrite(fpga->pins.reset, 0);
// Also monitor the C_DONE pin
gpioSetMode(fpga->pins.done, PI_INPUT);
// Also monitor the C_DONE pin
gpioSetMode(fpga->pins.done, PI_INPUT);
return 0;
return 0;
}
struct ff_fpga *fpgaAlloc(void) {
struct ff_fpga *fpga = (struct ff_fpga *)malloc(sizeof(struct ff_fpga));
memset(fpga, 0, sizeof(*fpga));
return fpga;
struct ff_fpga *fpga = (struct ff_fpga *)malloc(sizeof(struct ff_fpga));
memset(fpga, 0, sizeof(*fpga));
return fpga;
}
void fpgaSetPin(struct ff_fpga *fpga, enum fpga_pin pin, int val) {
switch (pin) {
case FP_RESET: fpga->pins.reset = val; break;
case FP_DONE: fpga->pins.done = val; break;
case FP_CS: fpga->pins.cs = val; break;
default: fprintf(stderr, "unrecognized pin: %d\n", pin); break;
}
switch (pin) {
case FP_RESET: fpga->pins.reset = val; break;
case FP_DONE: fpga->pins.done = val; break;
case FP_CS: fpga->pins.cs = val; break;
default: fprintf(stderr, "unrecognized pin: %d\n", pin); break;
}
}
void fpgaFree(struct ff_fpga **fpga) {
if (!fpga)
return;
if (!*fpga)
return;
if (!fpga)
return;
if (!*fpga)
return;
free(*fpga);
*fpga = NULL;
free(*fpga);
*fpga = NULL;
}

562
ice40.c Normal file
View File

@ -0,0 +1,562 @@
#include <assert.h>
#include <string.h>
#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
{
uint32_t offset;
uint32_t current_bank;
uint32_t current_width;
uint32_t current_height;
uint32_t current_offset;
uint32_t cram_width;
uint32_t cram_height;
uint32_t bram_width;
uint32_t bram_height;
uint16_t crc_value;
uint8_t warmboot;
uint8_t nosleep;
uint8_t frequency_range;
};
static void update_crc16(uint16_t *crc, uint8_t byte)
{
// CRC-16-CCITT, Initialize to 0xFFFF, No zero padding
for (int i = 7; i >= 0; i--)
{
uint16_t xor_value = ((*crc >> 15) ^ ((byte >> i) & 1)) ? 0x1021 : 0;
*crc = (*crc << 1) ^ xor_value;
}
}
static uint32_t get_bit_offset(int x, int total_bits) {
// return (8192 * (x & 7)) + (x >> 3);
int bitshift = ffs(total_bits)-1;
return ((x * 8192) % total_bits) + ((x*8192) >> bitshift);
}
uint32_t xorshift32(uint32_t x)
{
/* Algorithm "xor" from p. 4 of Marsaglia, "Xorshift RNGs" */
x = x ^ (x << 13);
x = x ^ (x >> 17);
x = x ^ (x << 5);
return x;
}
uint32_t get_rand(uint32_t x) {
uint32_t out = 0;
int i;
for (i = 0; i < 32; i++) {
x = xorshift32(x);
if ((x & 1) == 1)
out = out | (1 << i);
}
return out;
}
static uint32_t fill_rand(uint32_t *bfr, int count) {
int i;
uint32_t last = 1;
for (i = 0; i < count / 4; i++) {
last = get_rand(last);
bfr[i] = last;
}
return i;
}
uint32_t swap_u32(uint32_t word) {
return (((word >> 24) & 0x000000ff)
| ((word >> 8) & 0x0000ff00)
| ((word << 8) & 0x00ff0000)
| ((word << 24) & 0xff000000));
}
struct irw_file *irw_open(const char *filename, const char *mode)
{
struct irw_file *f = malloc(sizeof(*f));
memset(f, 0, sizeof(*f));
f->f = fopen(filename, mode);
return f;
}
struct irw_file *irw_open_fake(void *hook_data,
int (*read_hook)(void *data),
int (*write_hook)(void *data, uint8_t b)) {
struct irw_file *f = malloc(sizeof(*f));
memset(f, 0, sizeof(*f));
f->read_hook = read_hook;
f->write_hook = write_hook;
f->hook_data = hook_data;
return f;
}
int irw_readb(struct irw_file *f)
{
int val;
if (f->read_hook)
val = f->read_hook(f->hook_data);
else
val = fgetc(f->f);
if (val == EOF)
return EOF;
update_crc16(&f->crc, val);
return val;
}
int irw_writeb(struct irw_file *f, int c) {
update_crc16(&f->crc, c);
if (f->write_hook)
return f->write_hook(f->hook_data, c);
else
return fputc(c, f->f);
}
void irw_close(struct irw_file **f) {
if (!f)
return;
if (!*f)
return;
if (!(*f)->f)
return;
fclose((*f)->f);
*f = NULL;
}
uint8_t get_bit(uint32_t *field, uint32_t offset)
{
// printf("offset&31: %d\n", offset & 31);
// printf("offset/sizeof(*field): %d\n", offset >> 5);
assert(offset < 65536);
return !!(field[offset >> 5] & (1 << (offset & 31)));
}
void set_bit(uint32_t *field, uint32_t offset)
{
assert(offset < 65536);
field[offset >> 5] |= (1 << (offset & 31));
}
void clear_bit(uint32_t *field, uint32_t offset)
{
assert(offset < 65536);
field[offset >> 5] &= ~(1 << (offset & 31));
}
int ice40_patch(struct irw_file *f, struct irw_file *rom,
struct irw_file *o, uint32_t byte_count)
{
uint32_t preamble = 0;
uint8_t wakeup = 0;
struct Ice40Bitstream bs;
uint32_t input_rom[byte_count / sizeof(uint32_t)];
uint32_t input_rand[byte_count / sizeof(uint32_t)];
uint32_t output_rand[byte_count / sizeof(uint32_t)];
uint32_t output_rom[byte_count / sizeof(uint32_t)];
uint8_t *i8 = (uint8_t *)input_rom;
uint16_t *ora16 = (uint16_t *)output_rand;
uint16_t *oro16 = (uint16_t *)output_rom;
unsigned int ora_ptr = 0;
unsigned int input_ptr;
int b;
int errors = 0;
memset(&bs, 0, sizeof(bs));
// Read the ROM into a source buffer
memset(input_rom, 0, sizeof(input_rom));
input_ptr = 0;
while ((b = irw_readb(rom)) != EOF)
i8[input_ptr++] = b;
DEBUG_PRINT("read %d bytes from rom\n", input_ptr);
// Generate our reference pattern
memset(input_rand, 0, sizeof(input_rand));
fill_rand(input_rand, sizeof(input_rand));
// Swap either the input or output data, as necessary
for (input_ptr = 0; input_ptr < sizeof(input_rom)/4; input_ptr++) {
// input_rand[input_ptr] = swap_u32(input_rand[input_ptr]);
// input_rom[input_ptr] = swap_u32(input_rom[input_ptr]);
}
// Spray the reference pattern and ROM like they would exist in the FPGA
for (input_ptr = 0; input_ptr < sizeof(input_rom) * 8; input_ptr++) {
int bit;
bit = get_bit(input_rand, get_bit_offset(input_ptr, sizeof(input_rand)*8));
if (bit)
set_bit(output_rand, input_ptr);
else
clear_bit(output_rand, input_ptr);
bit = get_bit(input_rom, get_bit_offset(input_ptr, sizeof(input_rom)*8));
if (bit)
set_bit(output_rom, input_ptr);
else
clear_bit(output_rom, input_ptr);
}
while (1)
{
b = irw_readb(f);
if (b == EOF)
break;
irw_writeb(o, b);
preamble = (preamble << 8) | b;
if (preamble == 0x7eaa997e)
{
// DEBUG_PRINT("found preamble at %d\n", bs.offset);
break;
}
}
while (!wakeup)
{
int b = irw_readb(f);
if (b == EOF)
{
// DEBUG_PRINT("reached end of file\n");
break;
}
irw_writeb(o, b);
uint8_t cmd = b >> 4;
uint8_t payload_len = b & 0xf;
uint32_t payload = 0;
uint8_t last0, last1;
unsigned int i;
for (i = 0; i < payload_len; i++)
{
b = irw_readb(f);
payload = (payload << 8) | (b & 0xff);
// Don't write the CRC16 out, since we'll do that later on.
if (cmd != 2)
irw_writeb(o, b);
}
switch (cmd)
{
case 0:
switch (payload)
{
case 1:
DEBUG_PRINT("CRAM data (bank %d): %d x %d @ 0x%08x; %d bits = %d bytes\n",
bs.current_bank,
bs.current_width,
bs.current_height,
bs.current_offset,
bs.current_width * bs.current_height,
(bs.current_width * bs.current_height) / 8);
bs.cram_width = MAX(bs.cram_width, bs.current_width);
bs.cram_height = MAX(bs.cram_height, bs.current_height);
for (i = 0; i < ((bs.current_width * bs.current_height) / 8); i++)
{
irw_writeb(o, irw_readb(f));
}
last0 = irw_readb(f);
last1 = irw_readb(f);
if (last0 || last1)
{
printf("expected 0x0000 after CRAM data, got %02x %02x\n", last0, last1);
}
irw_writeb(o, last0);
irw_writeb(o, last1);
break;
case 3:
DEBUG_PRINT("BRAM data (bank %d): %d x %d @ 0x%08x; %d bits = %d bytes\n",
bs.current_bank,
bs.current_width,
bs.current_height,
bs.current_offset,
bs.current_width * bs.current_height,
(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);
ora_ptr = 16 * bs.current_offset;
// 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;
}
}
}
}
}
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 {
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);
}
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)
{
printf("expected 0x0000 after BRAM data, got %02x %02x\n", last0, last1);
}
irw_writeb(o, last0);
irw_writeb(o, last1);
break;
// Reset CRC
case 5:
f->crc = 0xffff;
o->crc = 0xffff;
break;
// Wakeup
case 6:
wakeup = 1;
break;
default:
printf("unrecognized command 0x%02x 0x%02x\n", cmd, payload);
break;
}
break;
// Set current bank
case 1:
bs.current_bank = payload;
ora_ptr = 0;
// printf("setting bank number to %d\n", bs.current_bank);
break;
// Validate CRC16
case 2:
DEBUG_PRINT("crc check (%04x == %04x)\n", f->crc, 0);
uint16_t crc16 = o->crc;
irw_writeb(o, crc16 >> 8);
irw_writeb(o, crc16);
break;
// Set frequency range
case 5:
switch (payload)
{
case 0:
bs.frequency_range = 0;
break;
case 1:
bs.frequency_range = 1;
break;
case 2:
bs.frequency_range = 2;
break;
default:
printf("unknown frequency range payload: %02x\n", payload);
break;
}
break;
// Set current width
case 6:
bs.current_width = payload + 1;
break;
// Set current height
case 7:
bs.current_height = payload;
break;
// Set current ofset
case 8:
bs.current_offset = payload;
break;
// Set flags
case 9:
switch (payload)
{
case 0:
bs.warmboot = 0;
bs.nosleep = 0;
break;
case 1:
bs.warmboot = 0;
bs.nosleep = 1;
break;
case 32:
bs.warmboot = 1;
bs.nosleep = 0;
break;
case 33:
bs.warmboot = 1;
bs.nosleep = 1;
break;
default:
printf("unrecognized feature flags: %02x\n", payload);
break;
}
break;
default:
printf("unrecognized command: %02x\n", cmd);
break;
}
}
// Padding
irw_writeb(o, 0);
return errors;
}

28
ice40.h Normal file
View File

@ -0,0 +1,28 @@
#ifndef _ICE40_H
#define _ICE40_H
#include <stdio.h>
#include <stdint.h>
typedef struct irw_file
{
FILE *f;
uint16_t crc;
uint32_t offset;
void *hook_data;
int (*read_hook)(void *data);
int (*write_hook)(void *data, uint8_t b);
} IRW_FILE;
struct irw_file *irw_open(const char *filename, const char *mode);
struct irw_file *irw_open_fake(void *hook_data,
int (*read_hook)(void *data),
int (*write_hook)(void *data, uint8_t b));
int irw_readb(struct irw_file *f);
int irw_writeb(struct irw_file *f, int c);
void irw_close(struct irw_file **f);
int ice40_patch(struct irw_file *f, struct irw_file *rom,
struct irw_file *o, uint32_t byte_count);
#endif /* _ICE40_H */