|
From: openocd-gerrit <ope...@us...> - 2023-08-12 16:44:52
|
This is an automated email from the git hooks/post-receive script. It was
generated because a ref change was pushed to the repository containing
the project "Main OpenOCD repository".
The branch, master has been updated
via 94686eea6e982a83e5c1796e8a903bf683ed62e5 (commit)
from 9c91ce8d24d0789a0f25affb73201c9e0a6b89d8 (commit)
Those revisions listed above that are new to this repository have
not appeared on any other notification email; so we list those
revisions in full, below.
- Log -----------------------------------------------------------------
commit 94686eea6e982a83e5c1796e8a903bf683ed62e5
Author: Ahmed BOUDJELIDA <abo...@na...>
Date: Sat Jun 17 01:11:15 2023 +0200
jtag/drivers: Add new driver for ANGIE USB-JTAG Adapter
This is the driver code for NanoXplore's ANGIE USB-JTAG Adapter.
The driver is based on the openULINK project.
This driver communicate with ANGIE's firmware in order to establish
JTAG protocol to debug the target chip.
Since the ANGIE Adapter has a Spartan-6 FPGA in addition to the
FX2 microcontroller, the driver adds two functions, one to download
the firmware (embedded C) to the FX2, and the second to program
the FPGA with its bitstream.
Add ANGIE's configuration file to tcl/interface/
Add the device VID/PID to 60-openocd.rules file.
Add ANGIE to OpenOCD's documentation
Change-Id: Id17111c74073da01450d43d466e11b0cc086691f
Signed-off-by: Ahmed BOUDJELIDA <abo...@na...>
Reviewed-on: https://review.openocd.org/c/openocd/+/7702
Reviewed-by: Antonio Borneo <bor...@gm...>
Tested-by: jenkins
diff --git a/configure.ac b/configure.ac
index ac2808e1f..ecf8384bf 100644
--- a/configure.ac
+++ b/configure.ac
@@ -118,6 +118,7 @@ m4_define([USB1_ADAPTERS],
[[stlink], [ST-Link Programmer], [HLADAPTER_STLINK]],
[[ti_icdi], [TI ICDI JTAG Programmer], [HLADAPTER_ICDI]],
[[ulink], [Keil ULINK JTAG Programmer], [ULINK]],
+ [[angie], [ANGIE Adapter], [ANGIE]],
[[usb_blaster_2], [Altera USB-Blaster II Compatible], [USB_BLASTER_2]],
[[ft232r], [Bitbang mode of FT232R based devices], [FT232R]],
[[vsllink], [Versaloon-Link JTAG Programmer], [VSLLINK]],
diff --git a/contrib/60-openocd.rules b/contrib/60-openocd.rules
index fc35fb9b9..fd88564bd 100644
--- a/contrib/60-openocd.rules
+++ b/contrib/60-openocd.rules
@@ -224,6 +224,10 @@ ATTRS{idVendor}=="2aec", ATTRS{idProduct}=="1106", MODE="660", GROUP="plugdev",
ATTRS{idVendor}=="303a", ATTRS{idProduct}=="1001", MODE="660", GROUP="plugdev", TAG+="uaccess"
ATTRS{idVendor}=="303a", ATTRS{idProduct}=="1002", MODE="660", GROUP="plugdev", TAG+="uaccess"
+# ANGIE USB-JTAG Adapter
+ATTRS{idVendor}=="584e", ATTRS{idProduct}=="424e", MODE="660", GROUP="plugdev", TAG+="uaccess"
+ATTRS{idVendor}=="584e", ATTRS{idProduct}=="4a55", MODE="660", GROUP="plugdev", TAG+="uaccess"
+
# Marvell Sheevaplug
ATTRS{idVendor}=="9e88", ATTRS{idProduct}=="9e8f", MODE="660", GROUP="plugdev", TAG+="uaccess"
diff --git a/doc/openocd.texi b/doc/openocd.texi
index 03c5190ad..3348e472b 100644
--- a/doc/openocd.texi
+++ b/doc/openocd.texi
@@ -508,6 +508,9 @@ debuggers to ARM Cortex based targets @url{http://www.keil.com/support/man/docs/
@item @b{ARM-JTAG-EW}
@* Link: @url{http://www.olimex.com/dev/arm-jtag-ew.html}
+@item @b{angie}
+@* Link: @url{https://nanoxplore.org/}
+
@item @b{Buspirate}
@* Link: @url{http://dangerousprototypes.com/bus-pirate-manual/}
@@ -2515,6 +2518,10 @@ Optionally sets that option first.
@end deffn
@end deffn
+@deffn {Interface Driver} {angie}
+This is the NanoXplore's ANGIE USB-JTAG Adapter.
+@end deffn
+
@deffn {Interface Driver} {arm-jtag-ew}
Olimex ARM-JTAG-EW USB adapter
This has one driver-specific command:
diff --git a/src/jtag/drivers/Makefile.am b/src/jtag/drivers/Makefile.am
index 6410f3754..4b2dbc44d 100644
--- a/src/jtag/drivers/Makefile.am
+++ b/src/jtag/drivers/Makefile.am
@@ -10,8 +10,10 @@ noinst_LTLIBRARIES += %D%/libocdjtagdrivers.la
%C%_libocdjtagdrivers_la_CPPFLAGS = $(AM_CPPFLAGS)
ULINK_FIRMWARE = %D%/OpenULINK
+ANGIE_FILES = %D%/angie
EXTRA_DIST += $(ULINK_FIRMWARE) \
+ $(ANGIE_FILES) \
%D%/usb_blaster/README.CheapClone \
%D%/Makefile.rlink \
%D%/rlink_call.m4 \
@@ -123,6 +125,12 @@ ulinkdir = $(pkgdatadir)/OpenULINK
dist_ulink_DATA = $(ULINK_FIRMWARE)/ulink_firmware.hex
%C%_libocdjtagdrivers_la_LIBADD += -lm
endif
+if ANGIE
+DRIVERFILES += %D%/angie.c
+angiedir = $(pkgdatadir)/angie
+dist_angie_DATA = $(ANGIE_FILES)/angie_firmware.bin $(ANGIE_FILES)/angie_bitstream.bit
+%C%_libocdjtagdrivers_la_LIBADD += -lm
+endif
if VSLLINK
DRIVERFILES += %D%/versaloon/usbtoxxx/usbtogpio.c
DRIVERFILES += %D%/versaloon/usbtoxxx/usbtojtagraw.c
diff --git a/src/jtag/drivers/angie.c b/src/jtag/drivers/angie.c
new file mode 100644
index 000000000..35811fb80
--- /dev/null
+++ b/src/jtag/drivers/angie.c
@@ -0,0 +1,2280 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/***************************************************************************
+ File : angie.c *
+ Contents : OpenOCD driver code for NanoXplore USB-JTAG ANGIE *
+ adapter hardware. *
+ Based on openULINK driver code by: Martin Schmoelzer. *
+ Copyright 2023, Ahmed Errached BOUDJELIDA, NanoXplore SAS. *
+ <abo...@na...> *
+ <ahm...@gm...> *
+ ***************************************************************************/
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <math.h>
+#include "helper/system.h"
+#include <jtag/interface.h>
+#include <jtag/commands.h>
+#include <target/image.h>
+#include <libusb.h>
+#include "libusb_helper.h"
+#include "angie/include/msgtypes.h"
+
+/** USB Vendor ID of ANGIE device in unconfigured state (no firmware loaded
+ * yet) or with its firmware. */
+#define ANGIE_VID 0x584e
+
+/** USB Product ID of ANGIE device in unconfigured state (no firmware loaded
+ * yet) or with its firmware. */
+#define ANGIE_PID 0x424e
+#define ANGIE_PID_2 0x4a55
+
+/** Address of EZ-USB ANGIE CPU Control & Status register. This register can be
+ * written by issuing a Control EP0 vendor request. */
+#define CPUCS_REG 0xE600
+
+/** USB Control EP0 bRequest: "Firmware Load". */
+#define REQUEST_FIRMWARE_LOAD 0xA0
+
+/** Value to write into CPUCS to put EZ-USB ANGIE into reset. */
+#define CPU_RESET 0x01
+
+/** Value to write into CPUCS to put EZ-USB ANGIE out of reset. */
+#define CPU_START 0x00
+
+/** Base address of firmware in EZ-USB ANGIE code space. */
+#define FIRMWARE_ADDR 0x0000
+
+/** USB interface number */
+#define USB_INTERFACE 0
+
+/** Delay (in microseconds) to wait while EZ-USB performs ReNumeration. */
+#define ANGIE_RENUMERATION_DELAY_US 1500000
+
+/** Default location of ANGIE firmware image. */
+#define ANGIE_FIRMWARE_FILE PKGDATADIR "/angie/angie_firmware.bin"
+
+/** Default location of ANGIE firmware image. */
+#define ANGIE_BITSTREAM_FILE PKGDATADIR "/angie/angie_bitstream.bit"
+
+/** Maximum size of a single firmware section. Entire EZ-USB ANGIE code space = 16kB */
+#define SECTION_BUFFERSIZE 16384
+
+/** Tuning of OpenOCD SCAN commands split into multiple ANGIE commands. */
+#define SPLIT_SCAN_THRESHOLD 10
+
+/** ANGIE hardware type */
+enum angie_type {
+ ANGIE,
+};
+
+enum angie_payload_direction {
+ PAYLOAD_DIRECTION_OUT,
+ PAYLOAD_DIRECTION_IN
+};
+
+enum angie_delay_type {
+ DELAY_CLOCK_TCK,
+ DELAY_CLOCK_TMS,
+ DELAY_SCAN_IN,
+ DELAY_SCAN_OUT,
+ DELAY_SCAN_IO
+};
+
+/**
+ * ANGIE command (ANGIE command queue element).
+ *
+ * For the OUT direction payload, things are quite easy: Payload is stored
+ * in a rather small array (up to 63 bytes), the payload is always allocated
+ * by the function generating the command and freed by angie_clear_queue().
+ *
+ * For the IN direction payload, things get a little bit more complicated:
+ * The maximum IN payload size for a single command is 64 bytes. Assume that
+ * a single OpenOCD command needs to scan 256 bytes. This results in the
+ * generation of four ANGIE commands. The function generating these
+ * commands shall allocate an uint8_t[256] array. Each command's #payload_in
+ * pointer shall point to the corresponding offset where IN data shall be
+ * placed, while #payload_in_start shall point to the first element of the 256
+ * byte array.
+ * - first command: #payload_in_start + 0
+ * - second command: #payload_in_start + 64
+ * - third command: #payload_in_start + 128
+ * - fourth command: #payload_in_start + 192
+ *
+ * The last command sets #needs_postprocessing to true.
+ */
+struct angie_cmd {
+ uint8_t id; /**< ANGIE command ID */
+
+ uint8_t *payload_out; /**< Pointer where OUT payload shall be stored */
+ uint8_t payload_out_size; /**< OUT direction payload size for this command */
+
+ uint8_t *payload_in_start; /**< Pointer to first element of IN payload array */
+ uint8_t *payload_in; /**< Pointer where IN payload shall be stored */
+ uint8_t payload_in_size; /**< IN direction payload size for this command */
+
+ /** Indicates if this command needs post-processing */
+ bool needs_postprocessing;
+
+ /** Indicates if angie_clear_queue() should free payload_in_start */
+ bool free_payload_in_start;
+
+ /** Pointer to corresponding OpenOCD command for post-processing */
+ struct jtag_command *cmd_origin;
+
+ struct angie_cmd *next; /**< Pointer to next command (linked list) */
+};
+
+/** Describes one driver instance */
+struct angie {
+ struct libusb_context *libusb_ctx;
+ struct libusb_device_handle *usb_device_handle;
+ enum angie_type type;
+
+ unsigned int ep_in; /**< IN endpoint number */
+ unsigned int ep_out; /**< OUT endpoint number */
+
+ /* delay value for "SLOW_CLOCK commands" in [0:255] range in units of 4 us;
+ -1 means no need for delay */
+ int delay_scan_in; /**< Delay value for SCAN_IN commands */
+ int delay_scan_out; /**< Delay value for SCAN_OUT commands */
+ int delay_scan_io; /**< Delay value for SCAN_IO commands */
+ int delay_clock_tck; /**< Delay value for CLOCK_TMS commands */
+ int delay_clock_tms; /**< Delay value for CLOCK_TCK commands */
+
+ int commands_in_queue; /**< Number of commands in queue */
+ struct angie_cmd *queue_start; /**< Pointer to first command in queue */
+ struct angie_cmd *queue_end; /**< Pointer to last command in queue */
+};
+
+/**************************** Function Prototypes *****************************/
+
+/* USB helper functions */
+static int angie_usb_open(struct angie *device);
+static int angie_usb_close(struct angie *device);
+
+/* ANGIE MCU (Cypress EZ-USB) specific functions */
+static int angie_cpu_reset(struct angie *device, char reset_bit);
+static int angie_load_firmware_and_renumerate(struct angie *device, const char *filename,
+ uint32_t delay_us);
+static int angie_load_firmware(struct angie *device, const char *filename);
+static int angie_load_bitstream(struct angie *device, const char *filename);
+
+static int angie_write_firmware_section(struct angie *device,
+ struct image *firmware_image, int section_index);
+
+/* Generic helper functions */
+static void angie_dump_signal_states(uint8_t input_signals, uint8_t output_signals);
+
+/* ANGIE command generation helper functions */
+static int angie_allocate_payload(struct angie_cmd *angie_cmd, int size,
+ enum angie_payload_direction direction);
+
+/* ANGIE command queue helper functions */
+static int angie_get_queue_size(struct angie *device,
+ enum angie_payload_direction direction);
+static void angie_clear_queue(struct angie *device);
+static int angie_append_queue(struct angie *device, struct angie_cmd *angie_cmd);
+static int angie_execute_queued_commands(struct angie *device, int timeout_ms);
+
+static void angie_dump_queue(struct angie *device);
+
+static int angie_append_scan_cmd(struct angie *device,
+ enum scan_type scan_type,
+ int scan_size_bits,
+ uint8_t *tdi,
+ uint8_t *tdo_start,
+ uint8_t *tdo,
+ uint8_t tms_count_start,
+ uint8_t tms_sequence_start,
+ uint8_t tms_count_end,
+ uint8_t tms_sequence_end,
+ struct jtag_command *origin,
+ bool postprocess);
+static int angie_append_clock_tms_cmd(struct angie *device, uint8_t count,
+ uint8_t sequence);
+static int angie_append_clock_tck_cmd(struct angie *device, uint16_t count);
+static int angie_append_get_signals_cmd(struct angie *device);
+static int angie_append_set_signals_cmd(struct angie *device, uint8_t low,
+ uint8_t high);
+static int angie_append_sleep_cmd(struct angie *device, uint32_t us);
+static int angie_append_configure_tck_cmd(struct angie *device,
+ int delay_scan_in,
+ int delay_scan_out,
+ int delay_scan_io,
+ int delay_tck,
+ int delay_tms);
+static int angie_append_test_cmd(struct angie *device);
+
+/* ANGIE TCK frequency helper functions */
+static int angie_calculate_delay(enum angie_delay_type type, long f, int *delay);
+
+/* Interface between ANGIE and OpenOCD */
+static void angie_set_end_state(tap_state_t endstate);
+static int angie_queue_statemove(struct angie *device);
+
+static int angie_queue_scan(struct angie *device, struct jtag_command *cmd);
+static int angie_queue_tlr_reset(struct angie *device, struct jtag_command *cmd);
+static int angie_queue_runtest(struct angie *device, struct jtag_command *cmd);
+static int angie_queue_pathmove(struct angie *device, struct jtag_command *cmd);
+static int angie_queue_sleep(struct angie *device, struct jtag_command *cmd);
+static int angie_queue_stableclocks(struct angie *device, struct jtag_command *cmd);
+
+static int angie_post_process_scan(struct angie_cmd *angie_cmd);
+static int angie_post_process_queue(struct angie *device);
+
+/* adapter driver functions */
+static int angie_execute_queue(void);
+static int angie_khz(int khz, int *jtag_speed);
+static int angie_speed(int speed);
+static int angie_speed_div(int speed, int *khz);
+static int angie_init(void);
+static int angie_quit(void);
+static int angie_reset(int trst, int srst);
+
+/****************************** Global Variables ******************************/
+
+static struct angie *angie_handle;
+
+/**************************** USB helper functions ****************************/
+
+/**
+ * Opens the ANGIE device
+ *
+ * @param device pointer to struct angie identifying ANGIE driver instance.
+ * @return on success: ERROR_OK
+ * @return on failure: ERROR_FAIL
+ */
+static int angie_usb_open(struct angie *device)
+{
+ struct libusb_device_handle *usb_device_handle;
+ const uint16_t vids[] = {ANGIE_VID, ANGIE_VID, 0};
+ const uint16_t pids[] = {ANGIE_PID, ANGIE_PID_2, 0};
+
+ int ret = jtag_libusb_open(vids, pids, &usb_device_handle, NULL);
+
+ if (ret != ERROR_OK)
+ return ret;
+
+ device->usb_device_handle = usb_device_handle;
+ device->type = ANGIE;
+
+ return ERROR_OK;
+}
+
+/**
+ * Releases the ANGIE interface and closes the USB device handle.
+ *
+ * @param device pointer to struct angie identifying ANGIE driver instance.
+ * @return on success: ERROR_OK
+ * @return on failure: ERROR_FAIL
+ */
+static int angie_usb_close(struct angie *device)
+{
+ if (device->usb_device_handle) {
+ if (libusb_release_interface(device->usb_device_handle, 0) != 0)
+ return ERROR_FAIL;
+
+ jtag_libusb_close(device->usb_device_handle);
+ device->usb_device_handle = NULL;
+ }
+ return ERROR_OK;
+}
+
+/******************* ANGIE CPU (EZ-USB) specific functions ********************/
+
+/**
+ * Writes '0' or '1' to the CPUCS register, putting the EZ-USB CPU into reset
+ * or out of reset.
+ *
+ * @param device pointer to struct angie identifying ANGIE driver instance.
+ * @param reset_bit 0 to put CPU into reset, 1 to put CPU out of reset.
+ * @return on success: ERROR_OK
+ * @return on failure: ERROR_FAIL
+ */
+static int angie_cpu_reset(struct angie *device, char reset_bit)
+{
+ return jtag_libusb_control_transfer(device->usb_device_handle,
+ (LIBUSB_ENDPOINT_OUT | LIBUSB_REQUEST_TYPE_VENDOR | LIBUSB_RECIPIENT_DEVICE),
+ REQUEST_FIRMWARE_LOAD, CPUCS_REG, 0, &reset_bit, 1, LIBUSB_TIMEOUT_MS, NULL);
+}
+
+/**
+ * Puts the ANGIE's EZ-USB microcontroller into reset state, downloads
+ * the firmware image, resumes the microcontroller and re-enumerates
+ * USB devices.
+ *
+ * @param device pointer to struct angie identifying ANGIE driver instance.
+ * The usb_handle member will be modified during re-enumeration.
+ * @param filename path to the Intel HEX file containing the firmware image.
+ * @param delay_us the delay to wait for the device to re-enumerate.
+ * @return on success: ERROR_OK
+ * @return on failure: ERROR_FAIL
+ */
+static int angie_load_firmware_and_renumerate(struct angie *device,
+ const char *filename, uint32_t delay_us)
+{
+ int ret;
+
+ /* Basic process: After downloading the firmware, the ANGIE will disconnect
+ * itself and re-connect after a short amount of time so we have to close
+ * the handle and re-enumerate USB devices */
+
+ ret = angie_load_firmware(device, filename);
+ if (ret != ERROR_OK)
+ return ret;
+
+ ret = angie_usb_close(device);
+ if (ret != ERROR_OK)
+ return ret;
+
+ usleep(delay_us);
+
+ return angie_usb_open(device);
+}
+
+/**
+ * Downloads a firmware image to the ANGIE's EZ-USB microcontroller
+ * over the USB bus.
+ *
+ * @param device pointer to struct angie identifying ANGIE driver instance.
+ * @param filename an absolute or relative path to the Intel HEX file
+ * containing the firmware image.
+ * @return on success: ERROR_OK
+ * @return on failure: ERROR_FAIL
+ */
+static int angie_load_firmware(struct angie *device, const char *filename)
+{
+ struct image angie_firmware_image;
+ int ret;
+
+ ret = angie_cpu_reset(device, CPU_RESET);
+ if (ret != ERROR_OK) {
+ LOG_ERROR("Could not halt ANGIE CPU");
+ return ret;
+ }
+
+ angie_firmware_image.base_address = 0;
+ angie_firmware_image.base_address_set = false;
+
+ ret = image_open(&angie_firmware_image, filename, "bin");
+ if (ret != ERROR_OK) {
+ LOG_ERROR("Could not load firmware image");
+ return ret;
+ }
+
+ /* Download all sections in the image to ANGIE */
+ for (unsigned int i = 0; i < angie_firmware_image.num_sections; i++) {
+ ret = angie_write_firmware_section(device, &angie_firmware_image, i);
+ if (ret != ERROR_OK)
+ return ret;
+ }
+
+ image_close(&angie_firmware_image);
+
+ ret = angie_cpu_reset(device, CPU_START);
+ if (ret != ERROR_OK) {
+ LOG_ERROR("Could not restart ANGIE CPU");
+ return ret;
+ }
+
+ return ERROR_OK;
+}
+
+/**
+ * Downloads a bitstream file to the ANGIE's FPGA through the EZ-USB microcontroller
+ * over the USB bus.
+ *
+ * @param device pointer to struct angie identifying ANGIE driver instance.
+ * @param filename an absolute or relative path to the Xilinx .bit file
+ * containing the bitstream data.
+ * @return on success: ERROR_OK
+ * @return on failure: ERROR_FAIL
+ */
+static int angie_load_bitstream(struct angie *device, const char *filename)
+{
+ int ret, transferred;
+ const char *bitstream_file_path = filename;
+ FILE *bitstream_file = NULL;
+ char *bitstream_data = NULL;
+ size_t bitstream_size = 0;
+
+ /* CFGopen */
+ ret = jtag_libusb_control_transfer(device->usb_device_handle,
+ 0x00, 0xB0, 0, 0, NULL, 0, LIBUSB_TIMEOUT_MS, &transferred);
+ if (ret != ERROR_OK) {
+ LOG_ERROR("Failed opencfg");
+ /* Abort if libusb sent less data than requested */
+ return ERROR_FAIL;
+ }
+
+ /* Open the bitstream file */
+ bitstream_file = fopen(bitstream_file_path, "rb");
+ if (!bitstream_file) {
+ LOG_ERROR("Failed to open bitstream file: %s\n", bitstream_file_path);
+ return ERROR_FAIL;
+ }
+
+ /* Get the size of the bitstream file */
+ fseek(bitstream_file, 0, SEEK_END);
+ bitstream_size = ftell(bitstream_file);
+ fseek(bitstream_file, 0, SEEK_SET);
+
+ /* Allocate memory for the bitstream data */
+ bitstream_data = malloc(bitstream_size);
+ if (!bitstream_data) {
+ LOG_ERROR("Failed to allocate memory for bitstream data.");
+ fclose(bitstream_file);
+ return ERROR_FAIL;
+ }
+
+ /* Read the bitstream data from the file */
+ if (fread(bitstream_data, 1, bitstream_size, bitstream_file) != bitstream_size) {
+ LOG_ERROR("Failed to read bitstream data.");
+ free(bitstream_data);
+ fclose(bitstream_file);
+ return ERROR_FAIL;
+ }
+
+ /* Send the bitstream data to the microcontroller */
+ int actual_length = 0;
+ ret = jtag_libusb_bulk_write(device->usb_device_handle, 0x02, bitstream_data, bitstream_size, 1000, &actual_length);
+ if (ret != ERROR_OK) {
+ LOG_ERROR("Failed to send bitstream data: %s", libusb_strerror(ret));
+ free(bitstream_data);
+ fclose(bitstream_file);
+ return ERROR_FAIL;
+ }
+
+ LOG_INFO("Bitstream sent successfully.");
+
+ /* Clean up */
+ free(bitstream_data);
+ fclose(bitstream_file);
+
+ /* CFGclose */
+ transferred = 0;
+ ret = jtag_libusb_control_transfer(device->usb_device_handle,
+ 0x00, 0xB1, 0, 0, NULL, 0, LIBUSB_TIMEOUT_MS, &transferred);
+ if (ret != ERROR_OK) {
+ LOG_INFO("error cfgclose");
+ /* Abort if libusb sent less data than requested */
+ return ERROR_FAIL;
+ }
+ return ERROR_OK;
+}
+
+/**
+ * Send one contiguous firmware section to the ANGIE's EZ-USB microcontroller
+ * over the USB bus.
+ *
+ * @param device pointer to struct angie identifying ANGIE driver instance.
+ * @param firmware_image pointer to the firmware image that contains the section
+ * which should be sent to the ANGIE's EZ-USB microcontroller.
+ * @param section_index index of the section within the firmware image.
+ * @return on success: ERROR_OK
+ * @return on failure: ERROR_FAIL
+ */
+static int angie_write_firmware_section(struct angie *device,
+ struct image *firmware_image, int section_index)
+{
+ int addr, bytes_remaining, chunk_size;
+ uint8_t data[SECTION_BUFFERSIZE];
+ uint8_t *data_ptr = data;
+ uint16_t size;
+ size_t size_read;
+ int ret, transferred;
+
+ size = (uint16_t)firmware_image->sections[section_index].size;
+ addr = (uint16_t)firmware_image->sections[section_index].base_address;
+
+ LOG_DEBUG("section %02i at addr 0x%04x (size 0x%04" PRIx16 ")", section_index, addr,
+ size);
+
+ /* Copy section contents to local buffer */
+ ret = image_read_section(firmware_image, section_index, 0, size, data,
+ &size_read);
+
+ if (ret != ERROR_OK)
+ return ret;
+ if (size_read != size)
+ return ERROR_FAIL;
+
+ bytes_remaining = size;
+
+ /* Send section data in chunks of up to 64 bytes to ANGIE */
+ while (bytes_remaining > 0) {
+ if (bytes_remaining > 64)
+ chunk_size = 64;
+ else
+ chunk_size = bytes_remaining;
+
+ ret = jtag_libusb_control_transfer(device->usb_device_handle,
+ (LIBUSB_ENDPOINT_OUT | LIBUSB_REQUEST_TYPE_VENDOR | LIBUSB_RECIPIENT_DEVICE),
+ REQUEST_FIRMWARE_LOAD, addr, FIRMWARE_ADDR, (char *)data_ptr,
+ chunk_size, LIBUSB_TIMEOUT_MS, &transferred);
+
+ if (ret != ERROR_OK)
+ return ret;
+
+ if (transferred != chunk_size) {
+ /* Abort if libusb sent less data than requested */
+ return ERROR_FAIL;
+ }
+
+ bytes_remaining -= chunk_size;
+ addr += chunk_size;
+ data_ptr += chunk_size;
+ }
+
+ return ERROR_OK;
+}
+
+/************************** Generic helper functions **************************/
+
+/**
+ * Print state of interesting signals via LOG_INFO().
+ *
+ * @param input_signals input signal states as returned by CMD_GET_SIGNALS
+ * @param output_signals output signal states as returned by CMD_GET_SIGNALS
+ */
+static void angie_dump_signal_states(uint8_t input_signals, uint8_t output_signals)
+{
+ LOG_INFO("ANGIE signal states: TDI: %i, TDO: %i, TMS: %i, TCK: %i, TRST: %i "
+ "SRST: %i",
+ (output_signals & SIGNAL_TDI ? 1 : 0),
+ (input_signals & SIGNAL_TDO ? 1 : 0),
+ (output_signals & SIGNAL_TMS ? 1 : 0),
+ (output_signals & SIGNAL_TCK ? 1 : 0),
+ (output_signals & SIGNAL_TRST ? 1 : 0),
+ (output_signals & SIGNAL_SRST ? 1 : 0));
+}
+
+/**************** ANGIE command generation helper functions ***************/
+
+/**
+ * Allocate and initialize space in memory for ANGIE command payload.
+ *
+ * @param angie_cmd pointer to command whose payload should be allocated.
+ * @param size the amount of memory to allocate (bytes).
+ * @param direction which payload to allocate.
+ * @return on success: ERROR_OK
+ * @return on failure: ERROR_FAIL
+ */
+static int angie_allocate_payload(struct angie_cmd *angie_cmd, int size,
+ enum angie_payload_direction direction)
+{
+ uint8_t *payload;
+
+ payload = calloc(size, sizeof(uint8_t));
+
+ if (!payload) {
+ LOG_ERROR("Could not allocate ANGIE command payload: out of memory");
+ return ERROR_FAIL;
+ }
+
+ switch (direction) {
+ case PAYLOAD_DIRECTION_OUT:
+ if (angie_cmd->payload_out) {
+ LOG_ERROR("BUG: Duplicate payload allocation for ANGIE command");
+ free(payload);
+ return ERROR_FAIL;
+ }
+ angie_cmd->payload_out = payload;
+ angie_cmd->payload_out_size = size;
+ break;
+ case PAYLOAD_DIRECTION_IN:
+ if (angie_cmd->payload_in_start) {
+ LOG_ERROR("BUG: Duplicate payload allocation for ANGIE command");
+ free(payload);
+ return ERROR_FAIL;
+ }
+
+ angie_cmd->payload_in_start = payload;
+ angie_cmd->payload_in = payload;
+ angie_cmd->payload_in_size = size;
+
+ /* By default, free payload_in_start in angie_clear_queue(). Commands
+ * that do not want this behavior (e. g. split scans) must turn it off
+ * separately! */
+ angie_cmd->free_payload_in_start = true;
+
+ break;
+ }
+
+ return ERROR_OK;
+}
+
+/****************** ANGIE command queue helper functions ******************/
+
+/**
+ * Get the current number of bytes in the queue, including command IDs.
+ *
+ * @param device pointer to struct angie identifying ANGIE driver instance.
+ * @param direction the transfer direction for which to get byte count.
+ * @return the number of bytes currently stored in the queue for the specified
+ * direction.
+ */
+static int angie_get_queue_size(struct angie *device,
+ enum angie_payload_direction direction)
+{
+ struct angie_cmd *current = device->queue_start;
+ int sum = 0;
+
+ while (current) {
+ switch (direction) {
+ case PAYLOAD_DIRECTION_OUT:
+ sum += current->payload_out_size + 1; /* + 1 byte for Command ID */
+ break;
+ case PAYLOAD_DIRECTION_IN:
+ sum += current->payload_in_size;
+ break;
+ }
+
+ current = current->next;
+ }
+
+ return sum;
+}
+
+/**
+ * Clear the ANGIE command queue.
+ *
+ * @param device pointer to struct angie identifying ANGIE driver instance.
+ */
+static void angie_clear_queue(struct angie *device)
+{
+ struct angie_cmd *current = device->queue_start;
+ struct angie_cmd *next = NULL;
+
+ while (current) {
+ /* Save pointer to next element */
+ next = current->next;
+
+ /* Free payloads: OUT payload can be freed immediately */
+ free(current->payload_out);
+ current->payload_out = NULL;
+
+ /* IN payload MUST be freed ONLY if no other commands use the
+ * payload_in_start buffer */
+ if (current->free_payload_in_start) {
+ free(current->payload_in_start);
+ current->payload_in_start = NULL;
+ current->payload_in = NULL;
+ }
+
+ /* Free queue element */
+ free(current);
+
+ /* Proceed with next element */
+ current = next;
+ }
+
+ device->commands_in_queue = 0;
+ device->queue_start = NULL;
+ device->queue_end = NULL;
+}
+
+/**
+ * Add a command to the ANGIE command queue.
+ *
+ * @param device pointer to struct angie identifying ANGIE driver instance.
+ * @param angie_cmd pointer to command that shall be appended to the ANGIE
+ * command queue.
+ * @return on success: ERROR_OK
+ * @return on failure: ERROR_FAIL
+ */
+static int angie_append_queue(struct angie *device, struct angie_cmd *angie_cmd)
+{
+ int newsize_out, newsize_in;
+ int ret = ERROR_OK;
+
+ newsize_out = angie_get_queue_size(device, PAYLOAD_DIRECTION_OUT) + 1
+ + angie_cmd->payload_out_size;
+
+ newsize_in = angie_get_queue_size(device, PAYLOAD_DIRECTION_IN)
+ + angie_cmd->payload_in_size;
+
+ /* Check if the current command can be appended to the queue */
+ if (newsize_out > 64 || newsize_in > 64) {
+ /* New command does not fit. Execute all commands in queue before starting
+ * new queue with the current command as first entry. */
+ ret = angie_execute_queued_commands(device, LIBUSB_TIMEOUT_MS);
+
+ if (ret == ERROR_OK)
+ ret = angie_post_process_queue(device);
+
+ if (ret == ERROR_OK)
+ angie_clear_queue(device);
+ }
+
+ if (!device->queue_start) {
+ /* Queue was empty */
+ device->commands_in_queue = 1;
+
+ device->queue_start = angie_cmd;
+ device->queue_end = angie_cmd;
+ } else {
+ /* There are already commands in the queue */
+ device->commands_in_queue++;
+
+ device->queue_end->next = angie_cmd;
+ device->queue_end = angie_cmd;
+ }
+
+ if (ret != ERROR_OK)
+ angie_clear_queue(device);
+
+ return ret;
+}
+
+/**
+ * Sends all queued ANGIE commands to the ANGIE for execution.
+ *
+ * @param device pointer to struct angie identifying ANGIE driver instance.
+ * @param timeout_ms
+ * @return on success: ERROR_OK
+ * @return on failure: ERROR_FAIL
+ */
+static int angie_execute_queued_commands(struct angie *device, int timeout_ms)
+{
+ struct angie_cmd *current;
+ int ret, i, index_out, index_in, count_out, count_in, transferred;
+ uint8_t buffer[64];
+
+ if (LOG_LEVEL_IS(LOG_LVL_DEBUG_IO))
+ angie_dump_queue(device);
+
+ index_out = 0;
+ count_out = 0;
+ count_in = 0;
+
+ for (current = device->queue_start; current; current = current->next) {
+ /* Add command to packet */
+ buffer[index_out] = current->id;
+ index_out++;
+ count_out++;
+
+ for (i = 0; i < current->payload_out_size; i++)
+ buffer[index_out + i] = current->payload_out[i];
+ index_out += current->payload_out_size;
+ count_in += current->payload_in_size;
+ count_out += current->payload_out_size;
+ }
+
+ /* Send packet to ANGIE */
+ ret = jtag_libusb_bulk_write(device->usb_device_handle, device->ep_out,
+ (char *)buffer, count_out, timeout_ms, &transferred);
+ if (ret != ERROR_OK)
+ return ret;
+ if (transferred != count_out)
+ return ERROR_FAIL;
+
+ /* Wait for response if commands contain IN payload data */
+ if (count_in > 0) {
+ ret = jtag_libusb_bulk_write(device->usb_device_handle, device->ep_in,
+ (char *)buffer, count_in, timeout_ms, &transferred);
+ if (ret != ERROR_OK)
+ return ret;
+ if (transferred != coun...
[truncated message content] |