aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDaniel Anselmi <danselmi@gmx.ch>2022-12-12 09:49:51 +0100
committerAntonio Borneo <borneo.antonio@gmail.com>2023-04-30 14:53:25 +0000
commitd35faaa35cd5d56fa946d194d7cf780127a3f8c8 (patch)
treedffcb19ad0922a7ad78012bf74fb5cfa3e95bad5
parent8670ad4caa705c460972badbd0fc28aa98c41866 (diff)
downloadriscv-openocd-d35faaa35cd5d56fa946d194d7cf780127a3f8c8.zip
riscv-openocd-d35faaa35cd5d56fa946d194d7cf780127a3f8c8.tar.gz
riscv-openocd-d35faaa35cd5d56fa946d194d7cf780127a3f8c8.tar.bz2
pld: add support for lattice ecp2 and ecp3 devices
Change-Id: I29c227c37be464f7ecc97a30d9cf3da1442e2b7f Signed-off-by: Daniel Anselmi <danselmi@gmx.ch> Reviewed-on: https://review.openocd.org/c/openocd/+/7396 Reviewed-by: Antonio Borneo <borneo.antonio@gmail.com> Tested-by: jenkins
-rw-r--r--doc/openocd.texi32
-rw-r--r--src/pld/Makefile.am8
-rw-r--r--src/pld/ecp2_3.c250
-rw-r--r--src/pld/ecp2_3.h19
-rw-r--r--src/pld/lattice.c435
-rw-r--r--src/pld/lattice.h32
-rw-r--r--src/pld/lattice_bit.c105
-rw-r--r--src/pld/lattice_bit.h33
-rw-r--r--src/pld/pld.c2
-rw-r--r--src/pld/raw_bit.c55
-rw-r--r--src/pld/raw_bit.h21
-rw-r--r--tcl/fpga/lattice_ecp2.cfg31
-rw-r--r--tcl/fpga/lattice_ecp3.cfg22
13 files changed, 1044 insertions, 1 deletions
diff --git a/doc/openocd.texi b/doc/openocd.texi
index 4154e56..2d94de8 100644
--- a/doc/openocd.texi
+++ b/doc/openocd.texi
@@ -8488,13 +8488,43 @@ openocd -f board/digilent_zedboard.cfg -c "init" \
@end example
-
@deffn {Command} {virtex2 read_stat} num
Reads and displays the Virtex-II status register (STAT)
for FPGA @var{num}.
@end deffn
@end deffn
+
+
+@deffn {FPGA Driver} {lattice} [family]
+The FGPA families ECP2 and ECP3 by Lattice are supported.
+This driver can be used to load the bitstream into the FPGA or read the status register and read/write the usercode register.
+
+The option @option{family} is one of @var{ecp2 ecp3}. This is needed when the JTAG ID of the device is not known by openocd (newer NX devices).
+
+@deffn {Command} {lattice read_status} num
+Reads and displays the status register
+for FPGA @var{num}.
+@end deffn
+
+@deffn {Command} {lattice read_user} num
+Reads and displays the user register
+for FPGA @var{num}.
+@end deffn
+
+@deffn {Command} {lattice write_user} num val
+Writes the user register.
+for FPGA @var{num} with value @var{val}.
+@end deffn
+
+@deffn {Command} {lattice set_preload} num length
+Set the length of the register for the preload. This is needed when the JTAG ID of the device is not known by openocd (newer NX devices).
+The load command for the FPGA @var{num} will use a length for the preload of @var{length}.
+@end deffn
+@end deffn
+
+
+
@node General Commands
@chapter General Commands
@cindex commands
diff --git a/src/pld/Makefile.am b/src/pld/Makefile.am
index 14786af..7cff09e 100644
--- a/src/pld/Makefile.am
+++ b/src/pld/Makefile.am
@@ -2,9 +2,17 @@
noinst_LTLIBRARIES += %D%/libpld.la
%C%_libpld_la_SOURCES = \
+ %D%/ecp2_3.c \
+ %D%/lattice.c \
+ %D%/lattice_bit.c \
%D%/pld.c \
+ %D%/raw_bit.c \
%D%/xilinx_bit.c \
%D%/virtex2.c \
+ %D%/ecp2_3.h \
+ %D%/lattice.h \
+ %D%/lattice_bit.h \
%D%/pld.h \
+ %D%/raw_bit.h \
%D%/xilinx_bit.h \
%D%/virtex2.h
diff --git a/src/pld/ecp2_3.c b/src/pld/ecp2_3.c
new file mode 100644
index 0000000..6826d0b
--- /dev/null
+++ b/src/pld/ecp2_3.c
@@ -0,0 +1,250 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+/***************************************************************************
+ * Copyright (C) 2022 by Daniel Anselmi *
+ * danselmi@gmx.ch *
+ ***************************************************************************/
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "lattice.h"
+
+#define LSCC_REFRESH 0x23
+#define ISC_ENABLE 0x15
+#define LSCC_RESET_ADDRESS 0x21
+#define ISC_PROGRAM_USERCODE 0x1A
+#define ISC_ERASE 0x03
+#define READ_USERCODE 0x17
+#define ISC_DISABLE 0x1E
+#define LSCC_READ_STATUS 0x53
+#define LSCC_BITSTREAM_BURST 0x02
+
+#define STATUS_DONE_BIT 0x00020000
+#define STATUS_ERROR_BITS_ECP2 0x00040003
+#define STATUS_ERROR_BITS_ECP3 0x00040007
+#define REGISTER_ALL_BITS_1 0xffffffff
+#define REGISTER_ALL_BITS_0 0x00000000
+
+int lattice_ecp2_3_read_status(struct jtag_tap *tap, uint32_t *status, uint32_t out, bool do_idle)
+{
+ return lattice_read_u32_register(tap, LSCC_READ_STATUS, status, out, do_idle);
+}
+
+int lattice_ecp2_3_read_usercode(struct jtag_tap *tap, uint32_t *usercode, uint32_t out)
+{
+ return lattice_read_u32_register(tap, READ_USERCODE, usercode, out, false);
+}
+
+int lattice_ecp2_3_write_usercode(struct lattice_pld_device *lattice_device, uint32_t usercode)
+{
+ struct jtag_tap *tap = lattice_device->tap;
+ if (!tap)
+ return ERROR_FAIL;
+
+ int retval = lattice_set_instr(tap, ISC_ENABLE, TAP_IDLE);
+ if (retval != ERROR_OK)
+ return retval;
+ jtag_add_runtest(5, TAP_IDLE);
+ jtag_add_sleep(20000);
+
+ retval = lattice_set_instr(tap, ISC_PROGRAM_USERCODE, TAP_IDLE);
+ if (retval != ERROR_OK)
+ return retval;
+
+ struct scan_field field;
+ uint8_t buffer[4];
+ h_u32_to_le(buffer, usercode);
+ field.num_bits = 32;
+ field.out_value = buffer;
+ field.in_value = NULL;
+ jtag_add_dr_scan(tap, 1, &field, TAP_IDLE);
+ jtag_add_runtest(5, TAP_IDLE);
+ jtag_add_sleep(2000);
+
+ retval = lattice_set_instr(tap, ISC_DISABLE, TAP_IDLE);
+ if (retval != ERROR_OK)
+ return retval;
+ jtag_add_runtest(5, TAP_IDLE);
+ jtag_add_sleep(200000);
+
+ retval = jtag_execute_queue();
+ if (retval != ERROR_OK)
+ return retval;
+ return lattice_verify_usercode(lattice_device, 0x0, usercode, REGISTER_ALL_BITS_1);
+}
+
+static int lattice_ecp2_3_erase_device(struct lattice_pld_device *lattice_device)
+{
+ struct jtag_tap *tap = lattice_device->tap;
+ if (!tap)
+ return ERROR_FAIL;
+
+ /* program user code with all bits set */
+ int retval = lattice_set_instr(tap, ISC_PROGRAM_USERCODE, TAP_IRPAUSE);
+ if (retval != ERROR_OK)
+ return retval;
+ struct scan_field field;
+ uint8_t buffer[4] = {0xff, 0xff, 0xff, 0xff};
+ field.num_bits = 32;
+ field.out_value = buffer;
+ field.in_value = NULL;
+ jtag_add_dr_scan(tap, 1, &field, TAP_IDLE);
+ jtag_add_runtest(5, TAP_IDLE);
+ jtag_add_sleep(2000);
+
+ /* verify every bit is set */
+ const uint32_t out = REGISTER_ALL_BITS_1;
+ const uint32_t mask = REGISTER_ALL_BITS_1;
+ const uint32_t expected_pre = REGISTER_ALL_BITS_1;
+ retval = lattice_verify_usercode(lattice_device, out, expected_pre, mask);
+ if (retval != ERROR_OK)
+ return retval;
+
+ retval = lattice_set_instr(tap, ISC_ERASE, TAP_IDLE);
+ if (retval != ERROR_OK)
+ return retval;
+ jtag_add_runtest(5, TAP_IDLE);
+ if (lattice_device->family == LATTICE_ECP2)
+ jtag_add_sleep(100000);
+ else
+ jtag_add_sleep(2000000);
+
+ retval = lattice_set_instr(tap, LSCC_RESET_ADDRESS, TAP_IDLE);
+ if (retval != ERROR_OK)
+ return retval;
+ jtag_add_runtest(5, TAP_IDLE);
+ jtag_add_sleep(2000);
+
+ /* after erasing check all bits in user register are cleared */
+ const uint32_t expected_post = REGISTER_ALL_BITS_0;
+ return lattice_verify_usercode(lattice_device, out, expected_post, mask);
+}
+
+static int lattice_ecp2_3_program_config_map(struct lattice_pld_device *lattice_device,
+ struct lattice_bit_file *bit_file)
+{
+ struct jtag_tap *tap = lattice_device->tap;
+ if (!tap)
+ return ERROR_FAIL;
+
+ int retval = lattice_set_instr(tap, LSCC_RESET_ADDRESS, TAP_IDLE);
+ if (retval != ERROR_OK)
+ return retval;
+ jtag_add_runtest(5, TAP_IDLE);
+ jtag_add_sleep(2000);
+
+ struct scan_field field;
+ retval = lattice_set_instr(tap, LSCC_BITSTREAM_BURST, TAP_IDLE);
+ if (retval != ERROR_OK)
+ return retval;
+ field.num_bits = (bit_file->raw_bit.length - bit_file->offset) * 8;
+ field.out_value = bit_file->raw_bit.data + bit_file->offset;
+ field.in_value = NULL;
+ jtag_add_dr_scan(tap, 1, &field, TAP_IDLE);
+ jtag_add_runtest(256, TAP_IDLE);
+ jtag_add_sleep(2000);
+ return jtag_execute_queue();
+}
+
+static int lattice_ecp2_3_exit_programming_mode(struct lattice_pld_device *lattice_device)
+{
+ struct jtag_tap *tap = lattice_device->tap;
+ if (!tap)
+ return ERROR_FAIL;
+
+ int retval = lattice_set_instr(tap, ISC_DISABLE, TAP_IDLE);
+ if (retval != ERROR_OK)
+ return retval;
+ jtag_add_runtest(5, TAP_IDLE);
+ jtag_add_sleep(200000);
+ retval = lattice_set_instr(tap, BYPASS, TAP_IDLE);
+ if (retval != ERROR_OK)
+ return retval;
+ jtag_add_runtest(100, TAP_IDLE);
+ jtag_add_sleep(1000);
+ return jtag_execute_queue();
+}
+
+int lattice_ecp2_load(struct lattice_pld_device *lattice_device, struct lattice_bit_file *bit_file)
+{
+ struct jtag_tap *tap = lattice_device->tap;
+ if (!tap)
+ return ERROR_FAIL;
+
+ int retval = lattice_preload(lattice_device);
+ if (retval != ERROR_OK)
+ return retval;
+
+ /* Enable the programming mode */
+ retval = lattice_set_instr(tap, LSCC_REFRESH, TAP_IDLE);
+ if (retval != ERROR_OK)
+ return retval;
+ retval = lattice_set_instr(tap, ISC_ENABLE, TAP_IDLE);
+ if (retval != ERROR_OK)
+ return retval;
+ jtag_add_runtest(5, TAP_IDLE);
+ jtag_add_sleep(20000);
+
+ /* Erase the device */
+ retval = lattice_ecp2_3_erase_device(lattice_device);
+ if (retval != ERROR_OK)
+ return retval;
+
+ /* Program Fuse Map */
+ retval = lattice_ecp2_3_program_config_map(lattice_device, bit_file);
+ if (retval != ERROR_OK)
+ return retval;
+
+ retval = lattice_ecp2_3_exit_programming_mode(lattice_device);
+ if (retval != ERROR_OK)
+ return retval;
+
+ const uint32_t out = REGISTER_ALL_BITS_1;
+ const uint32_t mask = STATUS_DONE_BIT | STATUS_ERROR_BITS_ECP2;
+ const uint32_t expected = STATUS_DONE_BIT;
+ return lattice_verify_status_register_u32(lattice_device, out, expected, mask, false);
+}
+
+int lattice_ecp3_load(struct lattice_pld_device *lattice_device, struct lattice_bit_file *bit_file)
+{
+ struct jtag_tap *tap = lattice_device->tap;
+ if (!tap)
+ return ERROR_FAIL;
+
+ /* Program Bscan register */
+ int retval = lattice_preload(lattice_device);
+ if (retval != ERROR_OK)
+ return retval;
+
+ /* Enable the programming mode */
+ retval = lattice_set_instr(tap, LSCC_REFRESH, TAP_IDLE);
+ if (retval != ERROR_OK)
+ return retval;
+ jtag_add_runtest(5, TAP_IDLE);
+ jtag_add_sleep(500000);
+ retval = lattice_set_instr(tap, ISC_ENABLE, TAP_IDLE);
+ if (retval != ERROR_OK)
+ return retval;
+ jtag_add_runtest(5, TAP_IDLE);
+ jtag_add_sleep(20000);
+
+ retval = lattice_ecp2_3_erase_device(lattice_device);
+ if (retval != ERROR_OK)
+ return retval;
+
+ /* Program Fuse Map */
+ retval = lattice_ecp2_3_program_config_map(lattice_device, bit_file);
+ if (retval != ERROR_OK)
+ return retval;
+
+ retval = lattice_ecp2_3_exit_programming_mode(lattice_device);
+ if (retval != ERROR_OK)
+ return retval;
+
+ const uint32_t out = REGISTER_ALL_BITS_1;
+ const uint32_t mask = STATUS_DONE_BIT | STATUS_ERROR_BITS_ECP3;
+ const uint32_t expected = STATUS_DONE_BIT;
+ return lattice_verify_status_register_u32(lattice_device, out, expected, mask, false);
+}
diff --git a/src/pld/ecp2_3.h b/src/pld/ecp2_3.h
new file mode 100644
index 0000000..5f3e9e97
--- /dev/null
+++ b/src/pld/ecp2_3.h
@@ -0,0 +1,19 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+/***************************************************************************
+ * Copyright (C) 2022 by Daniel Anselmi *
+ * danselmi@gmx.ch *
+ ***************************************************************************/
+
+#ifndef OPENOCD_PLD_ECP2_3_H
+#define OPENOCD_PLD_ECP2_3_H
+
+#include "lattice.h"
+
+int lattice_ecp2_3_read_status(struct jtag_tap *tap, uint32_t *status, uint32_t out, bool do_idle);
+int lattice_ecp2_3_read_usercode(struct jtag_tap *tap, uint32_t *usercode, uint32_t out);
+int lattice_ecp2_3_write_usercode(struct lattice_pld_device *lattice_device, uint32_t usercode);
+int lattice_ecp2_load(struct lattice_pld_device *lattice_device, struct lattice_bit_file *bit_file);
+int lattice_ecp3_load(struct lattice_pld_device *lattice_device, struct lattice_bit_file *bit_file);
+
+#endif /* OPENOCD_PLD_ECP2_3_H */
diff --git a/src/pld/lattice.c b/src/pld/lattice.c
new file mode 100644
index 0000000..489b189
--- /dev/null
+++ b/src/pld/lattice.c
@@ -0,0 +1,435 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+/***************************************************************************
+ * Copyright (C) 2022 by Daniel Anselmi *
+ * danselmi@gmx.ch *
+ ***************************************************************************/
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "lattice.h"
+#include <jtag/jtag.h>
+#include "pld.h"
+#include "lattice_bit.h"
+#include "ecp2_3.h"
+
+#define PRELOAD 0x1C
+
+struct lattice_devices_elem {
+ uint32_t id;
+ size_t preload_length;
+ enum lattice_family_e family;
+};
+
+static const struct lattice_devices_elem lattice_devices[] = {
+ {0x01270043, 654, LATTICE_ECP2 /* ecp2-6e */},
+ {0x01271043, 643, LATTICE_ECP2 /* ecp2-12e */},
+ {0x01272043, 827, LATTICE_ECP2 /* ecp2-20e */},
+ {0x01274043, 1011, LATTICE_ECP2 /* ecp2-35e */},
+ {0x01273043, 1219, LATTICE_ECP2 /* ecp2-50e */},
+ {0x01275043, 654, LATTICE_ECP2 /* ecp2-70e */},
+ {0x01279043, 680, LATTICE_ECP2 /* ecp2m20e */},
+ {0x0127A043, 936, LATTICE_ECP2 /* ecp2m35e */},
+ {0x0127B043, 1056, LATTICE_ECP2 /* ecp2m50e */},
+ {0x0127C043, 1039, LATTICE_ECP2 /* ecp2m70e */},
+ {0x0127D043, 1311, LATTICE_ECP2 /* ecp2m100e */},
+ {0x01010043, 467, LATTICE_ECP3 /* ecp3 lae3-17ea & lfe3-17ea*/},
+ {0x01012043, 675, LATTICE_ECP3 /* ecp3 lae3-35ea & lfe3-35ea*/},
+ {0x01014043, 1077, LATTICE_ECP3 /* ecp3 lfe3-70ea & lfe3-70e & lfe3-95ea && lfe3-95e*/},
+ {0x01015043, 1326, LATTICE_ECP3 /* ecp3 lfe3-150e*/},
+};
+
+int lattice_set_instr(struct jtag_tap *tap, uint8_t new_instr, tap_state_t endstate)
+{
+ struct scan_field field;
+ field.num_bits = tap->ir_length;
+ void *t = calloc(DIV_ROUND_UP(field.num_bits, 8), 1);
+ if (!t) {
+ LOG_ERROR("Out of memory");
+ return ERROR_FAIL;
+ }
+ field.out_value = t;
+ buf_set_u32(t, 0, field.num_bits, new_instr);
+ field.in_value = NULL;
+ jtag_add_ir_scan(tap, &field, endstate);
+ free(t);
+ return ERROR_OK;
+}
+
+static int lattice_check_device_family(struct lattice_pld_device *lattice_device)
+{
+ if (lattice_device->family != LATTICE_UNKNOWN && lattice_device->preload_length != 0)
+ return ERROR_OK;
+
+ if (!lattice_device->tap || !lattice_device->tap->hasidcode)
+ return ERROR_FAIL;
+
+ for (size_t i = 0; i < ARRAY_SIZE(lattice_devices); ++i) {
+ if (lattice_devices[i].id == lattice_device->tap->idcode) {
+ if (lattice_device->family == LATTICE_UNKNOWN)
+ lattice_device->family = lattice_devices[i].family;
+ if (lattice_device->preload_length == 0)
+ lattice_device->preload_length = lattice_devices[i].preload_length;
+ return ERROR_OK;
+ }
+ }
+ LOG_ERROR("Unknown id! Specify family and preload-length manually.");
+ return ERROR_FAIL;
+}
+
+int lattice_read_u32_register(struct jtag_tap *tap, uint8_t cmd, uint32_t *in_val,
+ uint32_t out_val, bool do_idle)
+{
+ struct scan_field field;
+ uint8_t buffer[4];
+
+ int retval = lattice_set_instr(tap, cmd, TAP_IDLE);
+ if (retval != ERROR_OK)
+ return retval;
+ if (do_idle) {
+ jtag_add_runtest(2, TAP_IDLE);
+ jtag_add_sleep(1000);
+ }
+
+ h_u32_to_le(buffer, out_val);
+ field.num_bits = 32;
+ field.out_value = buffer;
+ field.in_value = buffer;
+ jtag_add_dr_scan(tap, 1, &field, TAP_IDLE);
+ retval = jtag_execute_queue();
+ if (retval == ERROR_OK)
+ *in_val = le_to_h_u32(buffer);
+
+ return retval;
+}
+
+int lattice_preload(struct lattice_pld_device *lattice_device)
+{
+ struct scan_field field;
+ size_t sz_bytes = DIV_ROUND_UP(lattice_device->preload_length, 8);
+
+ int retval = lattice_set_instr(lattice_device->tap, PRELOAD, TAP_IDLE);
+ if (retval != ERROR_OK)
+ return retval;
+ uint8_t *buffer = malloc(sz_bytes);
+ if (!buffer) {
+ LOG_ERROR("Out of memory");
+ return ERROR_FAIL;
+ }
+ memset(buffer, 0xff, sz_bytes);
+
+ field.num_bits = lattice_device->preload_length;
+ field.out_value = buffer;
+ field.in_value = NULL;
+ jtag_add_dr_scan(lattice_device->tap, 1, &field, TAP_IDLE);
+ retval = jtag_execute_queue();
+ free(buffer);
+ return retval;
+}
+
+static int lattice_read_usercode(struct lattice_pld_device *lattice_device, uint32_t *usercode, uint32_t out)
+{
+ struct jtag_tap *tap = lattice_device->tap;
+ if (!tap)
+ return ERROR_FAIL;
+
+ if (lattice_device->family == LATTICE_ECP2 || lattice_device->family == LATTICE_ECP3)
+ return lattice_ecp2_3_read_usercode(tap, usercode, out);
+
+ return ERROR_FAIL;
+}
+
+int lattice_verify_usercode(struct lattice_pld_device *lattice_device, uint32_t out,
+ uint32_t expected, uint32_t mask)
+{
+ uint32_t usercode;
+
+ int retval = lattice_read_usercode(lattice_device, &usercode, out);
+ if (retval != ERROR_OK)
+ return retval;
+
+ if ((usercode & mask) != expected) {
+ LOG_ERROR("verifying user code register failed got: 0x%08" PRIx32 " expected: 0x%08" PRIx32,
+ usercode & mask, expected);
+ return ERROR_FAIL;
+ }
+ return ERROR_OK;
+}
+
+static int lattice_write_usercode(struct lattice_pld_device *lattice_device, uint32_t usercode)
+{
+ if (lattice_device->family == LATTICE_ECP2 || lattice_device->family == LATTICE_ECP3)
+ return lattice_ecp2_3_write_usercode(lattice_device, usercode);
+
+ return ERROR_FAIL;
+}
+
+static int lattice_read_status_u32(struct lattice_pld_device *lattice_device, uint32_t *status,
+ uint32_t out, bool do_idle)
+{
+ if (!lattice_device->tap)
+ return ERROR_FAIL;
+
+ if (lattice_device->family == LATTICE_ECP2 || lattice_device->family == LATTICE_ECP3)
+ return lattice_ecp2_3_read_status(lattice_device->tap, status, out, do_idle);
+
+ return ERROR_FAIL;
+}
+
+int lattice_verify_status_register_u32(struct lattice_pld_device *lattice_device, uint32_t out,
+ uint32_t expected, uint32_t mask, bool do_idle)
+{
+ uint32_t status;
+
+ int retval = lattice_read_status_u32(lattice_device, &status, out, do_idle);
+ if (retval != ERROR_OK)
+ return retval;
+
+ if ((status & mask) != expected) {
+ LOG_ERROR("verifying status register failed got: 0x%08" PRIx32 " expected: 0x%08" PRIx32,
+ status & mask, expected);
+ return ERROR_FAIL;
+ }
+ return ERROR_OK;
+}
+
+static int lattice_load_command(struct pld_device *pld_device, const char *filename)
+{
+ if (!pld_device)
+ return ERROR_FAIL;
+
+ struct lattice_pld_device *lattice_device = pld_device->driver_priv;
+
+ if (!lattice_device || !lattice_device->tap)
+ return ERROR_FAIL;
+ struct jtag_tap *tap = lattice_device->tap;
+
+ if (!tap || !tap->hasidcode)
+ return ERROR_FAIL;
+
+ int retval = lattice_check_device_family(lattice_device);
+ if (retval != ERROR_OK)
+ return retval;
+
+ struct lattice_bit_file bit_file;
+ retval = lattice_read_file(&bit_file, filename, lattice_device->family);
+ if (retval != ERROR_OK)
+ return retval;
+
+ retval = ERROR_FAIL;
+ switch (lattice_device->family) {
+ case LATTICE_ECP2:
+ retval = lattice_ecp2_load(lattice_device, &bit_file);
+ break;
+ case LATTICE_ECP3:
+ retval = lattice_ecp3_load(lattice_device, &bit_file);
+ break;
+ default:
+ LOG_ERROR("loading unknown device family");
+ break;
+ }
+ free(bit_file.raw_bit.data);
+ return retval;
+}
+
+PLD_DEVICE_COMMAND_HANDLER(lattice_pld_device_command)
+{
+ if (CMD_ARGC < 2 || CMD_ARGC > 3)
+ return ERROR_COMMAND_SYNTAX_ERROR;
+
+ struct jtag_tap *tap = jtag_tap_by_string(CMD_ARGV[1]);
+ if (!tap) {
+ command_print(CMD, "Tap: %s does not exist", CMD_ARGV[1]);
+ return ERROR_FAIL;
+ }
+
+ struct lattice_pld_device *lattice_device = malloc(sizeof(struct lattice_pld_device));
+ if (!lattice_device) {
+ LOG_ERROR("Out of memory");
+ return ERROR_FAIL;
+ }
+ /* id is not known yet -> postpone lattice_check_device_family() */
+ enum lattice_family_e family = LATTICE_UNKNOWN;
+ if (CMD_ARGC == 3) {
+ if (strcasecmp(CMD_ARGV[2], "ecp2") == 0) {
+ family = LATTICE_ECP2;
+ } else if (strcasecmp(CMD_ARGV[2], "ecp3") == 0) {
+ family = LATTICE_ECP3;
+ } else {
+ command_print(CMD, "unknown family");
+ free(lattice_device);
+ return ERROR_FAIL;
+ }
+ }
+ lattice_device->tap = tap;
+ lattice_device->family = family;
+ lattice_device->preload_length = 0;
+
+ pld->driver_priv = lattice_device;
+
+ return ERROR_OK;
+}
+
+COMMAND_HANDLER(lattice_read_usercode_register_command_handler)
+{
+ int dev_id;
+ uint32_t usercode;
+
+ if (CMD_ARGC != 1)
+ return ERROR_COMMAND_SYNTAX_ERROR;
+
+ COMMAND_PARSE_NUMBER(int, CMD_ARGV[0], dev_id);
+ struct pld_device *device = get_pld_device_by_num(dev_id);
+ if (!device) {
+ command_print(CMD, "pld device '#%s' is out of bounds", CMD_ARGV[0]);
+ return ERROR_FAIL;
+ }
+
+ struct lattice_pld_device *lattice_device = device->driver_priv;
+ if (!lattice_device)
+ return ERROR_FAIL;
+
+ int retval = lattice_check_device_family(lattice_device);
+ if (retval != ERROR_OK)
+ return retval;
+
+ retval = lattice_read_usercode(lattice_device, &usercode, 0x0);
+ if (retval == ERROR_OK)
+ command_print(CMD, "0x%8.8" PRIx32, usercode);
+
+ return retval;
+}
+
+COMMAND_HANDLER(lattice_set_preload_command_handler)
+{
+ int dev_id;
+ unsigned int preload_length;
+
+ if (CMD_ARGC != 2)
+ return ERROR_COMMAND_SYNTAX_ERROR;
+
+ COMMAND_PARSE_NUMBER(int, CMD_ARGV[0], dev_id);
+ struct pld_device *device = get_pld_device_by_num(dev_id);
+ if (!device) {
+ command_print(CMD, "pld device '#%s' is out of bounds", CMD_ARGV[0]);
+ return ERROR_FAIL;
+ }
+
+ COMMAND_PARSE_NUMBER(uint, CMD_ARGV[1], preload_length);
+
+ struct lattice_pld_device *lattice_device = device->driver_priv;
+
+ if (!lattice_device)
+ return ERROR_FAIL;
+
+ lattice_device->preload_length = preload_length;
+
+ return ERROR_OK;
+}
+
+COMMAND_HANDLER(lattice_write_usercode_register_command_handler)
+{
+ int dev_id;
+ uint32_t usercode;
+
+ if (CMD_ARGC != 2)
+ return ERROR_COMMAND_SYNTAX_ERROR;
+
+ COMMAND_PARSE_NUMBER(int, CMD_ARGV[0], dev_id);
+ struct pld_device *device = get_pld_device_by_num(dev_id);
+ if (!device) {
+ command_print(CMD, "pld device '#%s' is out of bounds", CMD_ARGV[0]);
+ return ERROR_FAIL;
+ }
+
+ COMMAND_PARSE_NUMBER(u32, CMD_ARGV[1], usercode);
+
+ struct lattice_pld_device *lattice_device = device->driver_priv;
+ if (!lattice_device)
+ return ERROR_FAIL;
+
+ int retval = lattice_check_device_family(lattice_device);
+ if (retval != ERROR_OK)
+ return retval;
+
+ return lattice_write_usercode(lattice_device, usercode);
+}
+
+COMMAND_HANDLER(lattice_read_status_command_handler)
+{
+ int dev_id;
+
+ if (CMD_ARGC != 1)
+ return ERROR_COMMAND_SYNTAX_ERROR;
+
+ COMMAND_PARSE_NUMBER(int, CMD_ARGV[0], dev_id);
+ struct pld_device *device = get_pld_device_by_num(dev_id);
+ if (!device) {
+ command_print(CMD, "pld device '#%s' is out of bounds", CMD_ARGV[0]);
+ return ERROR_FAIL;
+ }
+
+ struct lattice_pld_device *lattice_device = device->driver_priv;
+ if (!lattice_device)
+ return ERROR_FAIL;
+
+ int retval = lattice_check_device_family(lattice_device);
+ if (retval != ERROR_OK)
+ return retval;
+
+ uint32_t status;
+ retval = lattice_read_status_u32(lattice_device, &status, 0x0, false);
+ if (retval == ERROR_OK)
+ command_print(CMD, "0x%8.8" PRIx32, status);
+
+ return retval;
+}
+
+static const struct command_registration lattice_exec_command_handlers[] = {
+ {
+ .name = "read_status",
+ .mode = COMMAND_EXEC,
+ .handler = lattice_read_status_command_handler,
+ .help = "reading status register from FPGA",
+ .usage = "num_pld",
+ }, {
+ .name = "read_user",
+ .mode = COMMAND_EXEC,
+ .handler = lattice_read_usercode_register_command_handler,
+ .help = "reading usercode register from FPGA",
+ .usage = "num_pld",
+ }, {
+ .name = "write_user",
+ .mode = COMMAND_EXEC,
+ .handler = lattice_write_usercode_register_command_handler,
+ .help = "writing usercode register to FPGA",
+ .usage = "num_pld value",
+ }, {
+ .name = "set_preload",
+ .mode = COMMAND_EXEC,
+ .handler = lattice_set_preload_command_handler,
+ .help = "set length for preload (device specific)",
+ .usage = "num_pld value",
+ },
+ COMMAND_REGISTRATION_DONE
+};
+
+static const struct command_registration lattice_command_handler[] = {
+ {
+ .name = "lattice",
+ .mode = COMMAND_ANY,
+ .help = "lattice specific commands",
+ .usage = "",
+ .chain = lattice_exec_command_handlers,
+ },
+ COMMAND_REGISTRATION_DONE
+};
+
+struct pld_driver lattice_pld = {
+ .name = "lattice",
+ .commands = lattice_command_handler,
+ .pld_device_command = &lattice_pld_device_command,
+ .load = &lattice_load_command,
+};
diff --git a/src/pld/lattice.h b/src/pld/lattice.h
new file mode 100644
index 0000000..6d692cb
--- /dev/null
+++ b/src/pld/lattice.h
@@ -0,0 +1,32 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+/***************************************************************************
+ * Copyright (C) 2022 by Daniel Anselmi *
+ * danselmi@gmx.ch *
+ ***************************************************************************/
+
+#ifndef OPENOCD_PLD_LATTICE_H
+#define OPENOCD_PLD_LATTICE_H
+
+#include <jtag/jtag.h>
+#include "pld.h"
+#include "lattice_bit.h"
+
+#define BYPASS 0xFF
+
+struct lattice_pld_device {
+ struct jtag_tap *tap;
+ size_t preload_length;
+ enum lattice_family_e family;
+};
+
+int lattice_set_instr(struct jtag_tap *tap, uint8_t new_instr, tap_state_t endstate);
+int lattice_read_u32_register(struct jtag_tap *tap, uint8_t cmd, uint32_t *in_val,
+ uint32_t out_val, bool do_idle);
+int lattice_verify_usercode(struct lattice_pld_device *lattice_device, uint32_t out,
+ uint32_t expected, uint32_t mask);
+int lattice_verify_status_register_u32(struct lattice_pld_device *lattice_device, uint32_t out,
+ uint32_t expected, uint32_t mask, bool do_idle);
+int lattice_preload(struct lattice_pld_device *lattice_device);
+
+#endif /* OPENOCD_PLD_LATTICE_H */
diff --git a/src/pld/lattice_bit.c b/src/pld/lattice_bit.c
new file mode 100644
index 0000000..562b17d
--- /dev/null
+++ b/src/pld/lattice_bit.c
@@ -0,0 +1,105 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+/***************************************************************************
+ * Copyright (C) 2022 by Daniel Anselmi *
+ * danselmi@gmx.ch *
+ ***************************************************************************/
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+#include "lattice_bit.h"
+#include "raw_bit.h"
+#include "pld.h"
+#include <helper/system.h>
+#include <helper/log.h>
+#include <helper/binarybuffer.h>
+
+enum read_bit_state {
+ SEEK_HEADER_START,
+ SEEK_HEADER_END,
+ SEEK_PREAMBLE,
+ SEEK_ID,
+ DONE,
+};
+
+static int lattice_read_bit_file(struct lattice_bit_file *bit_file, const char *filename, enum lattice_family_e family)
+{
+ int retval = cpld_read_raw_bit_file(&bit_file->raw_bit, filename);
+ if (retval != ERROR_OK)
+ return retval;
+
+ bit_file->part = 0;
+ bit_file->has_id = false;
+ enum read_bit_state state = SEEK_HEADER_START;
+ for (size_t pos = 1; pos < bit_file->raw_bit.length && state != DONE; ++pos) {
+ switch (state) {
+ case SEEK_HEADER_START:
+ if (bit_file->raw_bit.data[pos] == 0 && bit_file->raw_bit.data[pos - 1] == 0xff)
+ state = SEEK_HEADER_END;
+ break;
+ case SEEK_HEADER_END:
+ if (pos + 6 < bit_file->raw_bit.length &&
+ strncmp((const char *)(bit_file->raw_bit.data + pos), "Part: ", 6) == 0) {
+ bit_file->part = (const char *)bit_file->raw_bit.data + pos + 6;
+ LOG_INFO("part found: %s\n", bit_file->part);
+ } else if (bit_file->raw_bit.data[pos] == 0xff && bit_file->raw_bit.data[pos - 1] == 0) {
+ bit_file->offset = pos;
+ state = (family != LATTICE_ECP2 && family != LATTICE_ECP3) ? SEEK_PREAMBLE : DONE;
+ }
+ break;
+ case SEEK_PREAMBLE:
+ if (pos >= 4) {
+ uint32_t preamble = be_to_h_u32(bit_file->raw_bit.data + pos - 3);
+ switch (preamble) {
+ case 0xffffbdb3:
+ state = SEEK_ID;
+ break;
+ case 0xffffbfb3:
+ case 0xffffbeb3:
+ state = DONE;
+ break;
+ }
+ }
+ break;
+ case SEEK_ID:
+ if (pos + 7 < bit_file->raw_bit.length && bit_file->raw_bit.data[pos] == 0xe2) {
+ bit_file->idcode = be_to_h_u32(&bit_file->raw_bit.data[pos + 4]);
+ bit_file->has_id = true;
+ state = DONE;
+ }
+ break;
+ default:
+ break;
+ }
+ }
+
+ if (state != DONE) {
+ LOG_ERROR("parsing bitstream failed");
+ return ERROR_PLD_FILE_LOAD_FAILED;
+ }
+
+ for (size_t i = bit_file->offset; i < bit_file->raw_bit.length; i++)
+ bit_file->raw_bit.data[i] = flip_u32(bit_file->raw_bit.data[i], 8);
+
+ return ERROR_OK;
+}
+
+int lattice_read_file(struct lattice_bit_file *bit_file, const char *filename, enum lattice_family_e family)
+{
+ if (!filename || !bit_file)
+ return ERROR_COMMAND_SYNTAX_ERROR;
+
+ /* check if binary .bin or ascii .bit/.hex */
+ const char *file_suffix_pos = strrchr(filename, '.');
+ if (!file_suffix_pos) {
+ LOG_ERROR("Unable to detect filename suffix");
+ return ERROR_PLD_FILE_LOAD_FAILED;
+ }
+
+ if (strcasecmp(file_suffix_pos, ".bit") == 0)
+ return lattice_read_bit_file(bit_file, filename, family);
+
+ LOG_ERROR("Filetype not supported");
+ return ERROR_PLD_FILE_LOAD_FAILED;
+}
diff --git a/src/pld/lattice_bit.h b/src/pld/lattice_bit.h
new file mode 100644
index 0000000..33f1b34
--- /dev/null
+++ b/src/pld/lattice_bit.h
@@ -0,0 +1,33 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+/***************************************************************************
+ * Copyright (C) 2022 by Daniel Anselmi *
+ * danselmi@gmx.ch *
+ ***************************************************************************/
+
+#ifndef OPENOCD_PLD_LATTICE_BIT_H
+#define OPENOCD_PLD_LATTICE_BIT_H
+
+#include "helper/types.h"
+#include "raw_bit.h"
+
+
+struct lattice_bit_file {
+ struct raw_bit_file raw_bit;
+ size_t offset;
+ uint32_t idcode;
+ const char *part; /* reuses memory in raw_bit_file */
+ bool has_id;
+};
+
+enum lattice_family_e {
+ LATTICE_ECP2,
+ LATTICE_ECP3,
+ LATTICE_ECP5,
+ LATTICE_CERTUS,
+ LATTICE_UNKNOWN,
+};
+
+int lattice_read_file(struct lattice_bit_file *bit_file, const char *filename, enum lattice_family_e family);
+
+#endif /* OPENOCD_PLD_LATTICE_BIT_H */
diff --git a/src/pld/pld.c b/src/pld/pld.c
index af18369..e838888 100644
--- a/src/pld/pld.c
+++ b/src/pld/pld.c
@@ -18,9 +18,11 @@
/* pld drivers
*/
+extern struct pld_driver lattice_pld;
extern struct pld_driver virtex2_pld;
static struct pld_driver *pld_drivers[] = {
+ &lattice_pld,
&virtex2_pld,
NULL,
};
diff --git a/src/pld/raw_bit.c b/src/pld/raw_bit.c
new file mode 100644
index 0000000..0c3b92e
--- /dev/null
+++ b/src/pld/raw_bit.c
@@ -0,0 +1,55 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+/***************************************************************************
+ * Copyright (C) 2022 by Daniel Anselmi *
+ * danselmi@gmx.ch *
+ ***************************************************************************/
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "raw_bit.h"
+#include "pld.h"
+
+#include <helper/system.h>
+#include <helper/log.h>
+
+
+int cpld_read_raw_bit_file(struct raw_bit_file *bit_file, const char *filename)
+{
+ FILE *input_file = fopen(filename, "rb");
+
+ if (!input_file) {
+ LOG_ERROR("Couldn't open %s: %s", filename, strerror(errno));
+ return ERROR_PLD_FILE_LOAD_FAILED;
+ }
+
+ fseek(input_file, 0, SEEK_END);
+ long length = ftell(input_file);
+ fseek(input_file, 0, SEEK_SET);
+
+ if (length < 0) {
+ fclose(input_file);
+ LOG_ERROR("Failed to get length of file %s: %s", filename, strerror(errno));
+ return ERROR_PLD_FILE_LOAD_FAILED;
+ }
+ bit_file->length = (size_t)length;
+
+ bit_file->data = malloc(bit_file->length);
+ if (!bit_file->data) {
+ fclose(input_file);
+ LOG_ERROR("Out of memory");
+ return ERROR_PLD_FILE_LOAD_FAILED;
+ }
+
+ size_t read_count = fread(bit_file->data, sizeof(char), bit_file->length, input_file);
+ fclose(input_file);
+ if (read_count != bit_file->length) {
+ free(bit_file->data);
+ bit_file->data = NULL;
+ return ERROR_PLD_FILE_LOAD_FAILED;
+ }
+
+ return ERROR_OK;
+}
diff --git a/src/pld/raw_bit.h b/src/pld/raw_bit.h
new file mode 100644
index 0000000..583ff76
--- /dev/null
+++ b/src/pld/raw_bit.h
@@ -0,0 +1,21 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+/***************************************************************************
+ * Copyright (C) 2022 by Daniel Anselmi *
+ * danselmi@gmx.ch *
+ ***************************************************************************/
+
+#ifndef OPENOCD_PLD_RAW_BIN_H
+#define OPENOCD_PLD_RAW_BIN_H
+
+#include <stddef.h>
+#include <stdint.h>
+
+struct raw_bit_file {
+ size_t length;
+ uint8_t *data;
+};
+
+int cpld_read_raw_bit_file(struct raw_bit_file *bit_file, const char *filename);
+
+#endif /* OPENOCD_PLD_RAW_BIN_H */
diff --git a/tcl/fpga/lattice_ecp2.cfg b/tcl/fpga/lattice_ecp2.cfg
new file mode 100644
index 0000000..a1aa2ef
--- /dev/null
+++ b/tcl/fpga/lattice_ecp2.cfg
@@ -0,0 +1,31 @@
+# SPDX-License-Identifier: GPL-2.0-or-later
+
+if { [info exists CHIPNAME] } {
+ set _CHIPNAME $_CHIPNAME
+} else {
+ set _CHIPNAME ecp2
+}
+
+# Lattice ECP2 family
+# TAP IDs are extracted from BSDL files found on this page:
+# https://www.latticesemi.com/Products/FPGAandCPLD/LatticeECP2M
+#
+# LFE2M20E: 0x01279043
+# LFE2M35E: 0x0127A043
+# LFE2M50E: 0x0127B043
+# LFE2M70E: 0x0127C043
+# LFE2M100E: 0x0127D043
+# LFEC2_6E: 0x01270043
+# LFEC2_12E: 0x01271043
+# LFEC2_20E: 0x01272043
+# LFEC2_35E: 0x01274043
+# LFEC2_50E: 0x01273043
+# LFEC2_70E: 0x01275043
+
+jtag newtap $_CHIPNAME tap -irlen 8 \
+ -expected-id 0x01279043 -expected-id 0x0127A043 -expected-id 0x0127B043 \
+ -expected-id 0x0127C043 -expected-id 0x0127D043 -expected-id 0x01270043 \
+ -expected-id 0x01271043 -expected-id 0x01272043 -expected-id 0x01274043 \
+ -expected-id 0x01273043 -expected-id 0x01275043
+
+pld device lattice $_CHIPNAME.tap
diff --git a/tcl/fpga/lattice_ecp3.cfg b/tcl/fpga/lattice_ecp3.cfg
new file mode 100644
index 0000000..7cd5706
--- /dev/null
+++ b/tcl/fpga/lattice_ecp3.cfg
@@ -0,0 +1,22 @@
+# SPDX-License-Identifier: GPL-2.0-or-later
+
+if { [info exists CHIPNAME] } {
+ set _CHIPNAME $_CHIPNAME
+} else {
+ set _CHIPNAME ecp3
+}
+
+# Lattice ECP3 family
+# TAP IDs are extracted from BSDL files found on this page:
+# https://www.latticesemi.com/Products/FPGAandCPLD/LatticeECP3
+#
+# LFE3_17: 0x01010043
+# LFE3_35: 0x01012043
+# LFE3_95: 0x01014043 and LFE3_70
+# LFE3_150: 0x01015043
+
+jtag newtap $_CHIPNAME tap -irlen 8 \
+ -expected-id 0x01010043 -expected-id 0x01012043 \
+ -expected-id 0x01014043 -expected-id 0x01015043
+
+pld device lattice $_CHIPNAME.tap