jtagspi/pld: add interface to get support from pld drivers 22/7822/14
authorDaniel Anselmi <danselmi@gmx.ch>
Fri, 24 Feb 2023 14:57:30 +0000 (15:57 +0100)
committerAntonio Borneo <borneo.antonio@gmail.com>
Sat, 23 Sep 2023 14:33:37 +0000 (14:33 +0000)
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>
doc/openocd.texi
src/flash/nor/jtagspi.c
src/pld/pld.c
src/pld/pld.h
tcl/cpld/jtagspi.cfg

index f6f7a0c2937ddafe93046074b6e9abc3b3f9f0e1..7ad48c8629e646e59ee65febd8411a55c742cd16 100644 (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.
index 6bb3af9b7df1a89ff4d60a4cbb0649631f7dc2a2..4b975390be1c974d6885d9a9e8b60418cfe920ad 100644 (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)
index c375418a9d040833a7adf5ee2ffc826db5bb8dfb..81fb0c46321708673864ae591df9822b968c1943 100644 (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)
index b736e6ae200085b8fbadfca25815e3142cc4c95a..5e2fcd20cc5d500621d0d7fafb4620fb92f388b9 100644 (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) \
index 4c84792fe11bdd68cedb2ac54ffc0840b37845e1..a7f02b977010293cb7aac436af8876a486bb35a2 100644 (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

Linking to existing account procedure

If you already have an account and want to add another login method you MUST first sign in with your existing account and then change URL to read https://review.openocd.org/login/?link to get to this page again but this time it'll work for linking. Thank you.

SSH host keys fingerprints

1024 SHA256:YKx8b7u5ZWdcbp7/4AeXNaqElP49m6QrwfXaqQGJAOk gerrit-code-review@openocd.zylin.com (DSA)
384 SHA256:jHIbSQa4REvwCFG4cq5LBlBLxmxSqelQPem/EXIrxjk gerrit-code-review@openocd.org (ECDSA)
521 SHA256:UAOPYkU9Fjtcao0Ul/Rrlnj/OsQvt+pgdYSZ4jOYdgs gerrit-code-review@openocd.org (ECDSA)
256 SHA256:A13M5QlnozFOvTllybRZH6vm7iSt0XLxbA48yfc2yfY gerrit-code-review@openocd.org (ECDSA)
256 SHA256:spYMBqEYoAOtK7yZBrcwE8ZpYt6b68Cfh9yEVetvbXg gerrit-code-review@openocd.org (ED25519)
+--[ED25519 256]--+
|=..              |
|+o..   .         |
|*.o   . .        |
|+B . . .         |
|Bo. = o S        |
|Oo.+ + =         |
|oB=.* = . o      |
| =+=.+   + E     |
|. .=o   . o      |
+----[SHA256]-----+
2048 SHA256:0Onrb7/PHjpo6iVZ7xQX2riKN83FJ3KGU0TvI0TaFG4 gerrit-code-review@openocd.zylin.com (RSA)