This is an automated email from Gerrit. "Daniel Anselmi <danse...@gmx.ch>" just uploaded a new patch set to Gerrit, which you can find at https://review.openocd.org/c/openocd/+/7368
-- gerrit commit 7c9832aed407eee8acf1427fbe45f94da7ee05cb Author: Daniel Anselmi <danse...@gmx.ch> Date: Fri Oct 14 00:57:12 2022 +0200 pld: add support for gowin devices Change-Id: Idd1a09514bbbbe0a7b54d69010f6c2f91215fd1d Signed-off-by: Daniel Anselmi <danse...@gmx.ch> diff --git a/doc/openocd.texi b/doc/openocd.texi index 1a2b4cbb5a..f1081d51fc 100644 --- a/doc/openocd.texi +++ b/doc/openocd.texi @@ -8606,6 +8606,16 @@ With a value of -1 for @var{pos} the check will be omitted. @end deffn @end deffn +@deffn {FPGA Driver} {gowin} +This driver can be used to load the bitstream into FPGAs form Gowin. +It is possible to program the SRAM. Programming the flash is not supported. + +@example +openocd -f board/gowin_runber.cfg -c "init" \ + -c "pld load 0 impl/pnr/gw1n_blinker.fs" +@end example +@end deffn + @node General Commands @chapter General Commands diff --git a/src/pld/Makefile.am b/src/pld/Makefile.am index a42ed827d3..9bd1cf7f42 100644 --- a/src/pld/Makefile.am +++ b/src/pld/Makefile.am @@ -13,6 +13,7 @@ noinst_LTLIBRARIES += %D%/libpld.la %D%/certus.c \ %D%/efinix.c \ %D%/intel.c \ + %D%/gowin.c \ %D%/pld.h \ %D%/xilinx_bit.h \ %D%/virtex2.h \ diff --git a/src/pld/gowin.c b/src/pld/gowin.c new file mode 100644 index 0000000000..4b140f7563 --- /dev/null +++ b/src/pld/gowin.c @@ -0,0 +1,606 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + +/*************************************************************************** + * Copyright (C) 2022 by Daniel Anselmi * + * danse...@gmx.ch * + ***************************************************************************/ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <jtag/jtag.h> +#include <jtag/adapter.h> +#include <sys/stat.h> +#include "pld.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 (0x00000001 << 5) +#define STAUS_MASK_SYSTEM_EDIT_MODE (0x00000001 << 7) + +#define GW1N 1 /* GW1N(R)-1/2/4 */ +#define GW1NS 2 /* GW1NS-2 and GW1NS(R)-2C */ +#define GW1NZ 3 /* GW1N(R)-6/9 and GW1NZ-1 */ +#define GW2A 4 /* GW2A-18/55 */ + +struct gowin_pld_device { + struct jtag_tap *tap; +}; + +struct gowin_bit_file { + size_t length; + size_t capacity; + uint8_t *data; + 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 int gowin_read_bin_file(struct gowin_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 from file %s: %s", filename, strerror(errno)); + return ERROR_PLD_FILE_LOAD_FAILED; + } + bit_file->length = (uint32_t)length; + + bit_file->data = (uint8_t *)malloc(bit_file->length); + if (!bit_file->data) { + fclose(input_file); + LOG_ERROR("Unable to allocate memory for the bitstream"); + return ERROR_PLD_FILE_LOAD_FAILED; + } + + long read_count = fread(bit_file->data, sizeof(char), bit_file->length, input_file); + fclose(input_file); + if (read_count != (long)bit_file->length) + return ERROR_PLD_FILE_LOAD_FAILED; + + return ERROR_OK; +} + +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->length + 1 > bit_file->capacity) { + uint8_t *buffer; + if (bit_file->data) + buffer = realloc(bit_file->data, bit_file->capacity + 8192); + else + buffer = malloc(8192); + if (!buffer) + return ERROR_FAIL; + bit_file->data = buffer; + bit_file->capacity += 8192; + } + + bit_file->data[bit_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 & (0x1 << 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->data); + 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->data); + return ERROR_FAIL; + } + if (digits[0] == '\n') + continue; + + if (strlen(digits) != 8) { + free(bit_file->data); + 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->data); + return ERROR_FAIL; + } + } while (1); + + 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; + + struct stat input_stat; + if (stat(filename, &input_stat) == -1) { + LOG_ERROR("couldn't stat() %s: %s", filename, + strerror(errno)); + return ERROR_PLD_FILE_LOAD_FAILED; + } + + if (S_ISDIR(input_stat.st_mode)) { + LOG_ERROR("%s is a directory", filename); + return ERROR_PLD_FILE_LOAD_FAILED; + } + + if (input_stat.st_size == 0) { + LOG_ERROR("Empty file %s", filename); + return ERROR_PLD_FILE_LOAD_FAILED; + } + + /* 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, ".bin") == 0) { + *is_fs = false; + return gowin_read_bin_file(bit_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"); + return ERROR_PLD_FILE_LOAD_FAILED; +} + +static void gowin_set_instr(struct jtag_tap *tap, + uint32_t new_instr) +{ + struct scan_field field; + + field.num_bits = tap->ir_length; + + void *t = calloc(DIV_ROUND_UP(field.num_bits, 8), 1); + 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); +} + +static int gowin_read_register(struct jtag_tap *tap, uint32_t reg, uint32_t *result) +{ + struct scan_field field; + + gowin_set_instr(tap, reg); + int retval = jtag_execute_queue(); + if (retval != ERROR_OK) + return retval; + + field.check_mask = NULL; + field.check_value = NULL; + field.num_bits = 32; + field.out_value = (uint8_t *)result; + field.in_value = (uint8_t *)result; + + jtag_add_dr_scan(tap, 1, &field, TAP_IDLE); + return jtag_execute_queue(); +} + +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) +{ + gowin_set_instr(tap, CONFIG_ENABLE); + int 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) +{ + gowin_set_instr(tap, CONFIG_DISABLE); + int 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) +{ + gowin_set_instr(tap, RELOAD); + gowin_set_instr(tap, NO_OP); + 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 */ + gowin_set_instr(tap, ERASE_SRAM); + gowin_set_instr(tap, NO_OP); + + /* Delay or Run Test 2~10ms */ + /* 10 ms is worst case for GW2A-55 */ + jtag_add_sleep(10000); + int 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) { + gowin_set_instr(tap, SRAM_ERASE_DONE); + gowin_set_instr(tap, NO_OP); + 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; + } + + gowin_set_instr(tap, NO_OP); + 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.length; i++) + bit_file.data[i] = flip_u32(bit_file.data[i], 8); + + uint32_t id; + retval = gowin_read_register(tap, IDCODE, &id); + if (retval != ERROR_OK) { + free(bit_file.data); + return retval; + } + + if (is_fs && id != bit_file.id) { + free(bit_file.data); + LOG_ERROR("id in file: 0x%8.8" PRIx32 " and id on device: 0x%8.8" PRIx32 "", bit_file.id, id); + return ERROR_FAIL; + } + + retval = gowin_enable_config(tap); + if (retval != ERROR_OK) { + free(bit_file.data); + return retval; + } + + retval = gowin_erase_sram(tap, false); + if (retval != ERROR_OK) { + free(bit_file.data); + return retval; + } + + gowin_set_instr(tap, ADDRESS_INITIALIZATION); + gowin_set_instr(tap, TRANSFER_CONFIGURATION_DATA); + + /* scan out the bitstream */ + struct scan_field field; + field.num_bits = bit_file.length * 8; + field.out_value = bit_file.data; + field.in_value = bit_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.data); + return retval; + } + + retval = gowin_disable_config(tap); + free(bit_file.data); + if (retval != ERROR_OK) + return retval; + + gowin_set_instr(gowin_info->tap, NO_OP); + + 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 [GW2A] [verbose]", + .chain = NULL, + }, { + .name = "read_user", + .mode = COMMAND_EXEC, + .handler = gowin_read_user_register_command_handler, + .help = "reading user register from FPGA", + .usage = "num_pld", + .chain = NULL, + }, { + .name = "reload", + .mode = COMMAND_EXEC, + .handler = gowin_reload_command_handler, + .help = "reloading bitstream from flash to SRAM", + .usage = "num_pld", + .chain = NULL, + }, + COMMAND_REGISTRATION_DONE +}; + +static const struct command_registration gowin_command_handler[] = { + { + .name = "gowin", + .mode = COMMAND_ANY, + .handler = NULL, + .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 fd2a770204..5c2bb91d78 100644 --- a/src/pld/pld.c +++ b/src/pld/pld.c @@ -23,11 +23,13 @@ extern struct pld_driver ecp5_pld; extern struct pld_driver trion_pld; extern struct pld_driver certus_pld; extern struct pld_driver intel_pld; +extern struct pld_driver gowin_pld; static struct pld_driver *pld_drivers[] = { &certus_pld, &ecp2_3_pld, &ecp5_pld, + &gowin_pld, &intel_pld, &trion_pld, &virtex2_pld, diff --git a/tcl/board/gowin_runber.cfg b/tcl/board/gowin_runber.cfg new file mode 100644 index 0000000000..41406a827d --- /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 -idle 3 diff --git a/tcl/fpga/gowin_gw1n.cfg b/tcl/fpga/gowin_gw1n.cfg new file mode 100644 index 0000000000..d522376fd4 --- /dev/null +++ b/tcl/fpga/gowin_gw1n.cfg @@ -0,0 +1,27 @@ +# SPDX-License-Identifier: GPL-2.0-or-later + +# efinix trion +# https://www.efinixinc.com/docs/an021-jtag-bst-trion-v1.0.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 0x0000081B \ + -expected-id 0x0000281B + +pld device gowin $_CHIPNAME.tap --