diff --git a/configure.ac b/configure.ac index 47717bf2c..7b66d815d 100644 --- a/configure.ac +++ b/configure.ac @@ -115,6 +115,7 @@ m4_define([ADAPTER_OPT], [m4_translit(ADAPTER_ARG($1), [_], [-])]) m4_define([USB1_ADAPTERS], [[[ftdi], [MPSSE mode of FTDI based devices], [FTDI]], + [[tamarin], [Tamarin Cable], [TAMARIN]], [[stlink], [ST-Link Programmer], [HLADAPTER_STLINK]], [[ti_icdi], [TI ICDI JTAG Programmer], [HLADAPTER_ICDI]], [[ulink], [Keil ULINK JTAG Programmer], [ULINK]], diff --git a/src/jtag/core.c b/src/jtag/core.c index 574801187..8b466f122 100644 --- a/src/jtag/core.c +++ b/src/jtag/core.c @@ -625,6 +625,11 @@ static int adapter_system_reset(int req_srst) /* Maybe change SRST signal state */ if (jtag_srst != req_srst) { + if(adapter_driver->reset == NULL) { + LOG_ERROR("adapter_driver->reset is NULL"); + return ERROR_FAIL; + } + retval = adapter_driver->reset(0, req_srst); if (retval != ERROR_OK) { LOG_ERROR("SRST error"); diff --git a/src/jtag/drivers/Makefile.am b/src/jtag/drivers/Makefile.am index 6410f3754..3276db1c4 100644 --- a/src/jtag/drivers/Makefile.am +++ b/src/jtag/drivers/Makefile.am @@ -71,6 +71,9 @@ endif if FTDI DRIVERFILES += %D%/ftdi.c %D%/mpsse.c endif +if TAMARIN +DRIVERFILES += %D%/tamarin.c +endif if LINUXGPIOD DRIVERFILES += %D%/linuxgpiod.c endif diff --git a/src/jtag/drivers/tamarin.c b/src/jtag/drivers/tamarin.c new file mode 100644 index 000000000..260db1ce9 --- /dev/null +++ b/src/jtag/drivers/tamarin.c @@ -0,0 +1,530 @@ +/*************************************************************************** + * Copyright (C) 2022 by Thomas Roth * + * code@stacksmashing.net * + * * + * Based on picoprobe.c by Liam Fraser * + * * + * Also based on: kitprog.c, ftdi.c, mpsse.c * + * * + * 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. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program. If not, see . * + ***************************************************************************/ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + + +#include +#include +#include +#include +#include "target/target.h" +#include "libusb_helper.h" + +enum TAMARIN_CMDS { + TAMARIN_INVALID = 0, // Invalid command + TAMARIN_READ = 1, + TAMARIN_WRITE = 2, + TAMARIN_LINE_RESET = 3, + TAMARIN_SET_FREQ = 4, + TAMARIN_RESET = 5 +}; + +static int tamarin_line_reset(void); + +// This struct is the direct struct that is sent to +// the probe. +struct __attribute__((__packed__)) tamarin_cmd_hdr { + // Currently unused + uint8_t id; + // One of TAMARIN_CMDS + uint8_t cmd; + // The full (incl. start/stop/parity) SWD command + // Unused for LINE_RESET and SET_FREQ. + uint8_t request; + // The data for writes (unused otherwise) + uint32_t data; + // Number of (8 bit) idle cycles to perform after this op + uint8_t idle_cycles; +}; + +// This is the struture returned by the probe for each command +struct __attribute__((__packed__)) tamarin_res_hdr { + // Unused + uint8_t id; + // The (3 bit) result: OK/WAIT/FAULT + uint8_t res; + // The data for reads (undefined otherwise) + uint32_t data; +}; + +// This struct is just an encapsulation for the +// cmd_hdr that also keeps the pointer for reads. +struct tamarin_cmd_hdr_enc { + struct tamarin_cmd_hdr cmd; + uint32_t *data; +}; + +// Random value that performs well enough +#define TAMARIN_QUEUE_SIZE 4096*16 + + +#define VID 0x2B3E /* Raspberry Pi */ +#define PID 0x0004 /* Picoprobe */ + +#define BULK_EP_OUT 4 +#define BULK_EP_IN 5 +#define PICOPROBE_INTERFACE 2 + +#define PICOPROBE_MAX_PACKET_LENGTH 512 +#define LIBUSB_TIMEOUT 1000 + + + +struct tamarin { + libusb_device_handle *usb_handle; + int freq; + struct tamarin_cmd_hdr_enc queue[TAMARIN_QUEUE_SIZE]; + size_t queue_length; +}; + +static int queued_retval; + + +static struct tamarin *tamarin_handle; + +static int tamarin_init(void); +static int tamarin_quit(void); + + +struct __attribute__((__packed__)) probe_cmd_hdr { + uint8_t id; + uint8_t cmd; + uint32_t bits; +}; + +struct __attribute__((__packed__)) probe_pkt_hdr { + uint32_t total_packet_length; +}; + + + + +/* Separate queue to swd_cmd_queue because we sometimes insert idle cycles not described + * there */ +#define PICOPROBE_QUEUE_SIZE 64 +struct tamarin_queue_entry { + uint8_t id; + uint8_t cmd; /* PROBE_CMDS */ + unsigned bits; + unsigned offset; + const uint8_t *buf; +}; + +static int tamarin_swd_run_queue(void) +{ + LOG_DEBUG_IO("Executing %zu queued transactions", tamarin_handle->queue_length); + + + // yes this is slow as fuck + for(size_t i = 0; i < tamarin_handle->queue_length; i++) { + // sleep(0.1); + struct tamarin_res_hdr result; + struct tamarin_cmd_hdr_enc *command_enc = &tamarin_handle->queue[i]; + struct tamarin_cmd_hdr *command = &command_enc->cmd; + int ret; + // write the SINGLE command + // LOG_DEBUG("USB BULK WRITE"); + if(tamarin_handle->usb_handle == NULL) { + LOG_DEBUG("usb_handle is null!\n"); + } + if(command == NULL) { + LOG_DEBUG("command is null!\n"); + } + int data_written_unused = 0; + ret = jtag_libusb_bulk_write(tamarin_handle->usb_handle, BULK_EP_OUT, (char*)command, sizeof(struct tamarin_cmd_hdr), LIBUSB_TIMEOUT, &data_written_unused); + if(ret < 0) { + LOG_DEBUG("BULK WRITE FAILED"); + return ERROR_JTAG_DEVICE_ERROR; + } + // LOG_DEBUG("DONE, LETS REAAAD"); + ret = jtag_libusb_bulk_read(tamarin_handle->usb_handle, + BULK_EP_IN | LIBUSB_ENDPOINT_IN, (char *)&result, + sizeof(struct tamarin_res_hdr), LIBUSB_TIMEOUT, &data_written_unused); + if(ret < 0) { + LOG_DEBUG("BULK READ FAILED"); + tamarin_handle->queue_length = 0; + return ERROR_JTAG_DEVICE_ERROR; + } + // LOG_DEBUG("READ DONE"); + // LOG_DEBUG("Result is: %d %d %d\n", result.id, result.res, result.data); + + // TODO: Handle errors better here. + if(result.res == 4) { + LOG_DEBUG("FAIL FAIL FAIL\n"); + tamarin_handle->queue_length = 0; + return ERROR_TARGET_FAILURE; + } + if((command->cmd == TAMARIN_READ) && (command_enc->data != NULL)) { + *command_enc->data = result.data; + } + + keep_alive(); + } + + + tamarin_handle->queue_length = 0; + + queued_retval = ERROR_OK; + return ERROR_OK; +} + +static void tamarin_swd_read_reg(uint8_t cmd, uint32_t *value, uint32_t ap_delay_clk) +{ + assert(cmd & SWD_CMD_RNW); + // Et hätt noch immer jot jejange. + assert(tamarin_handle->queue_length < TAMARIN_QUEUE_SIZE-1); + + struct tamarin_cmd_hdr command = { + .id = 0, + .cmd = TAMARIN_READ, + .request = cmd | 0x81, + .data = 0, + .idle_cycles = ap_delay_clk + }; + + struct tamarin_cmd_hdr_enc command_enc = { + .cmd = command, + .data = value + }; + + LOG_DEBUG("Enqueue read: 0x%02X", cmd); + tamarin_handle->queue[tamarin_handle->queue_length] = command_enc; + tamarin_handle->queue_length++; +} + +static void tamarin_swd_write_reg(uint8_t cmd, uint32_t value, uint32_t ap_delay_clk) +{ + assert(!(cmd & SWD_CMD_RNW)); + assert(tamarin_handle->queue_length < TAMARIN_QUEUE_SIZE-1); + + struct tamarin_cmd_hdr command = { + .id = 0, + .cmd = TAMARIN_WRITE, + .request = cmd | 0x81, + .data = value, + .idle_cycles = ap_delay_clk + }; + + struct tamarin_cmd_hdr_enc command_enc = { + .cmd = command, + .data = NULL + }; + + LOG_DEBUG("Enqueue write: 0x%02X - 0x%08X", cmd, value); + tamarin_handle->queue[tamarin_handle->queue_length] = command_enc; + tamarin_handle->queue_length++; +} + +static int_least32_t tamarin_set_frequency(int_least32_t hz) +{ + + struct tamarin_cmd_hdr command = { + .id = 0, + .cmd = TAMARIN_SET_FREQ, + .request = 0, + .data = hz / 1000, + .idle_cycles = 0 + }; + struct tamarin_cmd_hdr_enc command_enc = { + .cmd = command, + .data = NULL + }; + + LOG_DEBUG("Enqueue set frequency: %d", hz/1000); + tamarin_handle->queue[tamarin_handle->queue_length] = command_enc; + tamarin_handle->queue_length++; + + + // TODO: Flush + // TODO: handle error + // if (ret < 0) + // return ERROR_JTAG_DEVICE_ERROR; + + return hz; +} + +static int_least32_t tamarin_speed(int_least32_t hz) +{ + int ret = tamarin_set_frequency(hz); + + if (ret < 0) + LOG_ERROR("Couldn't set tamarin cable speed"); + else + tamarin_handle->freq = ret; + + return ERROR_OK; +} + +static int tamarin_khz(int khz, int *jtag_speed) +{ + *jtag_speed = khz * 1000; + return ERROR_OK; +} + +static int tamarin_speed_div(int speed, int *khz) +{ + *khz = speed / 1000; + return ERROR_OK; +} + +static int tamarin_swd_init(void) +{ + return ERROR_OK; +} + +static int tamarin_line_reset(void) { + struct tamarin_cmd_hdr command = { + .id = 0, + .cmd = TAMARIN_LINE_RESET, + .request = 0, + .data = 0, + .idle_cycles = 0 + }; + struct tamarin_cmd_hdr_enc command_enc = { + .cmd = command, + .data = NULL + }; + + + LOG_DEBUG("Enqueue line reset / SWD-to-JTAG sequence"); + tamarin_handle->queue[tamarin_handle->queue_length] = command_enc; + tamarin_handle->queue_length++; + return ERROR_OK; +} + +static int tamarin_swd_switch_seq(enum swd_special_seq seq) +{ + int ret = ERROR_OK; + + switch (seq) { + case LINE_RESET: + LOG_DEBUG_IO("SWD line reset"); + tamarin_line_reset(); + // ret = tamarin_write_bits(swd_seq_line_reset, 0, swd_seq_line_reset_len); + break; + case JTAG_TO_SWD: + LOG_DEBUG("JTAG-to-SWD"); + tamarin_line_reset(); + // ret = tamarin_write_bits(swd_seq_jtag_to_swd, 0, swd_seq_jtag_to_swd_len); + break; + case SWD_TO_JTAG: + LOG_DEBUG("SWD-to-JTAG"); + LOG_DEBUG("NOT IMPLEMENTED"); + // assert(false); + // ret = tamarin_write_bits(swd_seq_swd_to_jtag, 0, swd_seq_swd_to_jtag_len); + break; + case DORMANT_TO_SWD: + LOG_DEBUG("DORMANT-to-SWD"); + + LOG_DEBUG("NOT IMPLEMENTED"); + // assert(false); // ret = tamarin_write_bits(swd_seq_dormant_to_swd, 0, swd_seq_dormant_to_swd_len); + break; + case SWD_TO_DORMANT: + LOG_DEBUG("SWD-to-DORMANT"); + LOG_DEBUG("NOT IMPLEMENTED"); + // ret = tamarin_write_bits(swd_seq_swd_to_dormant, 0, swd_seq_swd_to_dormant_len); + break; + default: + LOG_ERROR("Sequence %d not supported", seq); + return ERROR_FAIL; + } + + return ret; +} + +static const struct swd_driver tamarin_swd = { + .init = tamarin_swd_init, + .switch_seq = tamarin_swd_switch_seq, + .read_reg = tamarin_swd_read_reg, + .write_reg = tamarin_swd_write_reg, + .run = tamarin_swd_run_queue, +}; + +const char *tamarin_serial_number = NULL; + + +static const struct command_registration serialnum_command_handlers[] = { + COMMAND_REGISTRATION_DONE +}; +static const char * const tamarin_transports[] = { "swd", NULL }; + + + +// struct adapter_driver jlink_adapter_driver = { +// .name = "jlink", +// .transports = jlink_transports, +// .commands = jlink_command_handlers, + +// .init = &jlink_init, +// .quit = &jlink_quit, +// .reset = &jlink_reset_safe, +// .speed = &jlink_speed, +// .khz = &jlink_khz, +// .speed_div = &jlink_speed_div, +// .config_trace = &config_trace, +// .poll_trace = &poll_trace, + +// .jtag_ops = &jlink_interface, +// .swd_ops = &jlink_swd, +// }; + +// static int tamarin_line_reset(void) { +// struct tamarin_cmd_hdr command = { +// .id = 0, +// .cmd = TAMARIN_LINE_RESET, +// .request = 0, +// .data = 0, +// .idle_cycles = 0 +// }; +// struct tamarin_cmd_hdr_enc command_enc = { +// .cmd = command, +// .data = NULL +// }; + + +// LOG_DEBUG("Enqueue line reset / SWD-to-JTAG sequence"); +// tamarin_handle->queue[tamarin_handle->queue_length] = command_enc; +// tamarin_handle->queue_length++; +// return ERROR_OK; +// } + +static int tamarin_reset(int trst, int srst) +{ + // struct signal *sig_ntrst = find_signal_by_name("nTRST"); + // struct signal *sig_nsrst = find_signal_by_name("nSRST"); + + LOG_DEBUG_IO("reset trst: %i srst %i", trst, srst); + struct tamarin_cmd_hdr command = { + .id = 0, + .cmd = TAMARIN_RESET, + .request = 0, + .data = 0, + .idle_cycles = 0 + }; + struct tamarin_cmd_hdr_enc command_enc = { + .cmd = command, + .data = NULL + }; + + + LOG_DEBUG("Enqueue RESET"); + tamarin_handle->queue[tamarin_handle->queue_length] = command_enc; + tamarin_handle->queue_length++; + // if (!swd_mode) { + // if (trst == 1) { + // if (sig_ntrst) + // ftdi_set_signal(sig_ntrst, '0'); + // else + // LOG_ERROR("Can't assert TRST: nTRST signal is not defined"); + // } else if (sig_ntrst && jtag_get_reset_config() & RESET_HAS_TRST && + // trst == 0) { + // if (jtag_get_reset_config() & RESET_TRST_OPEN_DRAIN) + // ftdi_set_signal(sig_ntrst, 'z'); + // else + // ftdi_set_signal(sig_ntrst, '1'); + // } + // } + + // if (srst == 1) { + // if (sig_nsrst) + // ftdi_set_signal(sig_nsrst, '0'); + // else + // LOG_ERROR("Can't assert SRST: nSRST signal is not defined"); + // } else if (sig_nsrst && jtag_get_reset_config() & RESET_HAS_SRST && + // srst == 0) { + // if (jtag_get_reset_config() & RESET_SRST_PUSH_PULL) + // ftdi_set_signal(sig_nsrst, '1'); + // else + // ftdi_set_signal(sig_nsrst, 'z'); + // } + + return ERROR_OK; +} + +struct adapter_driver tamarin_adapter_driver = { + .name = "tamarin", + .commands = serialnum_command_handlers, + .transports = tamarin_transports, + .swd_ops = &tamarin_swd, + .init = tamarin_init, + .quit = tamarin_quit, + .reset = tamarin_reset, + // .reset = NULL, // TODO: Is this really supported by OpenOCD or will it null deref? + .speed = tamarin_speed, + .speed_div = tamarin_speed_div, + .khz = tamarin_khz, +}; + +static int tamarin_usb_open(void) +{ + const uint16_t vids[] = { VID, 0 }; + const uint16_t pids[] = { PID, 0 }; + + if (jtag_libusb_open(vids, pids, + &tamarin_handle->usb_handle, NULL) != ERROR_OK) { + LOG_ERROR("Failed to open or find the device"); + return ERROR_FAIL; + } + + + + if (libusb_claim_interface(tamarin_handle->usb_handle, PICOPROBE_INTERFACE) != ERROR_OK) { + LOG_ERROR("Failed to claim tamarin cable interface"); + return ERROR_FAIL; + } + + return ERROR_OK; +} + +static void tamarin_usb_close(void) +{ + jtag_libusb_close(tamarin_handle->usb_handle); +} + +static int tamarin_init(void) +{ + tamarin_handle = malloc(sizeof(struct tamarin)); + if (tamarin_handle == NULL) { + LOG_ERROR("Failed to allocate memory"); + return ERROR_FAIL; + } + + if (tamarin_usb_open() != ERROR_OK) { + LOG_ERROR("Can't find a tamarin device! Please check device connections and permissions."); + return ERROR_JTAG_INIT_FAILED; + } + + + tamarin_handle->queue_length = 0; + + return ERROR_OK; +} + + +static int tamarin_quit(void) +{ + tamarin_usb_close(); + return ERROR_OK; +} diff --git a/src/jtag/interfaces.c b/src/jtag/interfaces.c index 67bbb3b36..e99524149 100644 --- a/src/jtag/interfaces.c +++ b/src/jtag/interfaces.c @@ -40,6 +40,9 @@ extern struct adapter_driver dummy_adapter_driver; #if BUILD_FTDI == 1 extern struct adapter_driver ftdi_adapter_driver; #endif +#if BUILD_TAMARIN == 1 +extern struct adapter_driver tamarin_adapter_driver; +#endif #if BUILD_USB_BLASTER == 1 || BUILD_USB_BLASTER_2 == 1 extern struct adapter_driver usb_blaster_adapter_driver; #endif @@ -160,6 +163,9 @@ struct adapter_driver *adapter_drivers[] = { #if BUILD_FTDI == 1 &ftdi_adapter_driver, #endif +#if BUILD_TAMARIN == 1 + &tamarin_adapter_driver, +#endif #if BUILD_USB_BLASTER || BUILD_USB_BLASTER_2 == 1 &usb_blaster_adapter_driver, #endif diff --git a/src/target/target.c b/src/target/target.c index e3a6f955e..4bf7853ff 100644 --- a/src/target/target.c +++ b/src/target/target.c @@ -1944,13 +1944,13 @@ static int target_call_timer_callbacks_check_time(int checktime) return ERROR_OK; } -int target_call_timer_callbacks() +int target_call_timer_callbacks(void) { return target_call_timer_callbacks_check_time(1); } /* invoke periodic callbacks immediately */ -int target_call_timer_callbacks_now() +int target_call_timer_callbacks_now(void) { return target_call_timer_callbacks_check_time(0); } diff --git a/tcl/interface/tamarin.cfg b/tcl/interface/tamarin.cfg new file mode 100644 index 000000000..db0aba953 --- /dev/null +++ b/tcl/interface/tamarin.cfg @@ -0,0 +1,3 @@ +interface tamarin +transport select swd +adapter_khz 2000