flash/nor/jtagspi: add JTAGSPI driver 44/2844/13
authorRobert Jordens <jordens@gmail.com>
Wed, 1 Jul 2015 09:18:46 +0000 (03:18 -0600)
committerSpencer Oliver <spen@spen-soft.co.uk>
Thu, 6 Aug 2015 12:14:08 +0000 (13:14 +0100)
Many FPGA board speak JTAG and have a SPI flash for their bitstream
attached to them. The SPI flash is programmed by first uploading a
proxy bitstream to the FPGA that connects the JTAG interface to the
SPI interface if the IR contains a certain USER instruction. Then the
SPI flash can be erase, written, read directly through the JTAG DR.

The JTAG and SPI signaling is compatible. Such a proxy bitstream only
needs to connect TDO-MISO, TDI-MOSI, TCK-CLK, and the activate the
chip select when the IR contains the special instruction and the JTAG
state machine is in the DR-SHIFT state.

Change-Id: Ibc21d793a83b36fa37e2704966aa5c837c4dd0d2
Signed-off-by: Robert Jordens <jordens@gmail.com>
Reviewed-on: http://openocd.zylin.com/2844
Tested-by: jenkins
Reviewed-by: Spencer Oliver <spen@spen-soft.co.uk>
contrib/loaders/flash/fpga/xilinx_bscan_spi.py [new file with mode: 0755]
doc/openocd.texi
src/flash/nor/Makefile.am
src/flash/nor/drivers.c
src/flash/nor/jtagspi.c [new file with mode: 0644]
tcl/cpld/jtagspi.cfg [new file with mode: 0644]

diff --git a/contrib/loaders/flash/fpga/xilinx_bscan_spi.py b/contrib/loaders/flash/fpga/xilinx_bscan_spi.py
new file mode 100755 (executable)
index 0000000..a107a6a
--- /dev/null
@@ -0,0 +1,317 @@
+#!/usr/bin/python3
+#
+#  Copyright (C) 2015 Robert Jordens <jordens@gmail.com>
+#
+#  This program is free software; you can redistribute it and/or modify
+#  it under the terms of the GNU General Public License as published by
+#  the Free Software Foundation; either version 2 of the License, or
+#  (at your option) any later version.
+#
+#  This program is distributed in the hope that it will be useful,
+#  but WITHOUT ANY WARRANTY; without even the implied warranty of
+#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#  GNU General Public License for more details.
+#
+
+from migen.fhdl.std import *
+from mibuild.generic_platform import *
+from mibuild.xilinx import XilinxPlatform
+from mibuild.xilinx.vivado import XilinxVivadoToolchain
+from mibuild.xilinx.ise import XilinxISEToolchain
+
+
+"""
+This migen script produces proxy bitstreams to allow programming SPI flashes
+behind FPGAs. JTAG signalling is connected directly to SPI signalling. CS_N is
+asserted when the JTAG IR contains the USER1 instruction and the state is
+SHIFT-DR.
+
+Xilinx bscan cells sample TDO on falling TCK and forward it.
+MISO requires sampling on rising CLK and leads to one cycle of latency.
+
+https://github.com/m-labs/migen
+"""
+
+
+class Spartan3(Module):
+    macro = "BSCAN_SPARTAN3"
+
+    def __init__(self, platform):
+        self.clock_domains.cd_jtag = ClockDomain(reset_less=True)
+        spi = platform.request("spiflash")
+        shift = Signal()
+        tdo = Signal()
+        sel1 = Signal()
+        self.comb += [
+            self.cd_jtag.clk.eq(spi.clk),
+            spi.cs_n.eq(~shift | ~sel1),
+        ]
+        self.sync.jtag += tdo.eq(spi.miso)
+        self.specials += Instance(self.macro,
+                                  o_DRCK1=spi.clk, o_SHIFT=shift,
+                                  o_TDI=spi.mosi, i_TDO1=tdo, i_TDO2=0,
+                                  o_SEL1=sel1)
+
+
+class Spartan3A(Spartan3):
+    macro = "BSCAN_SPARTAN3A"
+
+
+class Spartan6(Module):
+    def __init__(self, platform):
+        self.clock_domains.cd_jtag = ClockDomain(reset_less=True)
+        spi = platform.request("spiflash")
+        shift = Signal()
+        tdo = Signal()
+        sel = Signal()
+        self.comb += self.cd_jtag.clk.eq(spi.clk), spi.cs_n.eq(~shift | ~sel)
+        self.sync.jtag += tdo.eq(spi.miso)
+        self.specials += Instance("BSCAN_SPARTAN6", p_JTAG_CHAIN=1,
+                                  o_TCK=spi.clk, o_SHIFT=shift, o_SEL=sel,
+                                  o_TDI=spi.mosi, i_TDO=tdo)
+
+
+class Series7(Module):
+    def __init__(self, platform):
+        self.clock_domains.cd_jtag = ClockDomain(reset_less=True)
+        spi = platform.request("spiflash")
+        clk = Signal()
+        shift = Signal()
+        tdo = Signal()
+        sel = Signal()
+        self.comb += self.cd_jtag.clk.eq(clk), spi.cs_n.eq(~shift | ~sel)
+        self.sync.jtag += tdo.eq(spi.miso)
+        self.specials += Instance("BSCANE2", p_JTAG_CHAIN=1,
+                                  o_SHIFT=shift, o_TCK=clk, o_SEL=sel,
+                                  o_TDI=spi.mosi, i_TDO=tdo)
+        self.specials += Instance("STARTUPE2", i_CLK=0, i_GSR=0, i_GTS=0,
+                                  i_KEYCLEARB=0, i_PACK=1, i_USRCCLKO=clk,
+                                  i_USRCCLKTS=0, i_USRDONEO=1, i_USRDONETS=1)
+
+
+class XilinxBscanSpi(XilinxPlatform):
+    pinouts = {
+        # bitstreams are named by die, package does not matter, speed grade
+        # should not matter.
+        #                    cs_n, clk, mosi, miso, *pullups
+        "xc3s100e": ("cp132",
+            ["M2", "N12", "N2", "N8"],
+            "LVCMOS33", Spartan3),
+        "xc3s1200e": ("fg320",
+            ["U3", "U16", "T4", "N10"],
+            "LVCMOS33", Spartan3),
+        "xc3s1400a": ("fg484",
+            ["Y4", "AA20", "AB14", "AB20"],
+            "LVCMOS33", Spartan3A),
+        "xc3s1400an": ("fgg484",
+            ["Y4", "AA20", "AB14", "AB20"],
+            "LVCMOS33", Spartan3A),
+        "xc3s1600e": ("fg320",
+            ["U3", "U16", "T4", "N10"],
+            "LVCMOS33", Spartan3),
+        "xc3s200a": ("fg320",
+            ["V3", "U16", "T11", "V16"],
+            "LVCMOS33", Spartan3A),
+        "xc3s200an": ("ftg256",
+            ["T2", "R14", "P10", "T14"],
+            "LVCMOS33", Spartan3A),
+        "xc3s250e": ("cp132",
+            ["M2", "N12", "N2", "N8"],
+            "LVCMOS33", Spartan3),
+        "xc3s400a": ("fg320",
+            ["V3", "U16", "T11", "V16"],
+            "LVCMOS33", Spartan3A),
+        "xc3s400an": ("fgg400",
+            ["Y2", "Y19", "W12", "W18"],
+            "LVCMOS33", Spartan3A),
+        "xc3s500e": ("cp132",
+            ["M2", "N12", "N2", "N8"],
+            "LVCMOS33", Spartan3),
+        "xc3s50a": ("ft256",
+            ["T2", "R14", "P10", "T14"],
+            "LVCMOS33", Spartan3A),
+        "xc3s50an": ("ftg256",
+            ["T2", "R14", "P10", "T14"],
+            "LVCMOS33", Spartan3A),
+        "xc3s700a": ("fg400",
+            ["Y2", "Y19", "W12", "W18"],
+            "LVCMOS33", Spartan3A),
+        "xc3s700an": ("fgg484",
+            ["Y4", "AA20", "AB14", "AB20"],
+            "LVCMOS33", Spartan3A),
+        "xc3sd1800a": ("cs484",
+            ["U7", "V17", "V13", "W17"],
+            "LVCMOS33", Spartan3A),
+        "xc3sd3400a": ("cs484",
+            ["U7", "V17", "V13", "W17"],
+            "LVCMOS33", Spartan3A),
+
+        "xc6slx100": ("csg484-2",
+            ["AB5", "W17", "AB17", "Y17", "V13", "W13"],
+            "LVCMOS33", Spartan6),
+        "xc6slx100t": ("csg484-2",
+            ["AB5", "W17", "AB17", "Y17", "V13", "W13"],
+            "LVCMOS33", Spartan6),
+        "xc6slx150": ("csg484-2",
+            ["AB5", "W17", "AB17", "Y17", "V13", "W13"],
+            "LVCMOS33", Spartan6),
+        "xc6slx150t": ("csg484-2",
+            ["AB5", "W17", "AB17", "Y17", "V13", "W13"],
+            "LVCMOS33", Spartan6),
+        "xc6slx16": ("cpg196-2",
+            ["P2", "N13", "P11", "N11", "N10", "P10"],
+            "LVCMOS33", Spartan6),
+        "xc6slx25": ("csg324-2",
+            ["V3", "R15", "T13", "R13", "T14", "V14"],
+            "LVCMOS33", Spartan6),
+        "xc6slx25t": ("csg324-2",
+            ["V3", "R15", "T13", "R13", "T14", "V14"],
+            "LVCMOS33", Spartan6),
+        "xc6slx45": ("csg324-2",
+            ["V3", "R15", "T13", "R13", "T14", "V14"],
+            "LVCMOS33", Spartan6),
+        "xc6slx45t": ("csg324-2",
+            ["V3", "R15", "T13", "R13", "T14", "V14"],
+            "LVCMOS33", Spartan6),
+        "xc6slx4": ("cpg196-2",
+            ["P2", "N13", "P11", "N11", "N10", "P10"],
+            "LVCMOS33", Spartan6),
+        "xc6slx4t": ("qg144-2",
+            ["P38", "P70", "P64", "P65", "P62", "P61"],
+            "LVCMOS33", Spartan6),
+        "xc6slx75": ("csg484-2",
+            ["AB5", "W17", "AB17", "Y17", "V13", "W13"],
+            "LVCMOS33", Spartan6),
+        "xc6slx75t": ("csg484-2",
+            ["AB5", "W17", "AB17", "Y17", "V13", "W13"],
+            "LVCMOS33", Spartan6),
+        "xc6slx9": ("cpg196-2",
+            ["P2", "N13", "P11", "N11", "N10", "P10"],
+            "LVCMOS33", Spartan6),
+        "xc6slx9t": ("qg144-2",
+            ["P38", "P70", "P64", "P65", "P62", "P61"],
+            "LVCMOS33", Spartan6),
+
+        "xc7a100t": ("csg324-1",
+            ["L13", None, "K17", "K18", "L14", "M14"],
+            "LVCMOS25", Series7),
+        "xc7a15t": ("cpg236-1",
+            ["K19", None, "D18", "D19", "G18", "F18"],
+            "LVCMOS25", Series7),
+        "xc7a200t": ("fbg484-1",
+            ["T19", None, "P22", "R22", "P21", "R21"],
+            "LVCMOS25", Series7),
+        "xc7a35t": ("cpg236-1",
+            ["K19", None, "D18", "D19", "G18", "F18"],
+            "LVCMOS25", Series7),
+        "xc7a50t": ("cpg236-1",
+            ["K19", None, "D18", "D19", "G18", "F18"],
+            "LVCMOS25", Series7),
+        "xc7a75t": ("csg324-1",
+            ["L13", None, "K17", "K18", "L14", "M14"],
+            "LVCMOS25", Series7),
+        "xc7k160t": ("fbg484-1",
+            ["L16", None, "H18", "H19", "G18", "F19"],
+            "LVCMOS25", Series7),
+        "xc7k325t": ("fbg676-1",
+            ["C23", None, "B24", "A25", "B22", "A22"],
+            "LVCMOS25", Series7),
+        "xc7k355t": ("ffg901-1",
+            ["V26", None, "R30", "T30", "R28", "T28"],
+            "LVCMOS25", Series7),
+        "xc7k410t": ("fbg676-1",
+            ["C23", None, "B24", "A25", "B22", "A22"],
+            "LVCMOS25", Series7),
+        "xc7k420t": ("ffg1156-1",
+            ["V30", None, "AA33", "AA34", "Y33", "Y34"],
+            "LVCMOS25", Series7),
+        "xc7k480t": ("ffg1156-1",
+            ["V30", None, "AA33", "AA34", "Y33", "Y34"],
+            "LVCMOS25", Series7),
+        "xc7k70t": ("fbg484-1",
+            ["L16", None, "H18", "H19", "G18", "F19"],
+            "LVCMOS25", Series7),
+        "xc7v2000t": ("fhg1761-1",
+            ["AL36", None, "AM36", "AN36", "AJ36", "AJ37"],
+            "LVCMOS18", Series7),
+        "xc7v585t": ("ffg1157-1",
+            ["AL33", None, "AN33", "AN34", "AK34", "AL34"],
+            "LVCMOS18", Series7),
+        "xc7vh580t": ("flg1155-1",
+            ["AL28", None, "AE28", "AF28", "AJ29", "AJ30"],
+            "LVCMOS18", Series7),
+        "xc7vh870t": ("flg1932-1",
+            ["V32", None, "T33", "R33", "U31", "T31"],
+            "LVCMOS18", Series7),
+        "xc7vx1140t": ("flg1926-1",
+            ["AK33", None, "AN34", "AN35", "AJ34", "AK34"],
+            "LVCMOS18", Series7),
+        "xc7vx330t": ("ffg1157-1",
+            ["AL33", None, "AN33", "AN34", "AK34", "AL34"],
+            "LVCMOS18", Series7),
+        "xc7vx415t": ("ffg1157-1",
+            ["AL33", None, "AN33", "AN34", "AK34", "AL34"],
+            "LVCMOS18", Series7),
+        "xc7vx485t": ("ffg1157-1",
+            ["AL33", None, "AN33", "AN34", "AK34", "AL34"],
+            "LVCMOS18", Series7),
+        "xc7vx550t": ("ffg1158-1",
+            ["C24", None, "A23", "A24", "B26", "A26"],
+            "LVCMOS18", Series7),
+        "xc7vx690t": ("ffg1157-1",
+            ["AL33", None, "AN33", "AN34", "AK34", "AL34"],
+            "LVCMOS18", Series7),
+        "xc7vx980t": ("ffg1926-1",
+            ["AK33", None, "AN34", "AN35", "AJ34", "AK34"],
+            "LVCMOS18", Series7),
+    }
+
+    def __init__(self, device, pins, std):
+        cs_n, clk, mosi, miso = pins[:4]
+        io = ["spiflash", 0,
+              Subsignal("cs_n", Pins(cs_n)),
+              Subsignal("mosi", Pins(mosi)),
+              Subsignal("miso", Pins(miso), Misc("PULLUP")),
+              IOStandard(std),
+              ]
+        if clk:
+            io.append(Subsignal("clk", Pins(clk)))
+        for i, p in enumerate(pins[4:]):
+            io.append(Subsignal("pullup{}".format(i), Pins(p), Misc("PULLUP")))
+
+        XilinxPlatform.__init__(self, device, [io])
+        if isinstance(self.toolchain, XilinxVivadoToolchain):
+            self.toolchain.bitstream_commands.append(
+                "set_property BITSTREAM.GENERAL.COMPRESS True [current_design]"
+            )
+        elif isinstance(self.toolchain, XilinxISEToolchain):
+            self.toolchain.bitgen_opt += " -g compress"
+
+    @classmethod
+    def make(cls, device, errors=False):
+        pkg, pins, std, Top = cls.pinouts[device]
+        platform = cls("{}-{}".format(device, pkg), pins, std)
+        top = Top(platform)
+        name = "bscan_spi_{}".format(device)
+        dir = "build_{}".format(device)
+        try:
+            platform.build(top, build_name=name, build_dir=dir)
+        except Exception as e:
+            print("ERROR: build failed for {}: {}".format(device, e))
+            if errors:
+                raise
+
+
+if __name__ == "__main__":
+    import argparse
+    import multiprocessing
+    p = argparse.ArgumentParser(description="build bscan_spi bitstreams "
+                                "for openocd jtagspi flash driver")
+    p.add_argument("device", nargs="*",
+                   default=sorted(list(XilinxBscanSpi.pinouts)),
+                   help="build for these devices (default: %(default)s)")
+    p.add_argument("-p", "--parallel", default=1, type=int,
+                   help="number of parallel builds (default: %(default)s)")
+    args = p.parse_args()
+    pool = multiprocessing.Pool(args.parallel)
+    pool.map(XilinxBscanSpi.make, args.device, chunksize=1)
index 4430050eece3e94a6ab479b3e033cda53e3324a2..63ab5deb5dc29bc00ea612adcdc1e97387891295 100644 (file)
@@ -4802,6 +4802,49 @@ flash bank $_FLASHNAME cfi 0x00000000 0x02000000 2 4 $_TARGETNAME
 @c "cfi part_id" disabled
 @end deffn
 
+@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.
+
+Since signaling 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
+@file{contrib/loaders/flash/fpga/xilinx_bscan_spi.py}. It requires migen
+(@url{http://github.com/m-labs/migen}) and a Xilinx toolchain to build.
+
+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
+mapping, target commands that would otherwise be expected to access the flash
+will not work. These include all @command{*_image} and
+@command{$target_name m*} commands as well as @command{program}. Equivalent
+functionality is available through the @command{flash write_bank},
+@command{flash read_bank}, and @command{flash verify_bank} commands.
+
+@itemize
+@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.
+@item @var{dr_length} ... is the length of the DR register. This will be 1 for
+@file{xilinx_bscan_spi.py} bitstreams and most other cases.
+@end itemize
+
+@example
+target create $_TARGETNAME testee -chain-position $_CHIPNAME.fpga
+set _XILINX_USER1 0x02
+set _DR_LENGTH 1
+flash bank $_FLASHNAME spi 0x0 0 0 0 $_TARGETNAME $_XILINX_USER1 $_DR_LENGTH
+@end example
+@end deffn
+
 @deffn {Flash Driver} lpcspifi
 @cindex NXP SPI Flash Interface
 @cindex SPIFI
index 878fc26ba7a392476aeb049b209b8cc8b2d198b2..19ae90ee26330e9252840759d83e18222978fa01 100644 (file)
@@ -19,6 +19,7 @@ NOR_DRIVERS = \
        efm32.c \
        em357.c \
        faux.c \
+       jtagspi.c \
        lpc2000.c \
        lpc288x.c \
        lpc2900.c \
index fead7974fb3d205778f82e49603403631f0df51a..6ae0859dd41f1ee687306a3f6f9da387ced34d23 100644 (file)
@@ -59,6 +59,7 @@ extern struct flash_driver nrf51_flash;
 extern struct flash_driver mrvlqspi_flash;
 extern struct flash_driver psoc4_flash;
 extern struct flash_driver sim3x_flash;
+extern struct flash_driver jtagspi_flash;
 
 /**
  * The list of built-in flash drivers.
@@ -102,6 +103,7 @@ static struct flash_driver *flash_drivers[] = {
        &mrvlqspi_flash,
        &psoc4_flash,
        &sim3x_flash,
+       &jtagspi_flash,
        NULL,
 };
 
diff --git a/src/flash/nor/jtagspi.c b/src/flash/nor/jtagspi.c
new file mode 100644 (file)
index 0000000..6ca5c3c
--- /dev/null
@@ -0,0 +1,411 @@
+/***************************************************************************
+ *   Copyright (C) 2015 Robert Jordens <jordens@gmail.com>                 *
+ *                                                                         *
+ *   This program is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the GNU General Public License as published by  *
+ *   the Free Software Foundation; either version 2 of the License, or     *
+ *   (at your option) any later version.                                   *
+ *                                                                         *
+ *   This program is distributed in the hope that it will be useful,       *
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
+ *   GNU General Public License for more details.                          *
+ *                                                                         *
+ ***************************************************************************/
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "imp.h"
+#include <jtag/jtag.h>
+#include <flash/nor/spi.h>
+#include <helper/time_support.h>
+
+#define JTAGSPI_MAX_TIMEOUT 3000
+
+
+struct jtagspi_flash_bank {
+       struct jtag_tap *tap;
+       const struct flash_device *dev;
+       int probed;
+       uint32_t ir;
+       uint32_t dr_len;
+};
+
+FLASH_BANK_COMMAND_HANDLER(jtagspi_flash_bank_command)
+{
+       struct jtagspi_flash_bank *info;
+
+       if (CMD_ARGC < 8)
+               return ERROR_COMMAND_SYNTAX_ERROR;
+
+       info = malloc(sizeof(struct jtagspi_flash_bank));
+       if (info == NULL) {
+               LOG_ERROR("no memory for flash bank info");
+               return ERROR_FAIL;
+       }
+       bank->driver_priv = info;
+
+       info->tap = NULL;
+       info->probed = 0;
+       COMMAND_PARSE_NUMBER(u32, CMD_ARGV[6], info->ir);
+       COMMAND_PARSE_NUMBER(u32, CMD_ARGV[7], info->dr_len);
+
+       return ERROR_OK;
+}
+
+static void jtagspi_set_ir(struct flash_bank *bank)
+{
+       struct jtagspi_flash_bank *info = bank->driver_priv;
+       struct scan_field field;
+       uint8_t buf[4];
+
+       if (buf_get_u32(info->tap->cur_instr, 0, info->tap->ir_length) == info->ir)
+               return;
+
+       LOG_DEBUG("loading jtagspi ir");
+       buf_set_u32(buf, 0, info->tap->ir_length, info->ir);
+       field.num_bits = info->tap->ir_length;
+       field.out_value = buf;
+       field.in_value = NULL;
+       jtag_add_ir_scan(info->tap, &field, TAP_IDLE);
+}
+
+static void flip_u8(uint8_t *in, uint8_t *out, int len)
+{
+       for (int i = 0; i < len; i++)
+               out[i] = flip_u32(in[i], 8);
+}
+
+static int jtagspi_cmd(struct flash_bank *bank, uint8_t cmd,
+               uint32_t *addr, uint8_t *data, int len)
+{
+       struct jtagspi_flash_bank *info = bank->driver_priv;
+       struct scan_field fields[3];
+       uint8_t cmd_buf[4];
+       uint8_t *data_buf;
+       int is_read, lenb, n;
+
+       /* LOG_DEBUG("cmd=0x%02x len=%i", cmd, len); */
+
+       n = 0;
+       fields[n].num_bits = 8;
+       cmd_buf[0] = cmd;
+       if (addr) {
+               h_u24_to_be(cmd_buf + 1, *addr);
+               fields[n].num_bits += 24;
+       }
+       flip_u8(cmd_buf, cmd_buf, 4);
+       fields[n].out_value = cmd_buf;
+       fields[n].in_value = NULL;
+       n++;
+
+       is_read = (len < 0);
+       if (is_read)
+               len = -len;
+       lenb = DIV_ROUND_UP(len, 8);
+       data_buf = malloc(lenb);
+       if (lenb > 0) {
+               if (data_buf == NULL) {
+                       LOG_ERROR("no memory for spi buffer");
+                       return ERROR_FAIL;
+               }
+               if (is_read) {
+                       fields[n].num_bits = info->dr_len;
+                       fields[n].out_value = NULL;
+                       fields[n].in_value = NULL;
+                       n++;
+                       fields[n].out_value = NULL;
+                       fields[n].in_value = data_buf;
+               } else {
+                       flip_u8(data, data_buf, lenb);
+                       fields[n].out_value = data_buf;
+                       fields[n].in_value = NULL;
+               }
+               fields[n].num_bits = len;
+               n++;
+       }
+
+       jtagspi_set_ir(bank);
+       jtag_add_dr_scan(info->tap, n, fields, TAP_IDLE);
+       jtag_execute_queue();
+
+       if (is_read)
+               flip_u8(data_buf, data, lenb);
+       free(data_buf);
+       return ERROR_OK;
+}
+
+static int jtagspi_probe(struct flash_bank *bank)
+{
+       struct jtagspi_flash_bank *info = bank->driver_priv;
+       struct flash_sector *sectors;
+       uint8_t in_buf[3];
+       uint32_t id;
+
+       if (info->probed)
+               free(bank->sectors);
+       info->probed = 0;
+
+       if (bank->target->tap == NULL) {
+               LOG_ERROR("Target has no JTAG tap");
+               return ERROR_FAIL;
+       }
+       info->tap = bank->target->tap;
+
+       jtagspi_cmd(bank, SPIFLASH_READ_ID, NULL, in_buf, -24);
+       /* the table in spi.c has the manufacturer byte (first) as the lsb */
+       id = le_to_h_u24(in_buf);
+
+       info->dev = NULL;
+       for (const struct flash_device *p = flash_devices; p->name ; p++)
+               if (p->device_id == id) {
+                       info->dev = p;
+                       break;
+               }
+
+       if (!(info->dev)) {
+               LOG_ERROR("Unknown flash device (ID 0x%08" PRIx32 ")", id);
+               return ERROR_FAIL;
+       }
+
+       LOG_INFO("Found flash device \'%s\' (ID 0x%08" PRIx32 ")",
+               info->dev->name, info->dev->device_id);
+
+       /* Set correct size value */
+       bank->size = info->dev->size_in_bytes;
+
+       /* create and fill sectors array */
+       bank->num_sectors =
+               info->dev->size_in_bytes / info->dev->sectorsize;
+       sectors = malloc(sizeof(struct flash_sector) * bank->num_sectors);
+       if (sectors == NULL) {
+               LOG_ERROR("not enough memory");
+               return ERROR_FAIL;
+       }
+
+       for (int sector = 0; sector < bank->num_sectors; sector++) {
+               sectors[sector].offset = sector * info->dev->sectorsize;
+               sectors[sector].size = info->dev->sectorsize;
+               sectors[sector].is_erased = -1;
+               sectors[sector].is_protected = 0;
+       }
+
+       bank->sectors = sectors;
+       info->probed = 1;
+       return ERROR_OK;
+}
+
+static void jtagspi_read_status(struct flash_bank *bank, uint32_t *status)
+{
+       uint8_t buf;
+       jtagspi_cmd(bank, SPIFLASH_READ_STATUS, NULL, &buf, -8);
+       *status = buf;
+       /* LOG_DEBUG("status=0x%08" PRIx32, *status); */
+}
+
+static int jtagspi_wait(struct flash_bank *bank, int timeout_ms)
+{
+       uint32_t status;
+       long long t0 = timeval_ms();
+       long long dt;
+
+       do {
+               dt = timeval_ms() - t0;
+               jtagspi_read_status(bank, &status);
+               if ((status & SPIFLASH_BSY_BIT) == 0) {
+                       LOG_DEBUG("waited %lld ms", dt);
+                       return ERROR_OK;
+               }
+               alive_sleep(1);
+       } while (dt <= timeout_ms);
+
+       LOG_ERROR("timeout, device still busy");
+       return ERROR_FAIL;
+}
+
+static int jtagspi_write_enable(struct flash_bank *bank)
+{
+       uint32_t status;
+
+       jtagspi_cmd(bank, SPIFLASH_WRITE_ENABLE, NULL, NULL, 0);
+       jtagspi_read_status(bank, &status);
+       if ((status & SPIFLASH_WE_BIT) == 0) {
+               LOG_ERROR("Cannot enable write to flash. Status=0x%08" PRIx32, status);
+               return ERROR_FAIL;
+       }
+       return ERROR_OK;
+}
+
+static int jtagspi_bulk_erase(struct flash_bank *bank)
+{
+       struct jtagspi_flash_bank *info = bank->driver_priv;
+       int retval;
+       long long t0 = timeval_ms();
+
+       retval = jtagspi_write_enable(bank);
+       if (retval != ERROR_OK)
+               return retval;
+       jtagspi_cmd(bank, info->dev->chip_erase_cmd, NULL, NULL, 0);
+       retval = jtagspi_wait(bank, bank->num_sectors*JTAGSPI_MAX_TIMEOUT);
+       LOG_INFO("took %lld ms", timeval_ms() - t0);
+       return retval;
+}
+
+static int jtagspi_sector_erase(struct flash_bank *bank, int sector)
+{
+       struct jtagspi_flash_bank *info = bank->driver_priv;
+       int retval;
+       long long t0 = timeval_ms();
+
+       retval = jtagspi_write_enable(bank);
+       if (retval != ERROR_OK)
+               return retval;
+       jtagspi_cmd(bank, info->dev->erase_cmd, &bank->sectors[sector].offset, NULL, 0);
+       retval = jtagspi_wait(bank, JTAGSPI_MAX_TIMEOUT);
+       LOG_INFO("sector %d took %lld ms", sector, timeval_ms() - t0);
+       return retval;
+}
+
+static int jtagspi_erase(struct flash_bank *bank, int first, int last)
+{
+       int sector;
+       struct jtagspi_flash_bank *info = bank->driver_priv;
+       int retval;
+
+       LOG_DEBUG("erase from sector %d to sector %d", first, last);
+
+       if ((first < 0) || (last < first) || (last >= bank->num_sectors)) {
+               LOG_ERROR("Flash sector invalid");
+               return ERROR_FLASH_SECTOR_INVALID;
+       }
+
+       if (!(info->probed)) {
+               LOG_ERROR("Flash bank not probed");
+               return ERROR_FLASH_BANK_NOT_PROBED;
+       }
+
+       for (sector = first; sector <= last; sector++) {
+               if (bank->sectors[sector].is_protected) {
+                       LOG_ERROR("Flash sector %d protected", sector);
+                       return ERROR_FAIL;
+               }
+       }
+
+       if (first == 0 && last == (bank->num_sectors - 1)
+               && info->dev->chip_erase_cmd != info->dev->erase_cmd) {
+               LOG_DEBUG("Trying bulk erase.");
+               retval = jtagspi_bulk_erase(bank);
+               if (retval == ERROR_OK)
+                       return retval;
+               else
+                       LOG_WARNING("Bulk flash erase failed. Falling back to sector erase.");
+       }
+
+       for (sector = first; sector <= last; sector++) {
+               retval = jtagspi_sector_erase(bank, sector);
+               if (retval != ERROR_OK) {
+                       LOG_ERROR("Sector erase failed.");
+                       break;
+               }
+       }
+
+       return retval;
+}
+
+static int jtagspi_protect(struct flash_bank *bank, int set, int first, int last)
+{
+       int sector;
+
+       if ((first < 0) || (last < first) || (last >= bank->num_sectors)) {
+               LOG_ERROR("Flash sector invalid");
+               return ERROR_FLASH_SECTOR_INVALID;
+       }
+
+       for (sector = first; sector <= last; sector++)
+               bank->sectors[sector].is_protected = set;
+       return ERROR_OK;
+}
+
+static int jtagspi_protect_check(struct flash_bank *bank)
+{
+       return ERROR_OK;
+}
+
+static int jtagspi_read(struct flash_bank *bank, uint8_t *buffer, uint32_t offset, uint32_t count)
+{
+       struct jtagspi_flash_bank *info = bank->driver_priv;
+
+       if (!(info->probed)) {
+               LOG_ERROR("Flash bank not yet probed.");
+               return ERROR_FLASH_BANK_NOT_PROBED;
+       }
+
+       jtagspi_cmd(bank, SPIFLASH_READ, &offset, buffer, -count*8);
+       return ERROR_OK;
+}
+
+static int jtagspi_page_write(struct flash_bank *bank, const uint8_t *buffer, uint32_t offset, uint32_t count)
+{
+       int retval;
+
+       retval = jtagspi_write_enable(bank);
+       if (retval != ERROR_OK)
+               return retval;
+       jtagspi_cmd(bank, SPIFLASH_PAGE_PROGRAM, &offset, (uint8_t *) buffer, count*8);
+       return jtagspi_wait(bank, JTAGSPI_MAX_TIMEOUT);
+}
+
+static int jtagspi_write(struct flash_bank *bank, const uint8_t *buffer, uint32_t offset, uint32_t count)
+{
+       struct jtagspi_flash_bank *info = bank->driver_priv;
+       int retval;
+       uint32_t n;
+
+       if (!(info->probed)) {
+               LOG_ERROR("Flash bank not yet probed.");
+               return ERROR_FLASH_BANK_NOT_PROBED;
+       }
+
+       for (n = 0; n < count; n += info->dev->pagesize) {
+               retval = jtagspi_page_write(bank, buffer + n, offset + n,
+                               MIN(count - n, info->dev->pagesize));
+               if (retval != ERROR_OK) {
+                       LOG_ERROR("page write error");
+                       return retval;
+               }
+               LOG_DEBUG("wrote page at 0x%08" PRIx32, offset + n);
+       }
+       return ERROR_OK;
+}
+
+static int jtagspi_info(struct flash_bank *bank, char *buf, int buf_size)
+{
+       struct jtagspi_flash_bank *info = bank->driver_priv;
+
+       if (!(info->probed)) {
+               snprintf(buf, buf_size, "\nJTAGSPI flash bank not probed yet\n");
+               return ERROR_OK;
+       }
+
+       snprintf(buf, buf_size, "\nSPIFI flash information:\n"
+               "  Device \'%s\' (ID 0x%08" PRIx32 ")\n",
+               info->dev->name, info->dev->device_id);
+
+       return ERROR_OK;
+}
+
+struct flash_driver jtagspi_flash = {
+       .name = "jtagspi",
+       .flash_bank_command = jtagspi_flash_bank_command,
+       .erase = jtagspi_erase,
+       .protect = jtagspi_protect,
+       .write = jtagspi_write,
+       .read = jtagspi_read,
+       .probe = jtagspi_probe,
+       .auto_probe = jtagspi_probe,
+       .erase_check = default_flash_blank_check,
+       .protect_check = jtagspi_protect_check,
+       .info = jtagspi_info
+};
diff --git a/tcl/cpld/jtagspi.cfg b/tcl/cpld/jtagspi.cfg
new file mode 100644 (file)
index 0000000..60c3cb1
--- /dev/null
@@ -0,0 +1,43 @@
+set _USER1 0x02
+
+if { [info exists JTAGSPI_IR] } {
+       set _JTAGSPI_IR $JTAGSPI_IR
+} else {
+       set _JTAGSPI_IR $_USER1
+}
+
+if { [info exists DR_LENGTH] } {
+       set _DR_LENGTH $DR_LENGTH
+} else {
+       set _DR_LENGTH 1
+}
+
+if { [info exists TARGETNAME] } {
+       set _TARGETNAME $TARGETNAME
+} else {
+       set _TARGETNAME $_CHIPNAME.proxy
+}
+
+if { [info exists FLASHNAME] } {
+       set _FLASHNAME $FLASHNAME
+} else {
+       set _FLASHNAME $_CHIPNAME.spi
+}
+
+target create $_TARGETNAME testee -chain-position $_CHIPNAME.tap
+flash bank $_FLASHNAME jtagspi 0 0 0 0 $_TARGETNAME $_JTAGSPI_IR $_DR_LENGTH
+
+proc jtagspi_init {chain_id proxy_bit} {
+       # load proxy bitstream $proxy_bit and probe spi flash
+       global _FLASHNAME
+       pld load $chain_id $proxy_bit
+       reset halt
+       flash probe $_FLASHNAME
+}
+
+proc jtagspi_program {bin addr} {
+       # write and verify binary file $bin at offset $addr
+       global _FLASHNAME
+       flash write_image erase $bin $addr
+       flash verify_bank $_FLASHNAME $bin $addr
+}

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)