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/+/7822
-- gerrit commit 3d26da17b3e902816238b0a86eb921bca711ac65 Author: Daniel Anselmi <danse...@gmx.ch> Date: Fri Feb 24 15:57:30 2023 +0100 jtagspi/pld: add interface to get support from pld drivers Change-Id: I9563f26739589157b39a3664a73d91152cd13f77 Signed-off-by: Daniel Anselmi <danse...@gmx.ch> diff --git a/doc/openocd.texi b/doc/openocd.texi index 12a8ca56df..89ad921668 100644 --- a/doc/openocd.texi +++ b/doc/openocd.texi @@ -5817,6 +5817,7 @@ 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 @@ -5824,17 +5825,34 @@ flash bank $_FLASHNAME cfi 0x00000000 0x02000000 2 4 $_TARGETNAME @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. +is first programmed with a special proxy bitstream that exposes the SPI flash +on the device's JTAG interface or with dedicated JTAG instructions. 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 drives 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 @@ -5852,14 +5870,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} @@ -8579,7 +8608,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/Makefile.am b/src/flash/nor/Makefile.am index 534a7a804e..06d20eebad 100644 --- a/src/flash/nor/Makefile.am +++ b/src/flash/nor/Makefile.am @@ -89,6 +89,7 @@ NORHEADERS = \ %D%/cfi.h \ %D%/driver.h \ %D%/imp.h \ + %D%/jtagspi.h \ %D%/non_cfi.h \ %D%/ocl.h \ %D%/sfdp.h \ diff --git a/src/flash/nor/jtagspi.c b/src/flash/nor/jtagspi.c index 6bb3af9b7d..0dfaa2757b 100644 --- a/src/flash/nor/jtagspi.c +++ b/src/flash/nor/jtagspi.c @@ -12,28 +12,65 @@ #include <jtag/jtag.h> #include <flash/nor/spi.h> #include <helper/time_support.h> +#include <pld/pld.h> +#include "jtagspi.h" #define JTAGSPI_MAX_TIMEOUT 3000 + struct jtagspi_flash_bank { struct jtag_tap *tap; 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 */ + struct pld_jtagspi pld_jtagspi; }; -FLASH_BANK_COMMAND_HANDLER(jtagspi_flash_bank_command) +static int jtagspi_init_by_pld_driver(struct jtagspi_flash_bank *info, struct pld_device *device) { - struct jtagspi_flash_bank *info; + info->pld_jtagspi.pld_device = device; + if (!info->pld_jtagspi.pld_device->driver) { + LOG_ERROR("pld device has no associated driver"); + return ERROR_FAIL; + } + + struct pld_driver *pld_driver = info->pld_jtagspi.pld_device->driver; + if (!pld_driver->get_jtagspi_info) { + LOG_ERROR("pld driver does not support jtagspi"); + return ERROR_FAIL; + } + + int retval = pld_driver->get_jtagspi_info(&info->pld_jtagspi); + if (retval != ERROR_OK) + LOG_ERROR("unable to get jtagspi info from pld driver"); + return retval; +} + +FLASH_BANK_COMMAND_HANDLER(jtagspi_flash_bank_command) +{ if (CMD_ARGC < 7) return ERROR_COMMAND_SYNTAX_ERROR; - info = malloc(sizeof(struct jtagspi_flash_bank)); + unsigned int ir = 0; + struct pld_device *device; + bool pld_suport = strcmp(CMD_ARGV[6], "-pld") == 0; + if (pld_suport) { + if (CMD_ARGC < 8) + return ERROR_COMMAND_SYNTAX_ERROR; + device = get_pld_device_by_name_or_numstr(CMD_ARGV[7]); + if (!device) { + 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; @@ -46,20 +83,30 @@ FLASH_BANK_COMMAND_HANDLER(jtagspi_flash_bank_command) return ERROR_FAIL; } info->tap = bank->target->tap; + + info->pld_jtagspi.mode = JTAGSPI_MODE_PROXY_BITSTREAM; + info->pld_jtagspi.ir = ir; + info->pld_jtagspi.pld_device = NULL; + info->pld_jtagspi.connect_spi_to_jtag = NULL; + info->pld_jtagspi.disconnect_spi_from_jtag = NULL; + info->pld_jtagspi.get_facing_read_bits = NULL; + info->pld_jtagspi.get_trailing_write_bits = NULL; + info->probed = false; - COMMAND_PARSE_NUMBER(u32, CMD_ARGV[6], info->ir); + + if (pld_suport) + return jtagspi_init_by_pld_driver(info, 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"); - buf_set_u32(buf, 0, info->tap->ir_length, info->ir); + LOG_DEBUG("loading jtagspi ir(0x%x)", info->pld_jtagspi.ir); + buf_set_u32(buf, 0, info->tap->ir_length, info->pld_jtagspi.ir); field.num_bits = info->tap->ir_length; field.out_value = buf; field.in_value = NULL; @@ -72,6 +119,48 @@ static void flip_u8(const uint8_t *in, uint8_t *out, unsigned int len) out[i] = flip_u32(in[i], 8); } +static int get_stuff_bits(struct jtagspi_flash_bank *info, unsigned int *facing_read_bits, + unsigned int *trailing_write_bits) +{ + if (info->pld_jtagspi.mode == JTAGSPI_MODE_PROXY_BITSTREAM) { + *facing_read_bits = jtag_tap_count_enabled(); + *trailing_write_bits = 0; + } else { + if (info->pld_jtagspi.get_facing_read_bits) { + int retval = info->pld_jtagspi.get_facing_read_bits(info->pld_jtagspi.pld_device, + facing_read_bits); + if (retval != ERROR_OK) + return retval; + } + if (info->pld_jtagspi.get_trailing_write_bits) { + int retval = info->pld_jtagspi.get_trailing_write_bits(info->pld_jtagspi.pld_device, + trailing_write_bits); + if (retval != ERROR_OK) + return retval; + } + } + + return ERROR_OK; +} + +static int connect_spi_to_jtag(struct jtagspi_flash_bank *info) +{ + int retval = ERROR_OK; + if (info->pld_jtagspi.mode == JTAGSPI_MODE_PROXY_BITSTREAM) + jtagspi_set_user_ir(info); + else if (info->pld_jtagspi.connect_spi_to_jtag) + retval = info->pld_jtagspi.connect_spi_to_jtag(info->pld_jtagspi.pld_device); + + return retval; +} + +static int disconnect_spi_from_jtag(struct jtagspi_flash_bank *info) +{ + if (info->pld_jtagspi.mode != JTAGSPI_MODE_PROXY_BITSTREAM && info->pld_jtagspi.disconnect_spi_from_jtag) + return info->pld_jtagspi.disconnect_spi_from_jtag(info->pld_jtagspi.pld_device); + return ERROR_OK; +} + static int jtagspi_cmd(struct flash_bank *bank, uint8_t cmd, uint8_t *write_buffer, unsigned int write_len, uint8_t *data_buffer, int data_len) { @@ -79,6 +168,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 +177,31 @@ 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; + + int retval = get_stuff_bits(info, &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_jtagspi.mode == JTAGSPI_MODE_PROXY_BITSTREAM) { + 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 +219,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 +236,27 @@ 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++; + } + + retval = connect_spi_to_jtag(info); + if (retval != ERROR_OK) + return retval; - 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(); + retval = jtag_execute_queue(); + if (retval != ERROR_OK) + return retval; if (is_read) flip_u8(data_buffer, data_buffer, data_len); - return retval; + + return disconnect_spi_from_jtag(info); } COMMAND_HANDLER(jtagspi_handle_set) diff --git a/src/flash/nor/jtagspi.h b/src/flash/nor/jtagspi.h new file mode 100644 index 0000000000..671e460b4f --- /dev/null +++ b/src/flash/nor/jtagspi.h @@ -0,0 +1,12 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* Copyright (C) 2022 by Daniel Anselmi <danse...@gmx.ch> */ + +#ifndef OPENOCD_JTAG_SPI_H +#define OPENOCD_JTAG_SPI_H + +enum jtagspi_mode_e { + JTAGSPI_MODE_PROXY_BITSTREAM, /* special bitream using the jtag user instruction */ + JTAGSPI_MODE_SPECIFIC_INSTRUCTION, /* jtag tap has specific instruction to access the spi port directly */ +}; /* (without special bitstream) */ + +#endif /* OPENOCD_JTAG_SPI_H */ diff --git a/src/pld/pld.h b/src/pld/pld.h index b736e6ae20..cef747c034 100644 --- a/src/pld/pld.h +++ b/src/pld/pld.h @@ -9,6 +9,7 @@ #define OPENOCD_PLD_PLD_H #include <helper/command.h> +#include <flash/nor/jtagspi.h> struct pld_device; @@ -20,12 +21,23 @@ struct pld_ipdbg_hub { unsigned int user_ir_code; }; +struct pld_jtagspi { + enum jtagspi_mode_e mode; + unsigned int ir; /* convenient for sea of gate mode */ + struct pld_device *pld_device; + int (*connect_spi_to_jtag)(struct pld_device *pld_device); + int (*disconnect_spi_from_jtag)(struct pld_device *pld_device); + int (*get_facing_read_bits)(struct pld_device *pld_device, unsigned int *facing_read_bits); + int (*get_trailing_write_bits)(struct pld_device *pld_device, unsigned int *trailing_write_bits); +}; + 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 (*get_jtagspi_info)(struct pld_jtagspi *pld_jtagspi); }; #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 --