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:54:59 +0000
commit4b56c73ef3781c3232056b57be6d0d2ae3486a4a (patch)
tree49209a4a88dd4b529fafabee46a1caecce0fc13d
parentdb0609aeb4d5c8dec607afb1e07f5083d3b242c3 (diff)
downloadriscv-openocd-4b56c73ef3781c3232056b57be6d0d2ae3486a4a.zip
riscv-openocd-4b56c73ef3781c3232056b57be6d0d2ae3486a4a.tar.gz
riscv-openocd-4b56c73ef3781c3232056b57be6d0d2ae3486a4a.tar.bz2
pld: add support for gowin devices
Change-Id: Idd1a09514bbbbe0a7b54d69010f6c2f91215fd1d Signed-off-by: Daniel Anselmi <danselmi@gmx.ch> Reviewed-on: https://review.openocd.org/c/openocd/+/7368 Tested-by: jenkins Reviewed-by: Antonio Borneo <borneo.antonio@gmail.com>
-rw-r--r--doc/openocd.texi7
-rw-r--r--src/pld/Makefile.am1
-rw-r--r--src/pld/gowin.c581
-rw-r--r--src/pld/pld.c2
-rw-r--r--tcl/board/gowin_runber.cfg19
-rw-r--r--tcl/fpga/gowin_gw1n.cfg29
6 files changed, 639 insertions, 0 deletions
diff --git a/doc/openocd.texi b/doc/openocd.texi
index 8099455..cea02bd 100644
--- a/doc/openocd.texi
+++ b/doc/openocd.texi
@@ -8559,6 +8559,13 @@ With a value of -1 for @var{pos} the check will be omitted.
@end deffn
+@deffn {FPGA Driver} {gowin}
+This driver can be used to load the bitstream into FPGAs from Gowin.
+It is possible to program the SRAM. Programming the flash is not supported.
+The files @verb{|.fs|} and @verb{|.bin|} generated by Gowin FPGA Designer are supported.
+@end deffn
+
+
@node General Commands
@chapter General Commands
@cindex commands
diff --git a/src/pld/Makefile.am b/src/pld/Makefile.am
index a13f738..22ae9fd 100644
--- a/src/pld/Makefile.am
+++ b/src/pld/Makefile.am
@@ -6,6 +6,7 @@ noinst_LTLIBRARIES += %D%/libpld.la
%D%/ecp2_3.c \
%D%/ecp5.c \
%D%/efinix.c \
+ %D%/gowin.c \
%D%/intel.c \
%D%/lattice.c \
%D%/lattice_bit.c \
diff --git a/src/pld/gowin.c b/src/pld/gowin.c
new file mode 100644
index 0000000..467b799
--- /dev/null
+++ b/src/pld/gowin.c
@@ -0,0 +1,581 @@
+// 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 <jtag/jtag.h>
+#include <jtag/adapter.h>
+#include <helper/bits.h>
+#include "pld.h"
+#include "raw_bit.h"
+
+#define NO_OP 0x02
+#define ERASE_SRAM 0x05
+#define SRAM_ERASE_DONE 0x09
+#define IDCODE 0x11
+#define ADDRESS_INITIALIZATION 0x12
+#define READ_USERCODE 0x13
+#define CONFIG_ENABLE 0x15
+#define TRANSFER_CONFIGURATION_DATA 0x17
+#define CONFIG_DISABLE 0x3A
+#define RELOAD 0x3C
+#define STATUS_REGISTER 0x41
+#define ERASE_FLASH 0x75
+#define ENABLE_2ND_FLASH 0x78
+
+#define STAUS_MASK_MEMORY_ERASE BIT(5)
+#define STAUS_MASK_SYSTEM_EDIT_MODE BIT(7)
+
+struct gowin_pld_device {
+ struct jtag_tap *tap;
+};
+
+struct gowin_bit_file {
+ struct raw_bit_file raw_file;
+ size_t capacity;
+ uint32_t id;
+ uint16_t stored_checksum;
+ int compressed;
+ int crc_en;
+ uint16_t checksum;
+ uint8_t replace8x;
+ uint8_t replace4x;
+ uint8_t replace2x;
+};
+
+static uint64_t gowin_read_fs_file_bitsequence(const char *bits, int length)
+{
+ uint64_t res = 0;
+ for (int i = 0; i < length; i++)
+ res = (res << 1) | (*bits++ == '1' ? 1 : 0);
+ return res;
+}
+
+static int gowin_add_byte_to_bit_file(struct gowin_bit_file *bit_file, uint8_t byte)
+{
+ if (bit_file->raw_file.length + 1 > bit_file->capacity) {
+ uint8_t *buffer;
+ if (bit_file->raw_file.data)
+ buffer = realloc(bit_file->raw_file.data, bit_file->capacity + 8192);
+ else
+ buffer = malloc(8192);
+ if (!buffer) {
+ LOG_ERROR("Out of memory");
+ return ERROR_FAIL;
+ }
+ bit_file->raw_file.data = buffer;
+ bit_file->capacity += 8192;
+ }
+
+ bit_file->raw_file.data[bit_file->raw_file.length++] = byte;
+
+ return ERROR_OK;
+}
+
+static int gowin_read_fs_file_header(struct gowin_bit_file *bit_file, FILE *stream)
+{
+ if (!bit_file)
+ return ERROR_FAIL;
+
+ int end_of_header = 0;
+ while (!end_of_header) {
+ char buffer[256];
+ char *line = fgets(buffer, 256, stream);
+ if (!line || feof(stream) || ferror(stream))
+ return ERROR_FAIL;
+
+ if (line[0] == '/')
+ continue;
+
+ size_t line_length = strlen(line);
+ if (line[line_length - 1] != '\n')
+ return ERROR_FAIL;
+ line_length--;
+
+ for (unsigned int i = 0; i < line_length; i += 8) {
+ uint8_t byte = gowin_read_fs_file_bitsequence(line + i, 8);
+ int retval = gowin_add_byte_to_bit_file(bit_file, byte);
+ if (retval != ERROR_OK)
+ return retval;
+ }
+
+ uint8_t key = gowin_read_fs_file_bitsequence(line, 8);
+ line += 8;
+ uint64_t value = gowin_read_fs_file_bitsequence(line, line_length - 8);
+
+ if (key == 0x06) {
+ bit_file->id = value & 0xffffffff;
+ } else if (key == 0x3B) {
+ end_of_header = 1;
+ bit_file->crc_en = (value & BIT(23)) ? 1 : 0;
+ }
+ }
+
+ return ERROR_OK;
+}
+
+static int gowin_read_fs_file(struct gowin_bit_file *bit_file, const char *filename)
+{
+ FILE *input_file = fopen(filename, "r");
+
+ if (!input_file) {
+ LOG_ERROR("Couldn't open %s: %s", filename, strerror(errno));
+ return ERROR_PLD_FILE_LOAD_FAILED;
+ }
+
+ int retval = gowin_read_fs_file_header(bit_file, input_file);
+ if (retval != ERROR_OK) {
+ free(bit_file->raw_file.data);
+ fclose(input_file);
+ return retval;
+ }
+
+ char digits_buffer[9]; /* 8 + 1 trailing zero */
+ do {
+ char *digits = fgets(digits_buffer, 9, input_file);
+ if (feof(input_file))
+ break;
+ if (!digits || ferror(input_file)) {
+ free(bit_file->raw_file.data);
+ fclose(input_file);
+ return ERROR_FAIL;
+ }
+ if (digits[0] == '\n')
+ continue;
+
+ if (strlen(digits) != 8) {
+ free(bit_file->raw_file.data);
+ fclose(input_file);
+ return ERROR_FAIL;
+ }
+ uint8_t byte = gowin_read_fs_file_bitsequence(digits, 8);
+ retval = gowin_add_byte_to_bit_file(bit_file, byte);
+ if (retval != ERROR_OK) {
+ free(bit_file->raw_file.data);
+ fclose(input_file);
+ return ERROR_FAIL;
+ }
+ } while (1);
+
+ fclose(input_file);
+ return ERROR_OK;
+}
+
+static int gowin_read_file(struct gowin_bit_file *bit_file, const char *filename, bool *is_fs)
+{
+ memset(bit_file, 0, sizeof(struct gowin_bit_file));
+
+ if (!filename || !bit_file)
+ return ERROR_COMMAND_SYNTAX_ERROR;
+
+ const char *file_suffix_pos = strrchr(filename, '.');
+ if (!file_suffix_pos) {
+ LOG_ERROR("Unable to detect filename suffix");
+ return ERROR_PLD_FILE_LOAD_FAILED;
+ }
+
+ /* check if binary .bin or ascii .fs */
+ if (strcasecmp(file_suffix_pos, ".bin") == 0) {
+ *is_fs = false;
+ return cpld_read_raw_bit_file(&bit_file->raw_file, filename);
+ } else if (strcasecmp(file_suffix_pos, ".fs") == 0) {
+ *is_fs = true;
+ return gowin_read_fs_file(bit_file, filename);
+ }
+
+ LOG_ERROR("Filetype not supported, expecting .fs or .bin file");
+ return ERROR_PLD_FILE_LOAD_FAILED;
+}
+
+static int gowin_set_instr(struct jtag_tap *tap, uint8_t new_instr)
+{
+ 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, TAP_IDLE);
+ jtag_add_runtest(3, TAP_IDLE);
+ free(t);
+ return ERROR_OK;
+}
+
+static int gowin_read_register(struct jtag_tap *tap, uint32_t reg, uint32_t *result)
+{
+ struct scan_field field;
+
+ int retval = gowin_set_instr(tap, reg);
+ if (retval != ERROR_OK)
+ return retval;
+ retval = jtag_execute_queue();
+ if (retval != ERROR_OK)
+ return retval;
+
+ uint8_t buf[4] = {0};
+ field.check_mask = NULL;
+ field.check_value = NULL;
+ field.num_bits = 32;
+ field.out_value = buf;
+ field.in_value = buf;
+
+ jtag_add_dr_scan(tap, 1, &field, TAP_IDLE);
+ retval = jtag_execute_queue();
+ *result = le_to_h_u32(buf);
+ return retval;
+}
+
+static int gowin_check_status_flag(struct jtag_tap *tap, uint32_t mask, uint32_t flag)
+{
+ uint32_t status = 0;
+
+ int retries = 0;
+ do {
+ int retval = gowin_read_register(tap, STATUS_REGISTER, &status);
+ if (retval != ERROR_OK)
+ return retval;
+ if (retries++ == 100000)
+ return ERROR_FAIL;
+ } while ((status & mask) != flag);
+
+ return ERROR_OK;
+}
+
+static int gowin_enable_config(struct jtag_tap *tap)
+{
+ int retval = gowin_set_instr(tap, CONFIG_ENABLE);
+ if (retval != ERROR_OK)
+ return retval;
+ retval = jtag_execute_queue();
+ if (retval != ERROR_OK)
+ return retval;
+
+ return gowin_check_status_flag(tap, STAUS_MASK_SYSTEM_EDIT_MODE, STAUS_MASK_SYSTEM_EDIT_MODE);
+}
+
+static int gowin_disable_config(struct jtag_tap *tap)
+{
+ int retval = gowin_set_instr(tap, CONFIG_DISABLE);
+ if (retval != ERROR_OK)
+ return retval;
+ retval = jtag_execute_queue();
+ if (retval != ERROR_OK)
+ return retval;
+
+ return gowin_check_status_flag(tap, STAUS_MASK_SYSTEM_EDIT_MODE, 0);
+}
+
+static int gowin_reload(struct jtag_tap *tap)
+{
+ int retval = gowin_set_instr(tap, RELOAD);
+ if (retval != ERROR_OK)
+ return retval;
+ retval = gowin_set_instr(tap, NO_OP);
+ if (retval != ERROR_OK)
+ return retval;
+ return jtag_execute_queue();
+}
+
+static int gowin_runtest_idle(struct jtag_tap *tap, unsigned int frac_sec)
+{
+ int speed = adapter_get_speed_khz() * 1000;
+ int cycles = DIV_ROUND_UP(speed, frac_sec);
+ jtag_add_runtest(cycles, TAP_IDLE);
+ return jtag_execute_queue();
+}
+
+static int gowin_erase_sram(struct jtag_tap *tap, bool tx_erase_done)
+{
+ /* config is already enabled */
+ int retval = gowin_set_instr(tap, ERASE_SRAM);
+ if (retval != ERROR_OK)
+ return retval;
+ retval = gowin_set_instr(tap, NO_OP);
+ if (retval != ERROR_OK)
+ return retval;
+
+ /* Delay or Run Test 2~10ms */
+ /* 10 ms is worst case for GW2A-55 */
+ jtag_add_sleep(10000);
+ retval = jtag_execute_queue();
+ if (retval != ERROR_OK)
+ return retval;
+
+ retval = gowin_check_status_flag(tap, STAUS_MASK_MEMORY_ERASE,
+ STAUS_MASK_MEMORY_ERASE);
+ if (retval != ERROR_OK)
+ return retval;
+
+ if (tx_erase_done) {
+ retval = gowin_set_instr(tap, SRAM_ERASE_DONE);
+ if (retval != ERROR_OK)
+ return retval;
+ retval = gowin_set_instr(tap, NO_OP);
+ if (retval != ERROR_OK)
+ return retval;
+ retval = jtag_execute_queue();
+ if (retval != ERROR_OK)
+ return retval;
+ /* gen clock cycles in RUN/IDLE for 500us -> 1/500us = 2000/s */
+ retval = gowin_runtest_idle(tap, 2000);
+ if (retval != ERROR_OK)
+ return retval;
+ }
+
+ retval = gowin_set_instr(tap, NO_OP);
+ if (retval != ERROR_OK)
+ return retval;
+ return jtag_execute_queue();
+}
+
+static int gowin_load_to_sram(struct pld_device *pld_device, const char *filename)
+{
+ if (!pld_device)
+ return ERROR_FAIL;
+
+ struct gowin_pld_device *gowin_info = pld_device->driver_priv;
+
+ if (!gowin_info || !gowin_info->tap)
+ return ERROR_FAIL;
+ struct jtag_tap *tap = gowin_info->tap;
+
+ bool is_fs = false;
+ struct gowin_bit_file bit_file;
+ int retval = gowin_read_file(&bit_file, filename, &is_fs);
+ if (retval != ERROR_OK)
+ return retval;
+
+ for (unsigned int i = 0; i < bit_file.raw_file.length; i++)
+ bit_file.raw_file.data[i] = flip_u32(bit_file.raw_file.data[i], 8);
+
+ uint32_t id;
+ retval = gowin_read_register(tap, IDCODE, &id);
+ if (retval != ERROR_OK) {
+ free(bit_file.raw_file.data);
+ return retval;
+ }
+
+ if (is_fs && id != bit_file.id) {
+ free(bit_file.raw_file.data);
+ LOG_ERROR("Id on device (0x%8.8" PRIx32 ") and id in bit-stream (0x%8.8" PRIx32 ") don't match.",
+ id, bit_file.id);
+ return ERROR_FAIL;
+ }
+
+ retval = gowin_enable_config(tap);
+ if (retval != ERROR_OK) {
+ free(bit_file.raw_file.data);
+ return retval;
+ }
+
+ retval = gowin_erase_sram(tap, false);
+ if (retval != ERROR_OK) {
+ free(bit_file.raw_file.data);
+ return retval;
+ }
+
+ retval = gowin_set_instr(tap, ADDRESS_INITIALIZATION);
+ if (retval != ERROR_OK) {
+ free(bit_file.raw_file.data);
+ return retval;
+ }
+ retval = gowin_set_instr(tap, TRANSFER_CONFIGURATION_DATA);
+ if (retval != ERROR_OK) {
+ free(bit_file.raw_file.data);
+ return retval;
+ }
+
+ /* scan out the bitstream */
+ struct scan_field field;
+ field.num_bits = bit_file.raw_file.length * 8;
+ field.out_value = bit_file.raw_file.data;
+ field.in_value = bit_file.raw_file.data;
+ jtag_add_dr_scan(gowin_info->tap, 1, &field, TAP_IDLE);
+ jtag_add_runtest(3, TAP_IDLE);
+
+ retval = jtag_execute_queue();
+ if (retval != ERROR_OK) {
+ free(bit_file.raw_file.data);
+ return retval;
+ }
+
+ retval = gowin_disable_config(tap);
+ free(bit_file.raw_file.data);
+ if (retval != ERROR_OK)
+ return retval;
+
+ retval = gowin_set_instr(gowin_info->tap, NO_OP);
+ if (retval != ERROR_OK)
+ return retval;
+
+ retval = jtag_execute_queue();
+
+ return retval;
+}
+
+static int gowin_read_register_command(struct pld_device *pld_device, uint32_t cmd, uint32_t *value)
+{
+ if (!pld_device)
+ return ERROR_FAIL;
+
+ struct gowin_pld_device *gowin_info = pld_device->driver_priv;
+
+ if (!gowin_info || !gowin_info->tap)
+ return ERROR_FAIL;
+
+ return gowin_read_register(gowin_info->tap, cmd, value);
+}
+
+static int gowin_reload_command(struct pld_device *pld_device)
+{
+ if (!pld_device)
+ return ERROR_FAIL;
+
+ struct gowin_pld_device *gowin_info = pld_device->driver_priv;
+
+ if (!gowin_info || !gowin_info->tap)
+ return ERROR_FAIL;
+
+ return gowin_reload(gowin_info->tap);
+}
+
+COMMAND_HANDLER(gowin_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;
+ }
+
+ uint32_t status = 0;
+ int retval = gowin_read_register_command(device, STATUS_REGISTER, &status);
+
+ if (retval == ERROR_OK)
+ command_print(CMD, "0x%8.8" PRIx32, status);
+
+ return retval;
+}
+
+COMMAND_HANDLER(gowin_read_user_register_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;
+ }
+
+ uint32_t user_reg = 0;
+ int retval = gowin_read_register_command(device, READ_USERCODE, &user_reg);
+
+ if (retval == ERROR_OK)
+ command_print(CMD, "0x%8.8" PRIx32, user_reg);
+
+ return retval;
+}
+
+COMMAND_HANDLER(gowin_reload_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;
+ }
+
+ return gowin_reload_command(device);
+}
+
+static const struct command_registration gowin_exec_command_handlers[] = {
+ {
+ .name = "read_status",
+ .mode = COMMAND_EXEC,
+ .handler = gowin_read_status_command_handler,
+ .help = "reading status register from FPGA",
+ .usage = "num_pld",
+ }, {
+ .name = "read_user",
+ .mode = COMMAND_EXEC,
+ .handler = gowin_read_user_register_command_handler,
+ .help = "reading user register from FPGA",
+ .usage = "num_pld",
+ }, {
+ .name = "reload",
+ .mode = COMMAND_EXEC,
+ .handler = gowin_reload_command_handler,
+ .help = "reloading bitstream from flash to SRAM",
+ .usage = "num_pld",
+ },
+ COMMAND_REGISTRATION_DONE
+};
+
+static const struct command_registration gowin_command_handler[] = {
+ {
+ .name = "gowin",
+ .mode = COMMAND_ANY,
+ .help = "gowin specific commands",
+ .usage = "",
+ .chain = gowin_exec_command_handlers
+ },
+ COMMAND_REGISTRATION_DONE
+};
+
+PLD_DEVICE_COMMAND_HANDLER(gowin_pld_device_command)
+{
+ struct jtag_tap *tap;
+
+ struct gowin_pld_device *gowin_info;
+
+ if (CMD_ARGC != 2)
+ return ERROR_COMMAND_SYNTAX_ERROR;
+
+ tap = jtag_tap_by_string(CMD_ARGV[1]);
+ if (!tap) {
+ command_print(CMD, "Tap: %s does not exist", CMD_ARGV[1]);
+ return ERROR_FAIL;
+ }
+
+ gowin_info = malloc(sizeof(struct gowin_pld_device));
+ if (!gowin_info) {
+ LOG_ERROR("Out of memory");
+ return ERROR_FAIL;
+ }
+ gowin_info->tap = tap;
+
+ pld->driver_priv = gowin_info;
+
+ return ERROR_OK;
+}
+
+struct pld_driver gowin_pld = {
+ .name = "gowin",
+ .commands = gowin_command_handler,
+ .pld_device_command = &gowin_pld_device_command,
+ .load = &gowin_load_to_sram,
+};
diff --git a/src/pld/pld.c b/src/pld/pld.c
index f4f79ba..d9e01f1 100644
--- a/src/pld/pld.c
+++ b/src/pld/pld.c
@@ -19,12 +19,14 @@
/* pld drivers
*/
extern struct pld_driver efinix_pld;
+extern struct pld_driver gowin_pld;
extern struct pld_driver intel_pld;
extern struct pld_driver lattice_pld;
extern struct pld_driver virtex2_pld;
static struct pld_driver *pld_drivers[] = {
&efinix_pld,
+ &gowin_pld,
&intel_pld,
&lattice_pld,
&virtex2_pld,
diff --git a/tcl/board/gowin_runber.cfg b/tcl/board/gowin_runber.cfg
new file mode 100644
index 0000000..9496c6f
--- /dev/null
+++ b/tcl/board/gowin_runber.cfg
@@ -0,0 +1,19 @@
+# SPDX-License-Identifier: GPL-2.0-or-later
+
+# Gowin RUNBER FPGA Development Board
+# https://www.seeedstudio.com/Gowin-RUNBER-Development-Board-p-4779.html
+
+adapter driver ftdi
+ftdi vid_pid 0x0403 0x6010
+
+ftdi channel 0
+ftdi layout_init 0x0008 0x008b
+reset_config none
+transport select jtag
+adapter speed 6000
+
+source [find fpga/gowin_gw1n.cfg]
+
+
+#openocd -f board/gowin_runber.cfg -c "init" -c "pld load 0 impl/pnr/gw1n_blinker.fs"
+#ipdbg -start -tap gw1n.tap -hub 0x42 -port 5555 -tool 0
diff --git a/tcl/fpga/gowin_gw1n.cfg b/tcl/fpga/gowin_gw1n.cfg
new file mode 100644
index 0000000..43d66b7
--- /dev/null
+++ b/tcl/fpga/gowin_gw1n.cfg
@@ -0,0 +1,29 @@
+# SPDX-License-Identifier: GPL-2.0-or-later
+
+# Gowin FPGA IDCODEs
+# from JTAG Programming and Configuration Guide
+# http://cdn.gowinsemi.com.cn/TN653E.pdf
+
+if { [info exists CHIPNAME] } {
+ set _CHIPNAME $CHIPNAME
+} else {
+ set _CHIPNAME gw1n
+}
+
+jtag newtap $_CHIPNAME tap -irlen 8 -ignore-version \
+ -expected-id 0x0900281B \
+ -expected-id 0x0900381B \
+ -expected-id 0x0100681B \
+ -expected-id 0x0300081B \
+ -expected-id 0x0300181B \
+ -expected-id 0x0120681B \
+ -expected-id 0x0100381B \
+ -expected-id 0x1100381B \
+ -expected-id 0x0100981B \
+ -expected-id 0x1100581B \
+ -expected-id 0x1100481B \
+ -expected-id 0x0100181B \
+ -expected-id 0x1100181B \
+ -expected-id 0x0100481B
+
+pld device gowin $_CHIPNAME.tap