From fe5ed48f40e4f1b36d74900d0d9b410affea6bdb Mon Sep 17 00:00:00 2001 From: Daniel Anselmi Date: Fri, 24 Feb 2023 15:57:30 +0100 Subject: [PATCH] jtagspi/pld: add interface to get support from pld drivers Jtagspi is using a proxy bitstream to "connect" JTAG to the SPI pins. This is not possible with all FPGA vendors/families. In this cases a dedicated procedure is needed to establish such a connection. This patch adds a jtagspi-mode for these cases. It also adds the needed interfaces to jtagspi and the pld-driver so the driver can select the mode and provide the necessary procedures. For the cases where a proxy bitstream is needed, the pld driver will select the mode and provide instruction code needed in this case. Change-Id: I9563f26739589157b39a3664a73d91152cd13f77 Signed-off-by: Daniel Anselmi Reviewed-on: https://review.openocd.org/c/openocd/+/7822 Tested-by: jenkins Reviewed-by: Antonio Borneo --- doc/openocd.texi | 56 ++++++++++++++----- src/flash/nor/jtagspi.c | 119 ++++++++++++++++++++++++++++++---------- src/pld/pld.c | 91 +++++++++++++++++++++++++++++- src/pld/pld.h | 14 +++++ tcl/cpld/jtagspi.cfg | 12 +++- 5 files changed, 245 insertions(+), 47 deletions(-) diff --git a/doc/openocd.texi b/doc/openocd.texi index f6f7a0c293..7ad48c8629 100644 --- a/doc/openocd.texi +++ b/doc/openocd.texi @@ -5900,24 +5900,42 @@ flash bank $_FLASHNAME cfi 0x00000000 0x02000000 2 4 $_TARGETNAME @c "cfi part_id" disabled @end deffn +@anchor{jtagspi} @deffn {Flash Driver} {jtagspi} @cindex Generic JTAG2SPI driver @cindex SPI @cindex jtagspi @cindex bscan_spi Several FPGAs and CPLDs can retrieve their configuration (bitstream) from a -SPI flash connected to them. To access this flash from the host, the device -is first programmed with a special proxy bitstream that -exposes the SPI flash on the device's JTAG interface. The flash can then be -accessed through JTAG. +SPI flash connected to them. To access this flash from the host, some FPGA +device provides dedicated JTAG instructions, while other FPGA devices should +be programmed with a special proxy bitstream that exposes the SPI flash on +the device's JTAG interface. The flash can then be accessed through JTAG. -Since signaling between JTAG and SPI is compatible, all that is required for +Since signalling between JTAG and SPI is compatible, all that is required for a proxy bitstream is to connect TDI-MOSI, TDO-MISO, TCK-CLK and activate -the flash chip select when the JTAG state machine is in SHIFT-DR. Such -a bitstream for several Xilinx FPGAs can be found in +the flash chip select when the JTAG state machine is in SHIFT-DR. + +Such a bitstream for several Xilinx FPGAs can be found in @file{contrib/loaders/flash/fpga/xilinx_bscan_spi.py}. It requires @uref{https://github.com/m-labs/migen, migen} and a Xilinx toolchain to build. +This mechanism with a proxy bitstream can also be used for FPGAs from Intel and +Efinix. FPGAs from Lattice and Cologne Chip have dedicated JTAG instructions +and procedure to connect the JTAG to the SPI signals and don't need a proxy +bitstream. Support for these devices with dedicated procedure is provided by +the pld drivers. For convenience the PLD drivers will provide the USERx code +for FPGAs with a proxy bitstream. Currently the following PLD drivers are able +to support jtagspi: +@itemize +@item Efinix: proxy-bitstream +@item Gatemate: dedicated procedure +@item Intel/Altera: proxy-bitstream +@item Lattice: dedicated procedure supporting ECP2, ECP3, ECP5, Certus and Certus Pro devices +@item AMD/Xilinx: proxy-bitstream +@end itemize + + This flash bank driver requires a target on a JTAG tap and will access that tap directly. Since no support from the target is needed, the target can be a "testee" dummy. Since the target does not expose the flash memory @@ -5935,14 +5953,25 @@ command, see below. @item @var{ir} ... is loaded into the JTAG IR to map the flash as the JTAG DR. For the bitstreams generated from @file{xilinx_bscan_spi.py} this is the @var{USER1} instruction. -@end itemize +@example +target create $_TARGETNAME testee -chain-position $_CHIPNAME.tap +set _USER1_INSTR_CODE 0x02 +flash bank $_FLASHNAME jtagspi 0x0 0 0 0 \ + $_TARGETNAME $_USER1_INSTR_CODE +@end example + +@item The option @option{-pld} @var{name} is used to have support from the +PLD driver of pld device @var{name}. The name is the name of the pld device +given during creation of the pld device. +Pld device names are shown by the @command{pld devices} command. @example -target create $_TARGETNAME testee -chain-position $_CHIPNAME.fpga -set _XILINX_USER1 0x02 -flash bank $_FLASHNAME spi 0x0 0 0 0 \ - $_TARGETNAME $_XILINX_USER1 +target create $_TARGETNAME testee -chain-position $_CHIPNAME.tap +set _JTAGSPI_CHAIN_ID $_CHIPNAME.pld +flash bank $_FLASHNAME jtagspi 0x0 0 0 0 \ + $_TARGETNAME -pld $_JTAGSPI_CHAIN_ID @end example +@end itemize @deffn Command {jtagspi set} bank_id name total_size page_size read_cmd unused pprg_cmd mass_erase_cmd sector_size sector_erase_cmd Sets flash parameters: @var{name} human readable string, @var{total_size} @@ -8668,7 +8697,8 @@ Accordingly, both are called PLDs here. As it does for JTAG TAPs, debug targets, and flash chips (both NOR and NAND), OpenOCD maintains a list of PLDs available for use in various commands. -Also, each such PLD requires a driver. +Also, each such PLD requires a driver. PLD drivers may also be needed to program +SPI flash connected to the FPGA to store the bitstream (@xref{jtagspi} for details). They are referenced by the name which was given when the pld was created or the number shown by the @command{pld devices} command. diff --git a/src/flash/nor/jtagspi.c b/src/flash/nor/jtagspi.c index 6bb3af9b7d..4b975390be 100644 --- a/src/flash/nor/jtagspi.c +++ b/src/flash/nor/jtagspi.c @@ -12,6 +12,7 @@ #include #include #include +#include #define JTAGSPI_MAX_TIMEOUT 3000 @@ -21,19 +22,44 @@ struct jtagspi_flash_bank { struct flash_device dev; char devname[32]; bool probed; - bool always_4byte; /* use always 4-byte address except for basic read 0x03 */ - uint32_t ir; - unsigned int addr_len; /* address length in bytes */ + bool always_4byte; /* use always 4-byte address except for basic read 0x03 */ + unsigned int addr_len; /* address length in bytes */ + struct pld_device *pld_device; /* if not NULL, the PLD has special instructions for JTAGSPI */ + uint32_t ir; /* when !pld_device, this instruction code is used in + jtagspi_set_user_ir to connect through a proxy bitstream */ }; FLASH_BANK_COMMAND_HANDLER(jtagspi_flash_bank_command) { - struct jtagspi_flash_bank *info; - if (CMD_ARGC < 7) return ERROR_COMMAND_SYNTAX_ERROR; - info = malloc(sizeof(struct jtagspi_flash_bank)); + unsigned int ir = 0; + struct pld_device *device = NULL; + if (strcmp(CMD_ARGV[6], "-pld") == 0) { + if (CMD_ARGC < 8) + return ERROR_COMMAND_SYNTAX_ERROR; + device = get_pld_device_by_name_or_numstr(CMD_ARGV[7]); + if (device) { + bool has_jtagspi_instruction = false; + int retval = pld_has_jtagspi_instruction(device, &has_jtagspi_instruction); + if (retval != ERROR_OK) + return retval; + if (!has_jtagspi_instruction) { + retval = pld_get_jtagspi_userircode(device, &ir); + if (retval != ERROR_OK) + return retval; + device = NULL; + } + } else { + LOG_ERROR("pld device '#%s' is out of bounds or unknown", CMD_ARGV[7]); + return ERROR_FAIL; + } + } else { + COMMAND_PARSE_NUMBER(uint, CMD_ARGV[6], ir); + } + + struct jtagspi_flash_bank *info = calloc(1, sizeof(struct jtagspi_flash_bank)); if (!info) { LOG_ERROR("no memory for flash bank info"); return ERROR_FAIL; @@ -47,18 +73,19 @@ FLASH_BANK_COMMAND_HANDLER(jtagspi_flash_bank_command) } info->tap = bank->target->tap; info->probed = false; - COMMAND_PARSE_NUMBER(u32, CMD_ARGV[6], info->ir); + + info->ir = ir; + info->pld_device = device; return ERROR_OK; } -static void jtagspi_set_ir(struct flash_bank *bank) +static void jtagspi_set_user_ir(struct jtagspi_flash_bank *info) { - struct jtagspi_flash_bank *info = bank->driver_priv; struct scan_field field; uint8_t buf[4] = { 0 }; - LOG_DEBUG("loading jtagspi ir"); + LOG_DEBUG("loading jtagspi ir(0x%" PRIx32 ")", info->ir); buf_set_u32(buf, 0, info->tap->ir_length, info->ir); field.num_bits = info->tap->ir_length; field.out_value = buf; @@ -79,6 +106,7 @@ static int jtagspi_cmd(struct flash_bank *bank, uint8_t cmd, assert(data_buffer || data_len == 0); struct scan_field fields[6]; + struct jtagspi_flash_bank *info = bank->driver_priv; LOG_DEBUG("cmd=0x%02x write_len=%d data_len=%d", cmd, write_len, data_len); @@ -87,22 +115,34 @@ static int jtagspi_cmd(struct flash_bank *bank, uint8_t cmd, if (is_read) data_len = -data_len; + unsigned int facing_read_bits = 0; + unsigned int trailing_write_bits = 0; + + if (info->pld_device) { + int retval = pld_get_jtagspi_stuff_bits(info->pld_device, &facing_read_bits, &trailing_write_bits); + if (retval != ERROR_OK) + return retval; + } + int n = 0; const uint8_t marker = 1; - fields[n].num_bits = 1; - fields[n].out_value = ▮ - fields[n].in_value = NULL; - n++; - - /* transfer length = cmd + address + read/write, - * -1 due to the counter implementation */ uint8_t xfer_bits[4]; - h_u32_to_be(xfer_bits, ((sizeof(cmd) + write_len + data_len) * CHAR_BIT) - 1); - flip_u8(xfer_bits, xfer_bits, sizeof(xfer_bits)); - fields[n].num_bits = sizeof(xfer_bits) * CHAR_BIT; - fields[n].out_value = xfer_bits; - fields[n].in_value = NULL; - n++; + if (!info->pld_device) { /* mode == JTAGSPI_MODE_PROXY_BITSTREAM */ + facing_read_bits = jtag_tap_count_enabled(); + fields[n].num_bits = 1; + fields[n].out_value = ▮ + fields[n].in_value = NULL; + n++; + + /* transfer length = cmd + address + read/write, + * -1 due to the counter implementation */ + h_u32_to_be(xfer_bits, ((sizeof(cmd) + write_len + data_len) * CHAR_BIT) - 1); + flip_u8(xfer_bits, xfer_bits, sizeof(xfer_bits)); + fields[n].num_bits = sizeof(xfer_bits) * CHAR_BIT; + fields[n].out_value = xfer_bits; + fields[n].in_value = NULL; + n++; + } flip_u8(&cmd, &cmd, sizeof(cmd)); fields[n].num_bits = sizeof(cmd) * CHAR_BIT; @@ -120,10 +160,12 @@ static int jtagspi_cmd(struct flash_bank *bank, uint8_t cmd, if (data_len > 0) { if (is_read) { - fields[n].num_bits = jtag_tap_count_enabled(); - fields[n].out_value = NULL; - fields[n].in_value = NULL; - n++; + if (facing_read_bits) { + fields[n].num_bits = facing_read_bits; + fields[n].out_value = NULL; + fields[n].in_value = NULL; + n++; + } fields[n].out_value = NULL; fields[n].in_value = data_buffer; @@ -135,16 +177,33 @@ static int jtagspi_cmd(struct flash_bank *bank, uint8_t cmd, fields[n].num_bits = data_len * CHAR_BIT; n++; } + if (!is_read && trailing_write_bits) { + fields[n].num_bits = trailing_write_bits; + fields[n].out_value = NULL; + fields[n].in_value = NULL; + n++; + } + + if (info->pld_device) { + int retval = pld_connect_spi_to_jtag(info->pld_device); + if (retval != ERROR_OK) + return retval; + } else { + jtagspi_set_user_ir(info); + } - jtagspi_set_ir(bank); /* passing from an IR scan to SHIFT-DR clears BYPASS registers */ - struct jtagspi_flash_bank *info = bank->driver_priv; jtag_add_dr_scan(info->tap, n, fields, TAP_IDLE); int retval = jtag_execute_queue(); + if (retval != ERROR_OK) + return retval; if (is_read) flip_u8(data_buffer, data_buffer, data_len); - return retval; + + if (info->pld_device) + return pld_disconnect_spi_from_jtag(info->pld_device); + return ERROR_OK; } COMMAND_HANDLER(jtagspi_handle_set) diff --git a/src/pld/pld.c b/src/pld/pld.c index c375418a9d..81fb0c4632 100644 --- a/src/pld/pld.c +++ b/src/pld/pld.c @@ -69,8 +69,95 @@ struct pld_device *get_pld_device_by_name_or_numstr(const char *str) return get_pld_device_by_num(dev_num); } -/* @deffn {Config Command} {pld create} pld_name driver -chain-position tap_name [options] -*/ + +int pld_has_jtagspi_instruction(struct pld_device *pld_device, bool *has_instruction) +{ + *has_instruction = false; /* default is using a proxy bitstream */ + + if (!pld_device) + return ERROR_FAIL; + + struct pld_driver *pld_driver = pld_device->driver; + if (!pld_driver) { + LOG_ERROR("pld device has no associated driver"); + return ERROR_FAIL; + } + + if (pld_driver->has_jtagspi_instruction) + return pld_driver->has_jtagspi_instruction(pld_device, has_instruction); + /* else, take the default (proxy bitstream) */ + return ERROR_OK; +} + +int pld_get_jtagspi_userircode(struct pld_device *pld_device, unsigned int *ir) +{ + if (!pld_device) + return ERROR_FAIL; + + struct pld_driver *pld_driver = pld_device->driver; + if (!pld_driver) { + LOG_ERROR("pld device has no associated driver"); + return ERROR_FAIL; + } + + if (pld_driver->get_jtagspi_userircode) + return pld_driver->get_jtagspi_userircode(pld_device, ir); + + return ERROR_FAIL; +} + +int pld_get_jtagspi_stuff_bits(struct pld_device *pld_device, unsigned int *facing_read_bits, + unsigned int *trailing_write_bits) +{ + if (!pld_device) + return ERROR_FAIL; + + struct pld_driver *pld_driver = pld_device->driver; + if (!pld_driver) { + LOG_ERROR("pld device has no associated driver"); + return ERROR_FAIL; + } + + if (pld_driver->get_stuff_bits) + return pld_driver->get_stuff_bits(pld_device, facing_read_bits, trailing_write_bits); + + return ERROR_OK; +} + +int pld_connect_spi_to_jtag(struct pld_device *pld_device) +{ + if (!pld_device) + return ERROR_FAIL; + + struct pld_driver *pld_driver = pld_device->driver; + if (!pld_driver) { + LOG_ERROR("pld device has no associated driver"); + return ERROR_FAIL; + } + + if (pld_driver->connect_spi_to_jtag) + return pld_driver->connect_spi_to_jtag(pld_device); + + return ERROR_FAIL; +} + +int pld_disconnect_spi_from_jtag(struct pld_device *pld_device) +{ + if (!pld_device) + return ERROR_FAIL; + + struct pld_driver *pld_driver = pld_device->driver; + if (!pld_driver) { + LOG_ERROR("pld device has no associated driver"); + return ERROR_FAIL; + } + + if (pld_driver->disconnect_spi_from_jtag) + return pld_driver->disconnect_spi_from_jtag(pld_device); + + return ERROR_FAIL; +} + COMMAND_HANDLER(handle_pld_create_command) { if (CMD_ARGC < 2) diff --git a/src/pld/pld.h b/src/pld/pld.h index b736e6ae20..5e2fcd20cc 100644 --- a/src/pld/pld.h +++ b/src/pld/pld.h @@ -20,12 +20,26 @@ struct pld_ipdbg_hub { unsigned int user_ir_code; }; +int pld_has_jtagspi_instruction(struct pld_device *device, bool *has_instruction); +int pld_get_jtagspi_userircode(struct pld_device *pld_device, unsigned int *ir); + +int pld_get_jtagspi_stuff_bits(struct pld_device *pld_device, unsigned int *facing_read_bits, + unsigned int *trailing_write_bits); +int pld_connect_spi_to_jtag(struct pld_device *pld_device); +int pld_disconnect_spi_from_jtag(struct pld_device *pld_device); + struct pld_driver { const char *name; __PLD_CREATE_COMMAND((*pld_create_command)); const struct command_registration *commands; int (*load)(struct pld_device *pld_device, const char *filename); int (*get_ipdbg_hub)(int user_num, struct pld_device *pld_device, struct pld_ipdbg_hub *hub); + int (*has_jtagspi_instruction)(struct pld_device *device, bool *has_instruction); + int (*get_jtagspi_userircode)(struct pld_device *pld_device, unsigned int *ir); + int (*connect_spi_to_jtag)(struct pld_device *pld_device); + int (*disconnect_spi_from_jtag)(struct pld_device *pld_device); + int (*get_stuff_bits)(struct pld_device *pld_device, unsigned int *facing_read_bits, + unsigned int *trailing_write_bits); }; #define PLD_CREATE_COMMAND_HANDLER(name) \ diff --git a/tcl/cpld/jtagspi.cfg b/tcl/cpld/jtagspi.cfg index 4c84792fe1..a7f02b9770 100644 --- a/tcl/cpld/jtagspi.cfg +++ b/tcl/cpld/jtagspi.cfg @@ -4,6 +4,8 @@ set _USER1 0x02 if { [info exists JTAGSPI_IR] } { set _JTAGSPI_IR $JTAGSPI_IR +} elseif {[info exists JTAGSPI_CHAIN_ID]} { + set _JTAGSPI_CHAIN_ID $JTAGSPI_CHAIN_ID } else { set _JTAGSPI_IR $_USER1 } @@ -21,7 +23,11 @@ if { [info exists FLASHNAME] } { } target create $_TARGETNAME testee -chain-position $_CHIPNAME.tap -flash bank $_FLASHNAME jtagspi 0 0 0 0 $_TARGETNAME $_JTAGSPI_IR +if { [info exists _JTAGSPI_IR] } { + flash bank $_FLASHNAME jtagspi 0 0 0 0 $_TARGETNAME $_JTAGSPI_IR +} else { + flash bank $_FLASHNAME jtagspi 0 0 0 0 $_TARGETNAME -pld $_JTAGSPI_CHAIN_ID +} # initialize jtagspi flash # chain_id: identifier of pld (you can get a list with 'pld devices') @@ -33,7 +39,9 @@ flash bank $_FLASHNAME jtagspi 0 0 0 0 $_TARGETNAME $_JTAGSPI_IR proc jtagspi_init {chain_id proxy_bit {release_from_pwr_down_cmd -1}} { # load proxy bitstream $proxy_bit and probe spi flash global _FLASHNAME - pld load $chain_id $proxy_bit + if { $proxy_bit ne "" } { + pld load $chain_id $proxy_bit + } reset halt if {$release_from_pwr_down_cmd != -1} { jtagspi cmd $_FLASHNAME 0 $release_from_pwr_down_cmd