commit 9c107de586e66b05777bda0cffcd848eeef6762d
parent 6b430cd8294e52cd6ba8e997b9cad4ffc55da173
Author: Brian Swetland <swetland@frotz.net>
Date: Wed, 30 Dec 2015 18:43:52 -0800
icetool: download data to target fpga over SPI (via FTDI MSSPE)
Diffstat:
M | Makefile | | | 6 | +++++- |
A | src/ftdi.c | | | 224 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
A | src/ftdi.h | | | 54 | ++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
A | src/icetool.c | | | 125 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
4 files changed, 408 insertions(+), 1 deletion(-)
diff --git a/Makefile b/Makefile
@@ -7,7 +7,7 @@ VERILATOR := VERILATOR_ROOT=/work/verilator /work/verilator/bin/verilator
VOPTS := --top-module testbench --Mdir out --exe ../src/testbench.cpp --cc -CFLAGS -DTRACE --trace
-all: out/Vtestbench out/a16 out/d16
+all: out/Vtestbench out/a16 out/d16 out/icetool
out/Vtestbench: $(SRCS) src/testbench.cpp
@mkdir -p out
@@ -31,6 +31,10 @@ out/d16: src/d16.c
@mkdir -p out
gcc -g -Wall -O1 -o out/d16 -DSTANDALONE=1 src/d16.c
+out/icetool: src/icetool.c src/ftdi.c src/ftdi.h
+ @mkdir -p out
+ gcc -g -Wall -O1 -o out/icetool src/icetool.c src/ftdi.c -lusb-1.0 -lrt
+
TEST_DEPS := out/Vtestbench out/a16 out/d16 tests/runtest
TESTS := $(wildcard tests/*.s)
diff --git a/src/ftdi.c b/src/ftdi.c
@@ -0,0 +1,224 @@
+// Copyright 2015 Brian Swetland <swetland@frotz.net>
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "ftdi.h"
+
+#include <libusb-1.0/libusb.h>
+
+// FTDI MPSSE Device Info
+static struct {
+ u16 vid;
+ u16 pid;
+ u8 ep_in;
+ u8 ep_out;
+ const char *name;
+} devinfo[] = {
+ { 0x0403, 0x6010, 0x81, 0x02, "ftdi" },
+ { 0x0403, 0x6014, 0x81, 0x02, "ftdi" },
+ { 0x0000, 0x0000, 0},
+};
+
+#define FTDI_GPIO_READ_LO 0x81
+#define FTDI_GPIO_READ_HI 0x83
+#define FTDI_GPIO_WRITE_LO 0x80 // Val Dir (1=Out 0=In)
+#define FTDI_GPIO_WRITE_HI 0x82 // Val Dir (1=Out 0=In)
+
+#define FTDI_LOOPBACK_OFF 0x85
+#define FTDI_CLOCK_DIV_1 0x8A
+#define FTDI_CLOCK_DIV_5 0x8B
+#define FTDI_DIVISOR_SET 0x86
+
+// TN = TX on Negative Clock Edge
+// TP = TX on Positive CLock Edge
+// byte transfers followed by LenLo LenHi (len-1) then data
+#define FTDI_MSB_TX_BYTES_TP 0x10
+#define FTDI_MSB_TX_BYTES_TN 0x11 //
+#define FTDI_MSB_IO_BYTES_TN_RP 0x31 //
+#define FTDI_MSB_IO_BYTES_TP_RN 0x34
+
+#define FTDI_LSB_TX_BYTES_TP 0x18
+#define FTDI_LSB_TX_BYTES_TN 0x19
+#define FTDI_LSB_IO_BYTES_TN_RP 0x39
+#define FTDI_LSB_IO_BYTES_TP_RN 0x3C
+
+struct FTDI {
+ struct libusb_device_handle *udev;
+ u8 ep_in;
+ u8 ep_out;
+ u32 read_count;
+ u32 read_size;
+ u8 *read_ptr;
+ u8 read_buffer[512];
+};
+
+#define FTDI_REQTYPE_OUT (LIBUSB_REQUEST_TYPE_VENDOR \
+ | LIBUSB_RECIPIENT_DEVICE | LIBUSB_ENDPOINT_OUT)
+#define FTDI_CTL_RESET 0x00
+#define FTDI_CTL_SET_BITMODE 0x0B
+#define FTDI_CTL_SET_EVENT_CH 0x06
+#define FTDI_CTL_SET_ERROR_CH 0x07
+
+#define FTDI_IFC_A 1
+#define FTDI_IFC_B 2
+
+static int ftdi_reset(FTDI *d) {
+ struct libusb_device_handle *udev = d->udev;
+ if (libusb_control_transfer(udev, FTDI_REQTYPE_OUT, FTDI_CTL_RESET,
+ 0, FTDI_IFC_A, NULL, 0, 10000) < 0) {
+ fprintf(stderr, "ftdi: reset failed\n");
+ return -1;
+ }
+ return 0;
+}
+
+static int ftdi_mpsse_enable(FTDI *d) {
+ struct libusb_device_handle *udev = d->udev;
+ if (libusb_control_transfer(udev, FTDI_REQTYPE_OUT, FTDI_CTL_SET_BITMODE,
+ 0x0000, FTDI_IFC_A, NULL, 0, 10000) < 0) {
+ fprintf(stderr, "ftdi: set bitmode failed\n");
+ return -1;
+ }
+ if (libusb_control_transfer(udev, FTDI_REQTYPE_OUT, FTDI_CTL_SET_BITMODE,
+ 0x020b, FTDI_IFC_A, NULL, 0, 10000) < 0) {
+ fprintf(stderr, "ftdi: set bitmode failed\n");
+ return -1;
+ }
+ if (libusb_control_transfer(udev, FTDI_REQTYPE_OUT, FTDI_CTL_SET_EVENT_CH,
+ 0, FTDI_IFC_A, NULL, 0, 10000) < 0) {
+ fprintf(stderr, "ftdi: disable event character failed\n");
+ return -1;
+ }
+ return 0;
+ if (libusb_control_transfer(udev, FTDI_REQTYPE_OUT, FTDI_CTL_SET_ERROR_CH,
+ 0, FTDI_IFC_A, NULL, 0, 10000) < 0) {
+ fprintf(stderr, "ftdi: disable error character failed\n");
+ return -1;
+ }
+ return 0;
+}
+
+static int ftdi_init(FTDI *d) {
+ struct libusb_device_handle *udev;
+ int n;
+
+ if (libusb_init(NULL) < 0) {
+ fprintf(stderr, "ftdi_open: failed to init libusb\n");
+ return -1;
+ }
+ for (n = 0; devinfo[n].name; n++) {
+ udev = libusb_open_device_with_vid_pid(NULL,
+ devinfo[n].vid, devinfo[n].pid);
+ if (udev == 0)
+ continue;
+ libusb_detach_kernel_driver(udev, 0);
+ if (libusb_claim_interface(udev, 0) < 0) {
+ //TODO: close
+ continue;
+ }
+ d->udev = udev;
+ d->read_ptr = d->read_buffer;
+ d->read_size = 512;
+ d->read_count = 0;
+ d->ep_in = devinfo[n].ep_in;
+ d->ep_out = devinfo[n].ep_out;
+ return 0;
+ }
+ fprintf(stderr, "ftdi_open: failed to find usb device\n");
+ return -1;
+}
+
+static int usb_bulk(struct libusb_device_handle *udev,
+ unsigned char ep, void *data, int len, unsigned timeout) {
+ int r, xfer;
+ r = libusb_bulk_transfer(udev, ep, data, len, &xfer, timeout);
+ if (r < 0) {
+ fprintf(stderr,"bulk: error: %d\n", r);
+ return r;
+ }
+ return xfer;
+}
+
+/* TODO: handle smaller packet size for lowspeed version of the part */
+/* TODO: multi-packet reads */
+/* TODO: asynch/background reads */
+int ftdi_read(FTDI *d, unsigned char *buffer, int count, int timeout) {
+ int xfer;
+ while (count > 0) {
+ if (d->read_count >= count) {
+ memcpy(buffer, d->read_ptr, count);
+ d->read_count -= count;
+ d->read_ptr += count;
+ return 0;
+ }
+ if (d->read_count > 0) {
+ memcpy(buffer, d->read_ptr, d->read_count);
+ count -= d->read_count;
+ buffer += d->read_count;
+ d->read_count = 0;
+ }
+ xfer = usb_bulk(d->udev, d->ep_in, d->read_buffer, d->read_size, 1000);
+ if (xfer < 0)
+ return -1;
+ if (xfer < 2)
+ return -1;
+ /* discard header */
+ d->read_ptr = d->read_buffer + 2;
+ d->read_count = xfer - 2;
+ }
+ return 0;
+}
+
+
+FTDI *ftdi_open(void) {
+ FTDI *d;
+ if ((d = malloc(sizeof(FTDI))) == NULL) {
+ return NULL;
+ }
+ if (ftdi_init(d))
+ goto fail0;
+ if (ftdi_reset(d))
+ goto fail1;
+ if (ftdi_mpsse_enable(d))
+ goto fail1;
+ return d;
+fail1:
+ libusb_close(d->udev);
+fail0:
+ // close?
+ free(d);
+ return NULL;
+}
+
+int ftdi_send(FTDI *d, void *data, int len, int timeout_ms) {
+ return usb_bulk(d->udev, d->ep_out, data, len, timeout_ms);
+}
+
+int ftdi_gpio_set_lo(FTDI *d, u8 val, u8 out) {
+ u8 cmd[3] = { FTDI_GPIO_WRITE_LO, val, out };
+ return ftdi_send(d, cmd, 3, 1000);
+}
+
+int ftdi_gpio_get_lo(FTDI *d) {
+ u8 cmd[1] = { FTDI_GPIO_READ_LO };
+ if (ftdi_send(d, cmd, 1, 1000) < 0)
+ return -1;
+ if (ftdi_read(d, cmd, 1, 1000) < 0)
+ return -1;
+ return cmd[0];
+}
+
diff --git a/src/ftdi.h b/src/ftdi.h
@@ -0,0 +1,54 @@
+// Copyright 2015 Brian Swetland <swetland@frotz.net>
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef _FTDI_H_
+#define _FTDI_H_
+
+typedef unsigned char u8;
+typedef unsigned short u16;
+typedef unsigned int u32;
+
+#define FTDI_GPIO_READ_LO 0x81
+#define FTDI_GPIO_READ_HI 0x83
+#define FTDI_GPIO_WRITE_LO 0x80 // Val Dir (1=Out 0=In)
+#define FTDI_GPIO_WRITE_HI 0x82 // Val Dir (1=Out 0=In)
+
+#define FTDI_LOOPBACK_OFF 0x85
+#define FTDI_CLOCK_DIV_1 0x8A
+#define FTDI_CLOCK_DIV_5 0x8B
+#define FTDI_DIVISOR_SET 0x86
+
+// TN = TX on Negative Clock Edge
+// TP = TX on Positive CLock Edge
+// byte transfers followed by LenLo LenHi (len-1) then data
+#define FTDI_MSB_TX_BYTES_TP 0x10
+#define FTDI_MSB_TX_BYTES_TN 0x11 //
+#define FTDI_MSB_IO_BYTES_TN_RP 0x31 //
+#define FTDI_MSB_IO_BYTES_TP_RN 0x34
+
+#define FTDI_LSB_TX_BYTES_TP 0x18
+#define FTDI_LSB_TX_BYTES_TN 0x19
+#define FTDI_LSB_IO_BYTES_TN_RP 0x39
+#define FTDI_LSB_IO_BYTES_TP_RN 0x3C
+
+typedef struct FTDI FTDI;
+
+FTDI *ftdi_open(void);
+int ftdi_read(FTDI *d, unsigned char *data, int count, int timeout_ms);
+int ftdi_send(FTDI *ftdi, void *data, int len, int timeout_ms);
+
+int ftdi_gpio_set_lo(FTDI *d, u8 val, u8 out);
+int ftdi_gpio_get_lo(FTDI *d);
+
+#endif
diff --git a/src/icetool.c b/src/icetool.c
@@ -0,0 +1,125 @@
+// Copyright 2015 Brian Swetland <swetland@frotz.net>
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <ctype.h>
+
+#include "ftdi.h"
+
+#define GPIO_CS_N 0x10
+#define GPIO_CDONE 0x40
+#define GPIO_CRESET_N 0x80
+
+#define GPIO_DIR 0x93
+
+static unsigned char mpsse_init[] = {
+ FTDI_LOOPBACK_OFF,
+ FTDI_CLOCK_DIV_5,
+ FTDI_DIVISOR_SET, 0x00, 0x00, // 6MHz
+ FTDI_GPIO_WRITE_LO, 1 | GPIO_CS_N | GPIO_CRESET_N, GPIO_DIR,
+};
+
+// send CS1 8x0 CS0 <data> CS1 8x0
+int spi_tx(FTDI *d, void *data, int len) {
+ u8 cmd[13 + 4096 + 7];
+
+ // RST=1 CS=1
+ cmd[0] = FTDI_GPIO_WRITE_LO;
+ cmd[1] = 1 | GPIO_CRESET_N | GPIO_CS_N;
+ cmd[2] = GPIO_DIR;
+ // TX 8x 0
+ cmd[3] = FTDI_LSB_TX_BYTES_TN,
+ cmd[4] = 0;
+ cmd[5] = 0;
+ cmd[6] = 0;
+ // RST=1 CS=0
+ cmd[7] = FTDI_GPIO_WRITE_LO;
+ cmd[8] = 1 | GPIO_CRESET_N;
+ cmd[9] = GPIO_DIR;
+ // TX n
+ cmd[10] = FTDI_LSB_TX_BYTES_TN,
+ cmd[11] = len - 1;
+ cmd[12] = (len - 1) >> 8;
+
+ if (len > 4096)
+ return -1;
+ memcpy(cmd + 13, data, len);
+ memcpy(cmd + 13 + len, cmd, 7);
+ if (ftdi_send(d, cmd, 13 + len + 7, 1000))
+ return -1;
+ return 0;
+}
+
+void usage(void) {
+ fprintf(stderr,
+ "usage: icetool -load <addr> <hexfile>\n"
+ " icetool -write <addr> <value>...\n"
+ );
+ exit(1);
+}
+
+int main(int argc, char **argv) {
+ u16 addr;
+ u16 data[2049];
+ int n;
+ FTDI *d;
+
+ if (argc < 4) {
+ usage();
+ }
+ if (!strcmp(argv[1], "-load")) {
+ char buf[1024];
+ addr = strtoul(argv[2], 0, 16);
+ FILE *fp = fopen(argv[3], "r");
+ if (fp == NULL) {
+ fprintf(stderr, "cannot open '%s'\n", argv[3]);
+ return 1;
+ }
+ n = 0;
+ while (fgets(buf, 1024, fp)) {
+ if (!isalnum(buf[0])) continue;
+ if (n == 2048) return 1;
+ data[1 + n++] = strtoul(buf, 0, 16);
+ }
+ fclose(fp);
+ } else if (!strcmp(argv[1], "-write")) {
+ addr = strtoul(argv[2], 0, 16);
+ argc -= 3;
+ argv += 3;
+ n = 0;
+ while (argc > 0) {
+ if (n == 2048) return 1;
+ data[1 + n++] = strtoul(argv[0], 0, 16);
+ argc--;
+ argv++;
+ }
+ } else {
+ usage();
+ }
+ data[0] = addr;
+
+ if ((d = ftdi_open()) == NULL) {
+ return 1;
+ }
+ if (ftdi_send(d, mpsse_init, sizeof(mpsse_init), 1000) < 0) {
+ return 1;
+ }
+
+ spi_tx(d, data, (n + 1) * 2);
+
+ return 0;
+}