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
@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.

View File

@ -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 = &marker;
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 = &marker;
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)

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);
}
/* @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)

View File

@ -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) \

View File

@ -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