mirror of https://github.com/YosysHQ/icestorm.git
Speed up Pico programmer by switching to raw USB protocol
This update replaces the HID-based USB protocol with a vendor control transfer variant. The primary goal is speed- HID transfers are rate-limited to 1000/s, which gives a maximum transfer rate of 64kb/s. Unfortunately a majority of this bandwidth is wasted, and typical ice40 flash routines could take up to 17 seconds to complete. With the switch to a new protocol, the time is reduced to under 4 seconds.
This commit is contained in:
parent
ac1b22ffb9
commit
48ab6c3459
|
|
@ -8,8 +8,8 @@ else
|
|||
LDLIBS += $(shell for pkg in libftdi1 libftdi; do $(PKG_CONFIG) --silence-errors --libs $$pkg && exit; done; echo -lftdi; )
|
||||
CFLAGS += $(shell for pkg in libftdi1 libftdi; do $(PKG_CONFIG) --silence-errors --cflags $$pkg && exit; done; )
|
||||
|
||||
CFLAGS += -lhidapi-libusb
|
||||
LDLIBS += -lhidapi-libusb
|
||||
CFLAGS += -llibusb-1.0
|
||||
LDLIBS += -lusb-1.0
|
||||
endif
|
||||
|
||||
all: $(PROGRAM_PREFIX)iceprog$(EXE)
|
||||
|
|
|
|||
|
|
@ -376,7 +376,9 @@ static void flash_wait()
|
|||
flash_chip_deselect();
|
||||
|
||||
if ((data[1] & 0x01) == 0) {
|
||||
if (count < 2) {
|
||||
// TODO: Why check the return flag multiple times?
|
||||
// if (count < 2) {
|
||||
if (false) {
|
||||
count++;
|
||||
if (verbose) {
|
||||
fprintf(stderr, "r");
|
||||
|
|
|
|||
|
|
@ -2,19 +2,54 @@
|
|||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <hidapi/hidapi.h>
|
||||
#include <libusb-1.0/libusb.h>
|
||||
|
||||
#define VENDOR_ID 0xcafe
|
||||
#define PRODUCT_ID 0x4004
|
||||
#define VENDOR_ID 0xCAFE
|
||||
#define PRODUCT_ID 0x4010
|
||||
|
||||
static hid_device * handle;
|
||||
//static hid_device * handle;
|
||||
|
||||
libusb_context *ctx=NULL;
|
||||
struct libusb_device_handle *devhaccess;
|
||||
|
||||
// https://github.com/jerome-labidurie/avr/blob/master/fpusb/host-libusb/fpusb.c
|
||||
void usb_exit ( int sig )
|
||||
{
|
||||
libusb_release_interface (devhaccess, 0);
|
||||
libusb_close (devhaccess);
|
||||
libusb_exit (ctx);
|
||||
exit(sig);
|
||||
}
|
||||
|
||||
int usb_write(uint8_t request, uint8_t* data, int length) {
|
||||
return libusb_control_transfer ( devhaccess,
|
||||
0x40,
|
||||
request,
|
||||
0,
|
||||
0,
|
||||
data,
|
||||
length,
|
||||
1000);
|
||||
|
||||
}
|
||||
|
||||
int usb_read(uint8_t request, uint8_t* data, int length) {
|
||||
return libusb_control_transfer ( devhaccess,
|
||||
0xC0,
|
||||
request,
|
||||
0,
|
||||
0,
|
||||
data,
|
||||
length,
|
||||
1000);
|
||||
}
|
||||
|
||||
static void led_set(bool value) {
|
||||
|
||||
uint8_t buf[3];
|
||||
buf[0] = 0;
|
||||
buf[1] = 0x00;
|
||||
buf[2] = (value ? 1:0);
|
||||
// uint8_t buf[3];
|
||||
// buf[0] = 0;
|
||||
// buf[1] = 0x00;
|
||||
// buf[2] = (value ? 1:0);
|
||||
|
||||
// printf("led_set [");
|
||||
// for(int i = 0; i < sizeof(buf); i++) {
|
||||
|
|
@ -22,46 +57,36 @@ static void led_set(bool value) {
|
|||
// }
|
||||
// printf("]\n");
|
||||
|
||||
hid_write(handle, buf, sizeof(buf));
|
||||
// hid_write(handle, buf, sizeof(buf));
|
||||
}
|
||||
|
||||
static void pin_set_direction(uint8_t pin, bool direction) {
|
||||
const uint32_t mask = (1<<pin);
|
||||
const uint32_t val = ((direction?1:0)<<pin);
|
||||
|
||||
uint8_t buf[10];
|
||||
buf[0] = 0;
|
||||
buf[1] = 0x10;
|
||||
buf[2] = (mask >> 24) & 0xff;
|
||||
buf[3] = (mask >> 16) & 0xff;
|
||||
buf[4] = (mask >> 8) & 0xff;
|
||||
buf[5] = (mask >> 0) & 0xff;
|
||||
buf[6] = (val >> 24) & 0xff;
|
||||
buf[7] = (val >> 16) & 0xff;
|
||||
buf[8] = (val >> 8) & 0xff;
|
||||
buf[9] = (val >> 0) & 0xff;
|
||||
uint8_t buf[8];
|
||||
buf[0] = (mask >> 24) & 0xff;
|
||||
buf[1] = (mask >> 16) & 0xff;
|
||||
buf[2] = (mask >> 8) & 0xff;
|
||||
buf[3] = (mask >> 0) & 0xff;
|
||||
buf[4] = (val >> 24) & 0xff;
|
||||
buf[5] = (val >> 16) & 0xff;
|
||||
buf[6] = (val >> 8) & 0xff;
|
||||
buf[7] = (val >> 0) & 0xff;
|
||||
|
||||
// printf("pin_set_direction [");
|
||||
// for(int i = 0; i < sizeof(buf); i++) {
|
||||
// printf("%02x, ", buf[i]);
|
||||
// }
|
||||
// printf("]\n");
|
||||
|
||||
hid_write(handle, buf, sizeof(buf));
|
||||
usb_write(0x10, buf, sizeof(buf));
|
||||
}
|
||||
|
||||
static void pinmask_write(uint32_t mask, uint32_t val) {
|
||||
uint8_t buf[10];
|
||||
buf[0] = 0;
|
||||
buf[1] = 0x20;
|
||||
buf[2] = (mask >> 24) & 0xff;
|
||||
buf[3] = (mask >> 16) & 0xff;
|
||||
buf[4] = (mask >> 8) & 0xff;
|
||||
buf[5] = (mask >> 0) & 0xff;
|
||||
buf[6] = (val >> 24) & 0xff;
|
||||
buf[7] = (val >> 16) & 0xff;
|
||||
buf[8] = (val >> 8) & 0xff;
|
||||
buf[9] = (val >> 0) & 0xff;
|
||||
uint8_t buf[8];
|
||||
buf[0] = (mask >> 24) & 0xff;
|
||||
buf[1] = (mask >> 16) & 0xff;
|
||||
buf[2] = (mask >> 8) & 0xff;
|
||||
buf[3] = (mask >> 0) & 0xff;
|
||||
buf[4] = (val >> 24) & 0xff;
|
||||
buf[5] = (val >> 16) & 0xff;
|
||||
buf[6] = (val >> 8) & 0xff;
|
||||
buf[7] = (val >> 0) & 0xff;
|
||||
|
||||
// printf("pin_write [");
|
||||
// for(int i = 0; i < sizeof(buf); i++) {
|
||||
|
|
@ -69,8 +94,7 @@ static void pinmask_write(uint32_t mask, uint32_t val) {
|
|||
// }
|
||||
// printf("]\n");
|
||||
|
||||
hid_write(handle, buf, sizeof(buf));
|
||||
|
||||
usb_write(0x20, buf, sizeof(buf));
|
||||
}
|
||||
|
||||
static void pin_write(uint8_t pin, bool value) {
|
||||
|
|
@ -81,24 +105,8 @@ static void pin_write(uint8_t pin, bool value) {
|
|||
}
|
||||
|
||||
static bool pin_read(uint8_t pin) {
|
||||
uint8_t buf[2];
|
||||
buf[0] = 0;
|
||||
buf[1] = 0x30;
|
||||
|
||||
// printf("pin_read [");
|
||||
// for(int i = 0; i < sizeof(buf); i++) {
|
||||
// printf("%02x, ", buf[i]);
|
||||
// }
|
||||
// printf("]\n");
|
||||
|
||||
hid_write(handle, buf, sizeof(buf));
|
||||
|
||||
uint8_t ret_buf[5];
|
||||
const int bytes_read = hid_read_timeout(handle, ret_buf, sizeof(ret_buf), 400);
|
||||
if(bytes_read != sizeof(ret_buf)) {
|
||||
printf("error reading, got:%i\n", bytes_read);
|
||||
exit(1);
|
||||
}
|
||||
uint8_t ret_buf[4];
|
||||
usb_read(0x30, ret_buf, sizeof(ret_buf));
|
||||
|
||||
// printf(" ret=[");
|
||||
// for(int i = 0; i < sizeof(ret_buf); i++) {
|
||||
|
|
@ -107,69 +115,49 @@ static bool pin_read(uint8_t pin) {
|
|||
// printf("]\n");
|
||||
|
||||
uint32_t pins =
|
||||
(ret_buf[1] << 24)
|
||||
| (ret_buf[2] << 16)
|
||||
| (ret_buf[3] << 8)
|
||||
| (ret_buf[4] << 0);
|
||||
(ret_buf[0] << 24)
|
||||
| (ret_buf[1] << 16)
|
||||
| (ret_buf[2] << 8)
|
||||
| (ret_buf[3] << 0);
|
||||
|
||||
return (pins & (1<<pin));
|
||||
}
|
||||
|
||||
#define MAX_BYTES_PER_TRANSFER (64-8)
|
||||
//#define MAX_BYTES_PER_TRANSFER (256-7)
|
||||
#define MAX_BYTES_PER_TRANSFER (1024-8)
|
||||
|
||||
static void bitbang_spi(
|
||||
uint8_t sck_pin,
|
||||
uint8_t mosi_pin,
|
||||
uint8_t miso_pin,
|
||||
uint32_t bit_count,
|
||||
uint8_t* buffer) {
|
||||
uint8_t* buf_out,
|
||||
uint8_t* buf_in) {
|
||||
|
||||
const int byte_count = ((bit_count + 7) / 8);
|
||||
const uint32_t byte_count = ((bit_count + 7) / 8);
|
||||
if(byte_count > MAX_BYTES_PER_TRANSFER) {
|
||||
printf("bit count too high\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
// printf("bitbang_spi byte_count:%i bit_count:%i\n", byte_count, bit_count);
|
||||
|
||||
uint8_t buf[9+MAX_BYTES_PER_TRANSFER];
|
||||
uint8_t buf[7+MAX_BYTES_PER_TRANSFER];
|
||||
memset(buf, 0xFF, sizeof(buf));
|
||||
|
||||
buf[0] = 0;
|
||||
buf[1] = 0x40;
|
||||
buf[2] = sck_pin;
|
||||
buf[3] = mosi_pin;
|
||||
buf[4] = miso_pin;
|
||||
buf[5] = (bit_count >> 24) & 0xff;
|
||||
buf[6] = (bit_count >> 16) & 0xff;
|
||||
buf[7] = (bit_count >> 8) & 0xff;
|
||||
buf[8] = (bit_count >> 0) & 0xff;
|
||||
buf[0] = sck_pin;
|
||||
buf[1] = mosi_pin;
|
||||
buf[2] = miso_pin;
|
||||
buf[3] = (bit_count >> 24) & 0xff;
|
||||
buf[4] = (bit_count >> 16) & 0xff;
|
||||
buf[5] = (bit_count >> 8) & 0xff;
|
||||
buf[6] = (bit_count >> 0) & 0xff;
|
||||
|
||||
|
||||
memcpy(&buf[9], buffer, byte_count); // TODO: memory length
|
||||
memcpy(&buf[7], buf_out, byte_count);
|
||||
|
||||
// printf(" send:[");
|
||||
// for(int i = 0; i < (8+byte_count); i++) {
|
||||
// printf("%02x, ", buf[i]);
|
||||
// }
|
||||
// printf("]\n");
|
||||
usb_write(0x40, buf, byte_count+7);
|
||||
|
||||
uint8_t ret_buf[64];
|
||||
|
||||
hid_write(handle, buf, sizeof(buf));
|
||||
|
||||
const int bytes_read = hid_read_timeout(handle, ret_buf, 64, 4000);
|
||||
if(bytes_read != 64) {
|
||||
printf("error reading, got:%i\n", bytes_read);
|
||||
exit(1);
|
||||
if(buf_in != NULL) {
|
||||
usb_read(0x40, buf_in, byte_count);
|
||||
}
|
||||
|
||||
// printf(" receive:[");
|
||||
// for(int i = 0; i < (5+byte_count); i++) {
|
||||
// printf("%02x, ", ret_buf[i]);
|
||||
// }
|
||||
// printf("]\n");
|
||||
memcpy(buffer, &ret_buf[5], byte_count);
|
||||
}
|
||||
|
||||
#define PIN_POWER 7
|
||||
|
|
@ -195,8 +183,9 @@ static void close() {
|
|||
led_set(false);
|
||||
printf("closing\n");
|
||||
|
||||
hid_close(handle);
|
||||
handle = NULL;
|
||||
libusb_release_interface (devhaccess, 0);
|
||||
libusb_close (devhaccess);
|
||||
libusb_exit (ctx);
|
||||
}
|
||||
|
||||
// TODO
|
||||
|
|
@ -221,44 +210,33 @@ static bool get_cdone(void) {
|
|||
// TODO
|
||||
static uint8_t xfer_spi_bits(uint8_t data, int n) {
|
||||
uint8_t buf = data;
|
||||
bitbang_spi(PIN_SCK, PIN_MOSI, PIN_MISO, n, &buf);
|
||||
bitbang_spi(PIN_SCK, PIN_MOSI, PIN_MISO, n, &buf, &buf);
|
||||
|
||||
return buf;
|
||||
}
|
||||
|
||||
// TODO
|
||||
static void xfer_spi(uint8_t *data, int n) {
|
||||
// printf("xfer_spi %i\n",n);
|
||||
for(int byte_index = 0; byte_index < n;) {
|
||||
int bytes_to_transfer = n - byte_index;
|
||||
if(bytes_to_transfer > MAX_BYTES_PER_TRANSFER) {
|
||||
bytes_to_transfer = MAX_BYTES_PER_TRANSFER;
|
||||
}
|
||||
// printf(" byte_index:%i bytes_to_transfer:%i\n", byte_index, bytes_to_transfer);
|
||||
|
||||
bitbang_spi(PIN_SCK, PIN_MOSI, PIN_MISO, bytes_to_transfer*8, data+byte_index);
|
||||
byte_index += bytes_to_transfer;
|
||||
}
|
||||
bitbang_spi(PIN_SCK, PIN_MOSI, PIN_MISO, n*8, data, data);
|
||||
}
|
||||
|
||||
// TODO
|
||||
static void send_spi(uint8_t *data, int n) {
|
||||
uint8_t buf[n];
|
||||
memcpy(buf,data,sizeof(buf));
|
||||
xfer_spi(buf,n);
|
||||
bitbang_spi(PIN_SCK, PIN_MOSI, PIN_MISO, n*8, data, NULL);
|
||||
}
|
||||
|
||||
// TODO
|
||||
static void send_dummy_bytes(uint8_t n) {
|
||||
uint8_t buf[n];
|
||||
memset(buf, 0, sizeof(buf));
|
||||
xfer_spi(buf, sizeof(buf));
|
||||
send_spi(buf, sizeof(buf));
|
||||
}
|
||||
|
||||
|
||||
// TODO
|
||||
static void send_dummy_bit(void) {
|
||||
xfer_spi_bits(0, 1);
|
||||
uint8_t buf = 0;
|
||||
bitbang_spi(PIN_SCK, PIN_MOSI, PIN_MISO, 1, &buf, NULL);
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -278,16 +256,35 @@ const interface_t rpi_pico_interface = {
|
|||
};
|
||||
|
||||
void rpi_pico_interface_init() {
|
||||
hid_init();
|
||||
|
||||
handle = hid_open( VENDOR_ID, PRODUCT_ID, NULL);
|
||||
if(handle == NULL) {
|
||||
printf("Failure!\n");
|
||||
if (libusb_init(&ctx) != 0) {
|
||||
printf("failure!\n");
|
||||
|
||||
hid_exit();
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
if ( (devhaccess = libusb_open_device_with_vid_pid (ctx, VENDOR_ID, PRODUCT_ID)) == 0) {
|
||||
printf("libusb_open_device_with_vid_pid error\n");
|
||||
libusb_exit(ctx);
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
if (libusb_claim_interface (devhaccess, 0) != 0) {
|
||||
perror ("libusb_claim_interface error");
|
||||
usb_exit(-1);
|
||||
}
|
||||
|
||||
|
||||
|
||||
// hid_init();
|
||||
|
||||
// handle = hid_open( VENDOR_ID, PRODUCT_ID, NULL);
|
||||
// if(handle == NULL) {
|
||||
// printf("failure!\n");
|
||||
//
|
||||
// hid_exit();
|
||||
// exit(-1);
|
||||
// }
|
||||
|
||||
led_set(true);
|
||||
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue