Compare commits
18 Commits
Author | SHA1 | Date |
---|---|---|
Sean Cross | 63315d9a58 | |
Sean Cross | 798a0f167e | |
Sean Cross | 9506159380 | |
Sean Cross | 89af26047e | |
Sean Cross | 9876d92981 | |
Sean Cross | f4297532d0 | |
Sean Cross | 4156a37d72 | |
Sean Cross | f2ae56a13e | |
Sean Cross | 61151b4618 | |
Sean Cross | d6bff65d0b | |
Sean Cross | ea6c170023 | |
Sean Cross | d2b4ff089d | |
Sean Cross | fb21ad75e2 | |
Sean Cross | 5e3ac61921 | |
Sean Cross | 7fb6e4bb7b | |
Sean Cross | bcbc7319c1 | |
Sean Cross | 4ca2218403 | |
Peter Marheine | eb328612b2 |
70
README.md
70
README.md
|
@ -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`.
|
898
fomu-flash.c
898
fomu-flash.c
|
@ -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
112
fpga.c
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
|
@ -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 */
|
Loading…
Reference in New Issue