From: openocd-gerrit <ope...@us...> - 2023-09-23 14:34:12
|
This is an automated email from the git hooks/post-receive script. It was generated because a ref change was pushed to the repository containing the project "Main OpenOCD repository". The branch, master has been updated via fe5ed48f40e4f1b36d74900d0d9b410affea6bdb (commit) from 30375c6439ee97f60d729db747f01fb4eb2cf495 (commit) Those revisions listed above that are new to this repository have not appeared on any other notification email; so we list those revisions in full, below. - Log ----------------------------------------------------------------- commit fe5ed48f40e4f1b36d74900d0d9b410affea6bdb Author: Daniel Anselmi <dan...@gm...> Date: Fri Feb 24 15:57:30 2023 +0100 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 <dan...@gm...> Reviewed-on: https://review.openocd.org/c/openocd/+/7822 Tested-by: jenkins Reviewed-by: Antonio Borneo <bor...@gm...> diff --git a/doc/openocd.texi b/doc/openocd.texi index f6f7a0c29..7ad48c862 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 6bb3af9b7..4b975390b 100644 --- a/src/flash/nor/jtagspi.c +++ b/src/flash/nor/jtagspi.c @@ -12,6 +12,7 @@ #include <jtag/jtag.h> #include <flash/nor/spi.h> #include <helper/time_support.h> +#include <pld/pld.h> #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 c375418a9..81fb0c463 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 b736e6ae2..5e2fcd20c 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 4c84792fe..a7f02b977 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 ----------------------------------------------------------------------- Summary of changes: 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(-) hooks/post-receive -- Main OpenOCD repository |