Compare commits

...

39 Commits
v0.2 ... 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
Sean Cross 13464db5de fomu-flash: map F_RESET differently for original Pi
Pin 13, which we use as F_RESET, was originally routed to BCM Pin 21.
All other revisions of the board map it to BCM Pin 27.

Check for the original version of the board, and re-assign F_RESET as
necessary.

Signed-off-by: Sean Cross <sean@xobs.io>
2019-02-11 10:27:37 +08:00
Sean Cross 987ae9071a fomu-flash: fix pinspec to match hacker version silk
The silkscreen on the hacker version uses "S" for chip select and
"C" for clock.  Have the help screen and "-g" parameter use this
convention for arguments.

This fixes Github issue #2.

Signed-off-by: Sean Cross <sean@xobs.io>
2019-01-14 09:28:29 +08:00
Sean Cross e3f110f267
Merge pull request #1 from osresearch/master
erase the security register pages before writing
2019-01-14 09:04:42 +08:00
Trammell hudson 20978ad4e5
erase the security register pages before writing 2019-01-13 20:01:51 -05:00
Sean Cross 676bcb33a1 README: fix some typos
Signed-off-by: Sean Cross <sean@xobs.io>
2019-01-02 18:38:45 +08:00
Sean Cross fb72772b5a README: add pinout and connection instructions
Signed-off-by: Sean Cross <sean@xobs.io>
2019-01-02 18:37:25 +08:00
Sean Cross 556fdb8e1b spi: set sr2 quirk by default
most chips, it seems, write sr2 just after sr1 instead of having
a separate command.  Set this quirk by default.

Signed-off-by: Sean Cross <sean@xobs.io>
2018-12-19 00:01:58 -05:00
Sean Cross 27dec515e7 enable capacity overriding
We have different-sized SPI flash chips, so when possible set the
capacity based on various id codes.

Also allow for manually overriding capacity with the -b option.

Signed-off-by: Sean Cross <sean@xobs.io>
2018-12-19 00:00:25 -05:00
Sean Cross 9b1b9b8a88 fomu-flash: implement -k security register stuff
Signed-off-by: Sean Cross <sean@xobs.io>
2018-12-18 23:03:38 -05:00
Sean Cross ff1f8583af spi: get security register stuff working
Signed-off-by: Sean Cross <sean@xobs.io>
2018-12-18 23:03:25 -05:00
Sean Cross c0c9f22748 spi: add the ability to read and write security
Signed-off-by: Sean Cross <sean@xobs.io>
2018-12-18 22:18:26 -05:00
Sean Cross 70591122af fomu-flash: print out device strings as part of -i
Signed-off-by: Sean Cross <sean@xobs.io>
2018-12-18 22:09:57 -05:00
Sean Cross 09f0db5ca7 spi: add strings to device id
Signed-off-by: Sean Cross <sean@xobs.io>
2018-12-18 22:09:49 -05:00
Sean Cross 91b4a74dc8 spi: flush stdout
Signed-off-by: Sean Cross <sean@xobs.io>
2018-12-18 20:57:34 -05:00
Sean Cross f4ac6ea812 spi: handle qpi better for command handling
Signed-off-by: Sean Cross <sean@xobs.io>
2018-12-18 20:53:22 -05:00
Sean Cross af1dff1e28 spi: use quirks for reading from SR
Different chips handle reading from SR in different ways.

Signed-off-by: Sean Cross <sean@xobs.io>
2018-12-18 20:52:58 -05:00
Sean Cross edd8824a74 spi: add quirks system
Not all SPI chips are the same.  Add a system to differentiate them.

Signed-off-by: Sean Cross <sean@xobs.io>
2018-12-18 20:52:24 -05:00
Sean Cross fc6d6a575d spi: print out progress while writing
Signed-off-by: Sean Cross <sean@xobs.io>
2018-12-18 20:40:01 -05:00
Sean Cross 6b6266c1de spi: issue "wake from reset" as part of spiReset()
Signed-off-by: Sean Cross <sean@xobs.io>
2018-12-18 20:38:31 -05:00
Sean Cross 4e411774c0 spi: remove unused prototype
Signed-off-by: Sean Cross <sean@xobs.io>
2018-12-18 20:33:53 -05:00
Sean Cross f4d2c46eb3 fomu-flash: print out status registers as part of id
Print the contents of SR1, SR2, and SR3.  Not all chips have SR3,
so this may be 0xff.

Signed-off-by: Sean Cross <sean@xobs.io>
2018-12-18 20:32:10 -05:00
8 changed files with 1428 additions and 446 deletions

View File

@ -8,6 +8,21 @@ These tools can be used to control an FPGA and its accompanying SPI flash chip.
To build this repository, simply run `make`.
## Test Jig Setup
The EVT boards can be attached directly to the Raspberry Pi as a "hat". When building a test jig, attach wires according to the following image:
![Raspberry Pi Pinout](pinout.png)
The only pins that are required are 5V, GND, CRESET, SPI_MOSI, SPI_MISO, SPI_CLK, and SPI_CS.
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.
## Loading a Bitstream
The most basic usecase is to load a program into configuration RAM. This is a very quick process, and can be used for rapid prototyping.
@ -35,7 +50,7 @@ It will not reset the FPGA. To do that, you must re-run with `-r`.
## Verifying SPI flash
You can verify the SPI flash was programmered with the `-v` command:
You can verify the SPI flash was programmed with the `-v` command:
```sh
# ./tomu-flash -v top.bin
@ -45,5 +60,65 @@ You can verify the SPI flash was programmered with the `-v` command:
You can "peek" at 256 bytes of SPI with `-p [offset]`. This can be used to quickly verify that something was written.
## Patching ROM
numbers seen in https://elinux.org/File:Pi-GPIO-header.png
`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
@ -21,436 +22,562 @@
#define S_D1 S_MISO
#define S_D2 S_WP
#define S_D3 S_HOLD
#define F_RESET 27
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_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 'k': return SP_CLK;
case 'c': 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\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, " k SPI CLK %d\n", S_CLK);
fprintf(stream, " c 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");
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])\n", progname);
fprintf(stream, " [-g pinspec] [-t spitype] [-r]\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, "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;
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;
spi = spiAlloc();
fpga = fpgaAlloc();
#ifndef DEBUG_ICE40_PATCH
if (gpioInitialise() < 0) {
fprintf(stderr, "Unable to initialize GPIO\n");
return 1;
}
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);
// 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
fpgaSetPin(fpga, FP_RESET, F_RESET);
fpgaSetPin(fpga, FP_DONE, F_DONE);
fpgaSetPin(fpga, FP_CS, S_CE0);
spi = spiAlloc();
fpga = fpgaAlloc();
if (gpioInitialise() < 0) {
fprintf(stderr, "Unable to initialize GPIO\n");
return 1;
}
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);
while ((opt = getopt(argc, argv, "hip:rf:w:s:2:3:v:g:t:")) != -1) {
switch (opt) {
fpgaSetPin(fpga, FP_RESET, F_RESET);
fpgaSetPin(fpga, FP_DONE, F_DONE);
fpgaSetPin(fpga, FP_CS, S_CE0);
case 'r':
if (op != OP_UNKNOWN)
return print_usage_error(stdout);
op = OP_FPGA_RESET;
break;
while ((opt = getopt(argc, argv, "hip:rf:b:w:s:2:3:v:g:t:k:l:")) != -1) {
switch (opt) {
case 'i':
if (op != OP_UNKNOWN)
return print_usage_error(stdout);
op = OP_SPI_ID;
break;
case 'r':
if (op != OP_UNKNOWN)
return print_usage_error(stdout);
op = OP_FPGA_RESET;
break;
case 'p':
if (op != OP_UNKNOWN)
return print_usage_error(stdout);
op = OP_SPI_PEEK;
peek_offset = strtoul(optarg, NULL, 0);
break;
case 'b':
spi_flash_bytes = strtoul(optarg, NULL, 0);
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 'l':
replacement_rom = irw_open(optarg, "r");
if (!replacement_rom) {
perror("couldn't open replacement rom file");
return 10;
}
break;
case 'g':
if ((optarg[0] == '\0') || (optarg[1] != ':')) {
fprintf(stderr, "-g requires a pinspec. Usage:\n");
print_pinspec(stderr);
return 1;
}
case 'k': {
if (op != OP_UNKNOWN)
return print_usage_error(stdout);
char *security_filename = strchr(optarg, ':');
spiSetPin(spi, pinspec_to_pinname(optarg[0]), strtoul(optarg+2, NULL, 0));
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 '2':
spiSetPin(spi, SP_D2, strtoul(optarg, NULL, 0));
break;
case 'i':
if (op != OP_UNKNOWN)
return print_usage_error(stdout);
op = OP_SPI_ID;
break;
case '3':
spiSetPin(spi, SP_D3, strtoul(optarg, NULL, 0));
break;
case 'p':
if (op != OP_UNKNOWN)
return print_usage_error(stdout);
op = OP_SPI_PEEK;
peek_offset = 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 '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 '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 'g':
if ((optarg[0] == '\0') || (optarg[1] != ':')) {
fprintf(stderr, "-g requires a pinspec. Usage:\n");
print_pinspec(stderr);
return 1;
}
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;
spiSetPin(spi, pinspec_to_pinname(optarg[0]), strtoul(optarg+2, NULL, 0));
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 '2':
spiSetPin(spi, SP_D2, strtoul(optarg, NULL, 0));
break;
default:
print_help(stdout, argv[0]);
return 1;
}
}
case '3':
spiSetPin(spi, SP_D3, strtoul(optarg, NULL, 0));
break;
if (op == OP_UNKNOWN) {
print_help(stdout, argv[0]);
return 1;
}
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;
spiInit(spi);
fpgaInit(fpga);
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;
spiSetType(spi, spi_type);
fpgaReset(fpga);
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;
switch (op) {
case OP_SPI_ID: {
struct spi_id id = spiId(spi);
printf("Device ID: %02x\n", id.device_id);
if (id.device_id != id.signature)
printf("!! Electronic Signature: %02x\n", id.signature);
printf("Manufacturer ID: %02x\n", id.manufacturer_id);
if (id.manufacturer_id != id._manufacturer_id)
printf("!! JEDEC Manufacturer ID: %02x\n",
id._manufacturer_id);
printf("Memory type: %02x\n", id.memory_type);
printf("Memory size: %02x\n", id.memory_size);
printf("Serial number: %02x %02x %02x %02x\n", id.serial[0], id.serial[1], id.serial[2], id.serial[3]);
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 OP_SPI_READ: {
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(16777216);
spiRead(spi, 0, bfr, 16777216);
if (write(fd, bfr, 16777216) != 16777216) {
perror("unable to write SPI flash image");
break;
}
close(fd);
free(bfr);
break;
}
default:
print_help(stdout, argv[0]);
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;
}
if (op == OP_UNKNOWN) {
print_help(stdout, argv[0]);
return 1;
}
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;
}
#ifndef DEBUG_ICE40_PATCH
spiInit(spi);
fpgaInit(fpga);
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;
}
spiSetType(spi, spi_type);
fpgaReset(fpga);
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);
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
spiRead(spi, 0, spi_src, stat.st_size);
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;
}
int offset;
for (offset = 0; offset < stat.st_size; offset++) {
if (file_src[offset] != spi_src[offset])
printf("%9d: file: %02x spi: %02x\n",
case OP_SPI_SECURITY_WRITE: {
printf("Updating security register %d.\n", security_reg);
spiWriteSecurity(spi, security_reg, security_val);
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_READ: {
struct spi_id id = spiId(spi);
if (id.bytes == -1) {
fprintf(stderr, "unknown spi flash size -- specify with -b\n");
return 1;
}
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_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;
}
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;
}
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 *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);
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 */

BIN
pinout.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 45 KiB

239
spi.c
View File

@ -10,10 +10,26 @@
#include "rpi.h"
#include "spi.h"
enum ff_spi_quirks {
// There is no separate "Write SR 2" command. Instead,
// you must write SR2 after writing SR1
SQ_SR2_FROM_SR1 = (1 << 0),
// Don't issue a "Write Enable" command prior to writing
// a status register
SQ_SKIP_SR_WEL = (1 << 1),
// Security registers are shifted up by 4 bits
SQ_SECURITY_NYBBLE_SHIFT = (1 << 2),
};
struct ff_spi {
enum spi_state state;
enum spi_type type;
enum spi_type desired_type;
struct spi_id id;
enum ff_spi_quirks quirks;
int size_override;
struct {
int clk;
@ -29,6 +45,8 @@ struct ff_spi {
} pins;
};
static void spi_get_id(struct ff_spi *spi);
static void spi_set_state(struct ff_spi *spi, enum spi_state state) {
if (spi->state == state)
return;
@ -104,6 +122,10 @@ void spiPause(struct ff_spi *spi) {
void spiBegin(struct ff_spi *spi) {
spi_set_state(spi, SS_SINGLE);
if ((spi->type == ST_SINGLE) || (spi->type == ST_DUAL)) {
gpioWrite(spi->pins.wp, 1);
gpioWrite(spi->pins.hold, 1);
}
gpioWrite(spi->pins.cs, 0);
}
@ -283,28 +305,28 @@ uint8_t spiCommandRx(struct ff_spi *spi) {
return spiSingleRx(spi);
}
uint8_t spiReadSr(struct ff_spi *spi, int sr) {
uint8_t spiReadStatus(struct ff_spi *spi, uint8_t sr) {
uint8_t val = 0xff;
switch (sr) {
case 1:
spiBegin(spi);
spiCommand(spi, 0x05);
val = spiRx(spi);
val = spiCommandRx(spi);
spiEnd(spi);
break;
case 2:
spiBegin(spi);
spiCommand(spi, 0x35);
val = spiRx(spi);
val = spiCommandRx(spi);
spiEnd(spi);
break;
case 3:
spiBegin(spi);
spiCommand(spi, 0x15);
val = spiRx(spi);
val = spiCommandRx(spi);
spiEnd(spi);
break;
@ -316,9 +338,69 @@ uint8_t spiReadSr(struct ff_spi *spi, int sr) {
return val;
}
void spiWriteSr(struct ff_spi *spi, int sr, uint8_t val) {
void spiWriteSecurity(struct ff_spi *spi, uint8_t sr, uint8_t security[256]) {
if (spi->quirks & SQ_SECURITY_NYBBLE_SHIFT)
sr <<= 4;
spiBegin(spi);
spiCommand(spi, 0x06);
spiEnd(spi);
// erase the register
spiBegin(spi);
spiCommand(spi, 0x44);
spiCommand(spi, 0x00); // A23-16
spiCommand(spi, sr); // A15-8
spiCommand(spi, 0x00); // A0-7
spiEnd(spi);
spi_get_id(spi);
sleep(1);
// write enable
spiBegin(spi);
spiCommand(spi, 0x06);
spiEnd(spi);
spiBegin(spi);
spiCommand(spi, 0x42);
spiCommand(spi, 0x00); // A23-16
spiCommand(spi, sr); // A15-8
spiCommand(spi, 0x00); // A0-7
int i;
for (i = 0; i < 256; i++)
spiCommand(spi, security[i]);
spiEnd(spi);
spi_get_id(spi);
}
void spiReadSecurity(struct ff_spi *spi, uint8_t sr, uint8_t security[256]) {
if (spi->quirks & SQ_SECURITY_NYBBLE_SHIFT)
sr <<= 4;
spiBegin(spi);
spiCommand(spi, 0x48); // Read security registers
spiCommand(spi, 0x00); // A23-16
spiCommand(spi, sr); // A15-8
spiCommand(spi, 0x00); // A0-7
int i;
for (i = 0; i < 256; i++)
security[i] = spiCommandRx(spi);
spiEnd(spi);
}
void spiWriteStatus(struct ff_spi *spi, uint8_t sr, uint8_t val) {
switch (sr) {
case 1:
if (!(spi->quirks & SQ_SKIP_SR_WEL)) {
spiBegin(spi);
spiCommand(spi, 0x06);
spiEnd(spi);
}
spiBegin(spi);
spiCommand(spi, 0x50);
spiEnd(spi);
@ -329,18 +411,44 @@ void spiWriteSr(struct ff_spi *spi, int sr, uint8_t val) {
spiEnd(spi);
break;
case 2:
case 2: {
uint8_t sr1 = 0x00;
if (spi->quirks & SQ_SR2_FROM_SR1)
sr1 = spiReadStatus(spi, 1);
if (!(spi->quirks & SQ_SKIP_SR_WEL)) {
spiBegin(spi);
spiCommand(spi, 0x06);
spiEnd(spi);
}
spiBegin(spi);
spiCommand(spi, 0x50);
spiEnd(spi);
spiBegin(spi);
spiCommand(spi, 0x31);
spiCommand(spi, val);
if (spi->quirks & SQ_SR2_FROM_SR1) {
spiCommand(spi, 0x01);
spiCommand(spi, sr1);
spiCommand(spi, val);
}
else {
spiCommand(spi, 0x31);
spiCommand(spi, val);
}
spiEnd(spi);
break;
}
case 3:
if (!(spi->quirks & SQ_SKIP_SR_WEL)) {
spiBegin(spi);
spiCommand(spi, 0x06);
spiEnd(spi);
}
spiBegin(spi);
spiCommand(spi, 0x50);
spiEnd(spi);
@ -358,23 +466,56 @@ void spiWriteSr(struct ff_spi *spi, int sr, uint8_t val) {
}
struct spi_id spiId(struct ff_spi *spi) {
struct spi_id id;
memset(&id, 0xff, sizeof(id));
return spi->id;
}
static void spi_decode_id(struct ff_spi *spi) {
spi->id.manufacturer = "unknown";
spi->id.model = "unknown";
spi->id.capacity = "unknown";
spi->id.bytes = -1; // unknown
if (spi->id.manufacturer_id == 0xef) {
spi->id.manufacturer = "Winbond";
if ((spi->id.memory_type == 0x70)
&& (spi->id.memory_size == 0x18)) {
spi->id.model = "W25Q128JV";
spi->id.capacity = "128 Mbit";
spi->id.bytes = 16 * 1024 * 1024;
}
}
if (spi->id.manufacturer_id == 0x1f) {
spi->id.manufacturer = "Adesto";
if ((spi->id.memory_type == 0x86)
&& (spi->id.memory_size == 0x01)) {
spi->id.model = "AT25SF161";
spi->id.capacity = "16 Mbit";
spi->id.bytes = 1 * 1024 * 1024;
}
}
return;
}
static void spi_get_id(struct ff_spi *spi) {
memset(&spi->id, 0xff, sizeof(spi->id));
spiBegin(spi);
spiCommand(spi, 0x90); // Read manufacturer ID
spiCommand(spi, 0x00); // Dummy byte 1
spiCommand(spi, 0x00); // Dummy byte 2
spiCommand(spi, 0x00); // Dummy byte 3
id.manufacturer_id = spiCommandRx(spi);
id.device_id = spiCommandRx(spi);
spi->id.manufacturer_id = spiCommandRx(spi);
spi->id.device_id = spiCommandRx(spi);
spiEnd(spi);
spiBegin(spi);
spiCommand(spi, 0x9f); // Read device id
id._manufacturer_id = spiCommandRx(spi);
id.memory_type = spiCommandRx(spi);
id.memory_size = spiCommandRx(spi);
spi->id._manufacturer_id = spiCommandRx(spi);
spi->id.memory_type = spiCommandRx(spi);
spi->id.memory_size = spiCommandRx(spi);
spiEnd(spi);
spiBegin(spi);
@ -382,7 +523,7 @@ struct spi_id spiId(struct ff_spi *spi) {
spiCommand(spi, 0x00); // Dummy byte 1
spiCommand(spi, 0x00); // Dummy byte 2
spiCommand(spi, 0x00); // Dummy byte 3
id.signature = spiCommandRx(spi);
spi->id.signature = spiCommandRx(spi);
spiEnd(spi);
spiBegin(spi);
@ -391,16 +532,25 @@ struct spi_id spiId(struct ff_spi *spi) {
spiCommand(spi, 0x00); // Dummy byte 2
spiCommand(spi, 0x00); // Dummy byte 3
spiCommand(spi, 0x00); // Dummy byte 4
id.serial[0] = spiCommandRx(spi);
id.serial[1] = spiCommandRx(spi);
id.serial[2] = spiCommandRx(spi);
id.serial[3] = spiCommandRx(spi);
spi->id.serial[0] = spiCommandRx(spi);
spi->id.serial[1] = spiCommandRx(spi);
spi->id.serial[2] = spiCommandRx(spi);
spi->id.serial[3] = spiCommandRx(spi);
spiEnd(spi);
return id;
spi_decode_id(spi);
return;
}
void spiOverrideSize(struct ff_spi *spi, uint32_t size) {
spi->size_override = size;
// If size is 0, re-read the capacity
if (!size)
spi_decode_id(spi);
else
spi->id.bytes = size;
}
uint32_t spiJedecId(struct ff_spi *spi);
int spiSetType(struct ff_spi *spi, enum spi_type type) {
@ -437,7 +587,7 @@ int spiSetType(struct ff_spi *spi, enum spi_type type) {
}
// Enable QE bit
spiWriteSr(spi, 2, spiReadSr(spi, 2) | (1 << 1));
spiWriteStatus(spi, 2, spiReadStatus(spi, 2) | (1 << 1));
spi->type = type;
spi_set_state(spi, SS_QUAD_TX);
@ -445,7 +595,7 @@ int spiSetType(struct ff_spi *spi, enum spi_type type) {
case ST_QPI:
// Enable QE bit
spiWriteSr(spi, 2, spiReadSr(spi, 2) | (1 << 1));
spiWriteStatus(spi, 2, spiReadStatus(spi, 2) | (1 << 1));
spiBegin(spi);
spiCommand(spi, 0x38); // Enter QPI Mode
@ -486,8 +636,14 @@ int spiRead(struct ff_spi *spi, uint32_t addr, uint8_t *data, unsigned int count
spiCommand(spi, addr >> 8);
spiCommand(spi, addr >> 0);
spiCommand(spi, 0x00);
for (i = 0; i < count; i++)
for (i = 0; i < count; i++) {
if ((i & 0x3fff) == 0) {
printf("\rReading @ %06x / %06x", i, count);
fflush(stdout);
}
data[i] = spiRx(spi);
}
printf("\rReading @ %06x / %06x Done\n", i, count);
spiEnd(spi);
return 0;
@ -495,10 +651,10 @@ int spiRead(struct ff_spi *spi, uint32_t addr, uint8_t *data, unsigned int count
static int spi_wait_for_not_busy(struct ff_spi *spi) {
uint8_t sr1;
sr1 = spiReadSr(spi, 1);
sr1 = spiReadStatus(spi, 1);
do {
sr1 = spiReadSr(spi, 1);
sr1 = spiReadStatus(spi, 1);
} while (sr1 & (1 << 0));
return 0;
}
@ -524,6 +680,9 @@ int spiWrite(struct ff_spi *spi, uint32_t addr, const uint8_t *data, unsigned in
// Erase all applicable blocks
uint32_t erase_addr;
for (erase_addr = 0; erase_addr < count; erase_addr += 32768) {
printf("\rErasing @ %06x / %06x", erase_addr, count);
fflush(stdout);
spiBegin(spi);
spiCommand(spi, 0x06);
spiEnd(spi);
@ -537,6 +696,7 @@ int spiWrite(struct ff_spi *spi, uint32_t addr, const uint8_t *data, unsigned in
spi_wait_for_not_busy(spi);
}
printf(" Done\n");
uint8_t write_cmd;
switch (spi->type) {
@ -555,11 +715,18 @@ int spiWrite(struct ff_spi *spi, uint32_t addr, const uint8_t *data, unsigned in
return 1;
}
int total = count;
while (count) {
printf("\rProgramming @ %06x / %06x", addr, total);
fflush(stdout);
spiBegin(spi);
spiCommand(spi, 0x06);
spiEnd(spi);
uint8_t sr1 = spiReadStatus(spi, 1);
if (!(sr1 & (1 << 1)))
fprintf(stderr, "error: write-enable latch (WEL) not set, write will probably fail\n");
spiBegin(spi);
spiCommand(spi, write_cmd);
spiCommand(spi, addr >> 16);
@ -572,6 +739,8 @@ int spiWrite(struct ff_spi *spi, uint32_t addr, const uint8_t *data, unsigned in
addr += i;
spi_wait_for_not_busy(spi);
}
printf("\rProgramming @ %06x / %06x", addr, total);
printf(" Done\n");
return 0;
}
@ -592,6 +761,11 @@ uint8_t spiReset(struct ff_spi *spi) {
spiEnd(spi);
usleep(30);
spiBegin(spi);
spiCommand(spi, 0xab); // "Resume from Deep Power-Down" command
spiEnd(spi);
return 0;
}
@ -599,7 +773,6 @@ int spiInit(struct ff_spi *spi) {
spi->state = SS_UNCONFIGURED;
spi->type = ST_UNCONFIGURED;
// Reset the SPI flash, which will return it to SPI mode even
// if it's in QPI mode.
spiReset(spi);
@ -608,9 +781,17 @@ int spiInit(struct ff_spi *spi) {
// Have the SPI flash pay attention to us
gpioWrite(spi->pins.hold, 1);
// Disable WP
gpioWrite(spi->pins.wp, 1);
spi_get_id(spi);
spi->quirks |= SQ_SR2_FROM_SR1;
// if (spi->id.manufacturer_id == 0x1f)
if (spi->id.manufacturer_id == 0xef)
spi->quirks |= SQ_SKIP_SR_WEL | SQ_SECURITY_NYBBLE_SHIFT;
return 0;
}

13
spi.h
View File

@ -42,6 +42,10 @@ struct spi_id {
uint8_t memory_size; // Result from 0x9f
uint8_t signature; // Result from 0xab
uint8_t serial[4]; // Result from 0x4b
int bytes; // -1 if unknown
const char *manufacturer;
const char *model;
const char *capacity;
};
struct ff_spi;
@ -59,11 +63,16 @@ void spiCommand(struct ff_spi *spi, uint8_t cmd);
//uint8_t spiQuadRx(struct ff_spi *spi);
int spiTx(struct ff_spi *spi, uint8_t word);
uint8_t spiRx(struct ff_spi *spi);
uint8_t spiReadSr(struct ff_spi *spi, int sr);
void spiWriteSr(struct ff_spi *spi, int sr, uint8_t val);
uint8_t spiReadStatus(struct ff_spi *spi, uint8_t sr);
void spiWriteStatus(struct ff_spi *spi, uint8_t sr, uint8_t val);
void spiReadSecurity(struct ff_spi *spi, uint8_t sr, uint8_t security[256]);
void spiWriteSecurity(struct ff_spi *spi, uint8_t sr, uint8_t security[256]);
int spiSetType(struct ff_spi *spi, enum spi_type type);
int spiRead(struct ff_spi *spi, uint32_t addr, uint8_t *data, unsigned int count);
struct spi_id spiId(struct ff_spi *spi);
void spiOverrideSize(struct ff_spi *spi, uint32_t new_size);
//int spi_wait_for_not_busy(struct ff_spi *spi);
int spiWrite(struct ff_spi *spi, uint32_t addr, const uint8_t *data, unsigned int count);
uint8_t spiReset(struct ff_spi *spi);