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 <danselmi@gmx.ch>
Reviewed-on: https://review.openocd.org/c/openocd/+/7822
Tested-by: jenkins
Reviewed-by: Antonio Borneo <borneo.antonio@gmail.com>
This commit is contained in:
Daniel Anselmi 2023-02-24 15:57:30 +01:00 committed by Antonio Borneo
parent 30375c6439
commit fe5ed48f40
5 changed files with 245 additions and 47 deletions

View File

@ -5900,24 +5900,42 @@ flash bank $_FLASHNAME cfi 0x00000000 0x02000000 2 4 $_TARGETNAME
@c "cfi part_id" disabled @c "cfi part_id" disabled
@end deffn @end deffn
@anchor{jtagspi}
@deffn {Flash Driver} {jtagspi} @deffn {Flash Driver} {jtagspi}
@cindex Generic JTAG2SPI driver @cindex Generic JTAG2SPI driver
@cindex SPI @cindex SPI
@cindex jtagspi @cindex jtagspi
@cindex bscan_spi @cindex bscan_spi
Several FPGAs and CPLDs can retrieve their configuration (bitstream) from a 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 SPI flash connected to them. To access this flash from the host, some FPGA
is first programmed with a special proxy bitstream that device provides dedicated JTAG instructions, while other FPGA devices should
exposes the SPI flash on the device's JTAG interface. The flash can then be be programmed with a special proxy bitstream that exposes the SPI flash on
accessed through JTAG. 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 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 the flash chip select when the JTAG state machine is in SHIFT-DR.
a bitstream for several Xilinx FPGAs can be found in
Such a bitstream for several Xilinx FPGAs can be found in
@file{contrib/loaders/flash/fpga/xilinx_bscan_spi.py}. It requires @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. @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 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 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 "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. @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 For the bitstreams generated from @file{xilinx_bscan_spi.py} this is the
@var{USER1} instruction. @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 @example
target create $_TARGETNAME testee -chain-position $_CHIPNAME.fpga target create $_TARGETNAME testee -chain-position $_CHIPNAME.tap
set _XILINX_USER1 0x02 set _JTAGSPI_CHAIN_ID $_CHIPNAME.pld
flash bank $_FLASHNAME spi 0x0 0 0 0 \ flash bank $_FLASHNAME jtagspi 0x0 0 0 0 \
$_TARGETNAME $_XILINX_USER1 $_TARGETNAME -pld $_JTAGSPI_CHAIN_ID
@end example @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 @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} 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), 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. 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 They are referenced by the name which was given when the pld was created or
the number shown by the @command{pld devices} command. the number shown by the @command{pld devices} command.

View File

@ -12,6 +12,7 @@
#include <jtag/jtag.h> #include <jtag/jtag.h>
#include <flash/nor/spi.h> #include <flash/nor/spi.h>
#include <helper/time_support.h> #include <helper/time_support.h>
#include <pld/pld.h>
#define JTAGSPI_MAX_TIMEOUT 3000 #define JTAGSPI_MAX_TIMEOUT 3000
@ -22,18 +23,43 @@ struct jtagspi_flash_bank {
char devname[32]; char devname[32];
bool probed; bool probed;
bool always_4byte; /* use always 4-byte address except for basic read 0x03 */ bool always_4byte; /* use always 4-byte address except for basic read 0x03 */
uint32_t ir;
unsigned int addr_len; /* address length in bytes */ 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) FLASH_BANK_COMMAND_HANDLER(jtagspi_flash_bank_command)
{ {
struct jtagspi_flash_bank *info;
if (CMD_ARGC < 7) if (CMD_ARGC < 7)
return ERROR_COMMAND_SYNTAX_ERROR; 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) { if (!info) {
LOG_ERROR("no memory for flash bank info"); LOG_ERROR("no memory for flash bank info");
return ERROR_FAIL; return ERROR_FAIL;
@ -47,18 +73,19 @@ FLASH_BANK_COMMAND_HANDLER(jtagspi_flash_bank_command)
} }
info->tap = bank->target->tap; info->tap = bank->target->tap;
info->probed = false; info->probed = false;
COMMAND_PARSE_NUMBER(u32, CMD_ARGV[6], info->ir);
info->ir = ir;
info->pld_device = device;
return ERROR_OK; 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; struct scan_field field;
uint8_t buf[4] = { 0 }; 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); buf_set_u32(buf, 0, info->tap->ir_length, info->ir);
field.num_bits = info->tap->ir_length; field.num_bits = info->tap->ir_length;
field.out_value = buf; 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); assert(data_buffer || data_len == 0);
struct scan_field fields[6]; 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); LOG_DEBUG("cmd=0x%02x write_len=%d data_len=%d", cmd, write_len, data_len);
@ -87,8 +115,20 @@ static int jtagspi_cmd(struct flash_bank *bank, uint8_t cmd,
if (is_read) if (is_read)
data_len = -data_len; 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; int n = 0;
const uint8_t marker = 1; const uint8_t marker = 1;
uint8_t xfer_bits[4];
if (!info->pld_device) { /* mode == JTAGSPI_MODE_PROXY_BITSTREAM */
facing_read_bits = jtag_tap_count_enabled();
fields[n].num_bits = 1; fields[n].num_bits = 1;
fields[n].out_value = &marker; fields[n].out_value = &marker;
fields[n].in_value = NULL; fields[n].in_value = NULL;
@ -96,13 +136,13 @@ static int jtagspi_cmd(struct flash_bank *bank, uint8_t cmd,
/* transfer length = cmd + address + read/write, /* transfer length = cmd + address + read/write,
* -1 due to the counter implementation */ * -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); h_u32_to_be(xfer_bits, ((sizeof(cmd) + write_len + data_len) * CHAR_BIT) - 1);
flip_u8(xfer_bits, xfer_bits, sizeof(xfer_bits)); flip_u8(xfer_bits, xfer_bits, sizeof(xfer_bits));
fields[n].num_bits = sizeof(xfer_bits) * CHAR_BIT; fields[n].num_bits = sizeof(xfer_bits) * CHAR_BIT;
fields[n].out_value = xfer_bits; fields[n].out_value = xfer_bits;
fields[n].in_value = NULL; fields[n].in_value = NULL;
n++; n++;
}
flip_u8(&cmd, &cmd, sizeof(cmd)); flip_u8(&cmd, &cmd, sizeof(cmd));
fields[n].num_bits = sizeof(cmd) * CHAR_BIT; 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 (data_len > 0) {
if (is_read) { if (is_read) {
fields[n].num_bits = jtag_tap_count_enabled(); if (facing_read_bits) {
fields[n].num_bits = facing_read_bits;
fields[n].out_value = NULL; fields[n].out_value = NULL;
fields[n].in_value = NULL; fields[n].in_value = NULL;
n++; n++;
}
fields[n].out_value = NULL; fields[n].out_value = NULL;
fields[n].in_value = data_buffer; 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; fields[n].num_bits = data_len * CHAR_BIT;
n++; 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 */ /* 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); jtag_add_dr_scan(info->tap, n, fields, TAP_IDLE);
int retval = jtag_execute_queue(); int retval = jtag_execute_queue();
if (retval != ERROR_OK)
return retval;
if (is_read) if (is_read)
flip_u8(data_buffer, data_buffer, data_len); 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) COMMAND_HANDLER(jtagspi_handle_set)

View File

@ -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); 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) COMMAND_HANDLER(handle_pld_create_command)
{ {
if (CMD_ARGC < 2) if (CMD_ARGC < 2)

View File

@ -20,12 +20,26 @@ struct pld_ipdbg_hub {
unsigned int user_ir_code; 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 { struct pld_driver {
const char *name; const char *name;
__PLD_CREATE_COMMAND((*pld_create_command)); __PLD_CREATE_COMMAND((*pld_create_command));
const struct command_registration *commands; const struct command_registration *commands;
int (*load)(struct pld_device *pld_device, const char *filename); 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_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) \ #define PLD_CREATE_COMMAND_HANDLER(name) \

View File

@ -4,6 +4,8 @@ set _USER1 0x02
if { [info exists JTAGSPI_IR] } { if { [info exists JTAGSPI_IR] } {
set _JTAGSPI_IR $JTAGSPI_IR set _JTAGSPI_IR $JTAGSPI_IR
} elseif {[info exists JTAGSPI_CHAIN_ID]} {
set _JTAGSPI_CHAIN_ID $JTAGSPI_CHAIN_ID
} else { } else {
set _JTAGSPI_IR $_USER1 set _JTAGSPI_IR $_USER1
} }
@ -21,7 +23,11 @@ if { [info exists FLASHNAME] } {
} }
target create $_TARGETNAME testee -chain-position $_CHIPNAME.tap target create $_TARGETNAME testee -chain-position $_CHIPNAME.tap
if { [info exists _JTAGSPI_IR] } {
flash bank $_FLASHNAME jtagspi 0 0 0 0 $_TARGETNAME $_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 # initialize jtagspi flash
# chain_id: identifier of pld (you can get a list with 'pld devices') # 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}} { proc jtagspi_init {chain_id proxy_bit {release_from_pwr_down_cmd -1}} {
# load proxy bitstream $proxy_bit and probe spi flash # load proxy bitstream $proxy_bit and probe spi flash
global _FLASHNAME global _FLASHNAME
if { $proxy_bit ne "" } {
pld load $chain_id $proxy_bit pld load $chain_id $proxy_bit
}
reset halt reset halt
if {$release_from_pwr_down_cmd != -1} { if {$release_from_pwr_down_cmd != -1} {
jtagspi cmd $_FLASHNAME 0 $release_from_pwr_down_cmd jtagspi cmd $_FLASHNAME 0 $release_from_pwr_down_cmd