Skip to content

Commit

Permalink
jtagspi/pld: add interface to get support from pld drivers
Browse files Browse the repository at this point in the history
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 <[email protected]>
Reviewed-on: https://review.openocd.org/c/openocd/+/7822
Tested-by: jenkins
Reviewed-by: Antonio Borneo <[email protected]>
  • Loading branch information
danselmi authored and borneoa committed Sep 23, 2023
1 parent 30375c6 commit fe5ed48
Show file tree
Hide file tree
Showing 5 changed files with 245 additions and 47 deletions.
56 changes: 43 additions & 13 deletions doc/openocd.texi
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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}
Expand Down Expand Up @@ -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.
Expand Down
119 changes: 89 additions & 30 deletions src/flash/nor/jtagspi.c
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand All @@ -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;
Expand All @@ -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;
Expand All @@ -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);

Expand All @@ -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;
Expand All @@ -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;
Expand All @@ -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)
Expand Down
91 changes: 89 additions & 2 deletions src/pld/pld.c
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
Loading

0 comments on commit fe5ed48

Please sign in to comment.