aboutsummaryrefslogtreecommitdiff
path: root/src/target/esirisc_jtag.c
diff options
context:
space:
mode:
authorSteven Stallion <stallion@squareup.com>2018-08-28 17:18:01 -0700
committerMatthias Welwarsky <matthias@welwarsky.de>2018-10-16 11:58:24 +0100
commit4ab75a3634901c4e3897d771e2c75a64c7353c28 (patch)
tree475731fa20dae25c39a88804e894b69c69900e2f /src/target/esirisc_jtag.c
parente72b2601e71f49af10f72c4bb6220ee2061ef173 (diff)
esirisc: support eSi-RISC targets
eSi-RISC is a highly configurable microprocessor architecture for embedded systems provided by EnSilica. This patch adds support for 32-bit targets and also includes an internal flash driver and uC/OS-III RTOS support. This is a non-traditional target and required a number of additional changes to support non-linear register numbers and the 'p' packet in RTOS support for proper integration into EnSilica's GDB port. Change-Id: I59d5c40b3bb2ace1b1a01b2538bfab211adf113f Signed-off-by: Steven Stallion <stallion@squareup.com> Reviewed-on: http://openocd.zylin.com/4660 Tested-by: jenkins Reviewed-by: Matthias Welwarsky <matthias@welwarsky.de>
Diffstat (limited to 'src/target/esirisc_jtag.c')
-rw-r--r--src/target/esirisc_jtag.c514
1 files changed, 514 insertions, 0 deletions
diff --git a/src/target/esirisc_jtag.c b/src/target/esirisc_jtag.c
new file mode 100644
index 00000000..8ab47fa8
--- /dev/null
+++ b/src/target/esirisc_jtag.c
@@ -0,0 +1,514 @@
+/***************************************************************************
+ * Copyright (C) 2018 by Square, Inc. *
+ * Steven Stallion <stallion@squareup.com> *
+ * James Zhao <hjz@squareup.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. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program. If not, see <http://www.gnu.org/licenses/>. *
+ ***************************************************************************/
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <helper/binarybuffer.h>
+#include <helper/log.h>
+#include <helper/types.h>
+#include <jtag/jtag.h>
+#include <jtag/commands.h>
+#include <jtag/interface.h>
+
+#include "esirisc_jtag.h"
+
+static void esirisc_jtag_set_instr(struct esirisc_jtag *jtag_info, uint32_t new_instr)
+{
+ struct jtag_tap *tap = jtag_info->tap;
+
+ if (buf_get_u32(tap->cur_instr, 0, tap->ir_length) != new_instr) {
+ struct scan_field field;
+ uint8_t t[4];
+
+ field.num_bits = tap->ir_length;
+ field.out_value = t;
+ buf_set_u32(t, 0, field.num_bits, new_instr);
+ field.in_value = NULL;
+
+ jtag_add_ir_scan(tap, &field, TAP_IDLE);
+ }
+}
+
+/*
+ * The data register is latched every 8 bits while in the Shift-DR state
+ * (Update-DR is not supported). This necessitates prepending padding
+ * bits to ensure data is aligned when multiple TAPs are present.
+ */
+static int esirisc_jtag_get_padding(void)
+{
+ int padding = 0;
+ int bypass_devices = 0;
+
+ for (struct jtag_tap *tap = jtag_tap_next_enabled(NULL); tap != NULL;
+ tap = jtag_tap_next_enabled(tap))
+ if (tap->bypass)
+ bypass_devices++;
+
+ int num_bits = bypass_devices % 8;
+ if (num_bits > 0)
+ padding = 8 - num_bits;
+
+ return padding;
+}
+
+static int esirisc_jtag_count_bits(int num_fields, struct scan_field *fields)
+{
+ int bit_count = 0;
+
+ for (int i = 0; i < num_fields; ++i)
+ bit_count += fields[i].num_bits;
+
+ return bit_count;
+}
+
+/*
+ * Data received from the target will be byte-stuffed if it contains
+ * either the pad byte (0xAA) or stuffing marker (0x55). Buffers should
+ * be sized twice the expected length to account for stuffing overhead.
+ */
+static void esirisc_jtag_unstuff(uint8_t *data, size_t len)
+{
+ uint8_t *r, *w;
+ uint8_t *end;
+
+ r = w = data;
+ end = data + len;
+ while (r < end) {
+ if (*r == STUFF_MARKER) {
+ r++; /* skip stuffing marker */
+ assert(r < end);
+ *w++ = *r++ ^ STUFF_MARKER;
+ } else
+ *w++ = *r++;
+ }
+}
+
+/*
+ * The eSi-Debug protocol defines a byte-oriented command/response
+ * channel that operates over serial or JTAG. While not strictly
+ * required, separate DR scans are used for sending and receiving data.
+ * This allows the TAP to recover gracefully if the byte stream is
+ * corrupted at the expense of sending additional padding bits.
+ */
+
+static int esirisc_jtag_send(struct esirisc_jtag *jtag_info, uint8_t command,
+ int num_out_fields, struct scan_field *out_fields)
+{
+ int num_fields = 2 + num_out_fields;
+ struct scan_field *fields = cmd_queue_alloc(num_fields * sizeof(struct scan_field));
+
+ esirisc_jtag_set_instr(jtag_info, INSTR_DEBUG);
+
+ fields[0].num_bits = esirisc_jtag_get_padding();
+ fields[0].out_value = NULL;
+ fields[0].in_value = NULL;
+
+ fields[1].num_bits = 8;
+ fields[1].out_value = &command;
+ fields[1].in_value = NULL;
+
+ /* append command data */
+ for (int i = 0; i < num_out_fields; ++i)
+ jtag_scan_field_clone(&fields[2+i], &out_fields[i]);
+
+ jtag_add_dr_scan(jtag_info->tap, num_fields, fields, TAP_IDLE);
+
+ return jtag_execute_queue();
+}
+
+static int esirisc_jtag_recv(struct esirisc_jtag *jtag_info,
+ int num_in_fields, struct scan_field *in_fields)
+{
+ int num_in_bits = esirisc_jtag_count_bits(num_in_fields, in_fields);
+ int num_in_bytes = DIV_ROUND_UP(num_in_bits, 8);
+
+ struct scan_field fields[3];
+ uint8_t r[num_in_bytes * 2];
+
+ esirisc_jtag_set_instr(jtag_info, INSTR_DEBUG);
+
+ fields[0].num_bits = esirisc_jtag_get_padding() + 1;
+ fields[0].out_value = NULL;
+ fields[0].in_value = NULL;
+
+ fields[1].num_bits = 8;
+ fields[1].out_value = NULL;
+ fields[1].in_value = &jtag_info->status;
+
+ fields[2].num_bits = num_in_bits * 2;
+ fields[2].out_value = NULL;
+ fields[2].in_value = r;
+
+ jtag_add_dr_scan(jtag_info->tap, ARRAY_SIZE(fields), fields, TAP_IDLE);
+
+ int retval = jtag_execute_queue();
+ if (retval != ERROR_OK)
+ return retval;
+
+ /* unstuff response data and write back to caller */
+ if (num_in_fields > 0) {
+ esirisc_jtag_unstuff(r, ARRAY_SIZE(r));
+
+ int bit_count = 0;
+ for (int i = 0; i < num_in_fields; ++i) {
+ buf_set_buf(r, bit_count, in_fields[i].in_value, 0, in_fields[i].num_bits);
+ bit_count += in_fields[i].num_bits;
+ }
+ }
+
+ return ERROR_OK;
+}
+
+static int esirisc_jtag_check_status(struct esirisc_jtag *jtag_info)
+{
+ uint8_t eid = esirisc_jtag_get_eid(jtag_info);
+ if (eid != EID_NONE) {
+ LOG_ERROR("esirisc_jtag: bad status: 0x%02" PRIx32 " (DA: %" PRId32 ", "
+ "S: %" PRId32 ", EID: 0x%02" PRIx32 ")",
+ jtag_info->status, esirisc_jtag_is_debug_active(jtag_info),
+ esirisc_jtag_is_stopped(jtag_info), eid);
+ return ERROR_FAIL;
+ }
+
+ return ERROR_OK;
+}
+
+static int esirisc_jtag_send_and_recv(struct esirisc_jtag *jtag_info, uint8_t command,
+ int num_out_fields, struct scan_field *out_fields,
+ int num_in_fields, struct scan_field *in_fields)
+{
+ int retval;
+
+ jtag_info->status = 0; /* clear status */
+
+ retval = esirisc_jtag_send(jtag_info, command, num_out_fields, out_fields);
+ if (retval != ERROR_OK) {
+ LOG_ERROR("esirisc_jtag: send failed (command: 0x%02" PRIx32 ")", command);
+ return ERROR_FAIL;
+ }
+
+ retval = esirisc_jtag_recv(jtag_info, num_in_fields, in_fields);
+ if (retval != ERROR_OK) {
+ LOG_ERROR("esirisc_jtag: recv failed (command: 0x%02" PRIx32 ")", command);
+ return ERROR_FAIL;
+ }
+
+ return esirisc_jtag_check_status(jtag_info);
+}
+
+/*
+ * Status is automatically updated after each command completes;
+ * these functions make each field available to the caller.
+ */
+
+bool esirisc_jtag_is_debug_active(struct esirisc_jtag *jtag_info)
+{
+ return !!(jtag_info->status & 1<<7); /* DA */
+}
+
+bool esirisc_jtag_is_stopped(struct esirisc_jtag *jtag_info)
+{
+ return !!(jtag_info->status & 1<<6); /* S */
+}
+
+uint8_t esirisc_jtag_get_eid(struct esirisc_jtag *jtag_info)
+{
+ return jtag_info->status & 0x3f; /* EID */
+}
+
+/*
+ * Most commands manipulate target data (eg. memory and registers); each
+ * command returns a status byte that indicates success. Commands must
+ * transmit multibyte values in big-endian order, however response
+ * values are in little-endian order. Target endianness does not have an
+ * effect on this ordering.
+ */
+
+int esirisc_jtag_read_byte(struct esirisc_jtag *jtag_info, uint32_t address, uint8_t *data)
+{
+ struct scan_field out_fields[1];
+ uint8_t a[4];
+
+ out_fields[0].num_bits = 32;
+ out_fields[0].out_value = a;
+ h_u32_to_be(a, address);
+ out_fields[0].in_value = NULL;
+
+ struct scan_field in_fields[1];
+ uint8_t d[1];
+
+ in_fields[0].num_bits = 8;
+ in_fields[0].out_value = NULL;
+ in_fields[0].in_value = d;
+
+ int retval = esirisc_jtag_send_and_recv(jtag_info, DEBUG_READ_BYTE,
+ ARRAY_SIZE(out_fields), out_fields, ARRAY_SIZE(in_fields), in_fields);
+ if (retval != ERROR_OK)
+ return retval;
+
+ *data = *d;
+
+ return ERROR_OK;
+}
+
+int esirisc_jtag_read_hword(struct esirisc_jtag *jtag_info, uint32_t address, uint16_t *data)
+{
+ struct scan_field out_fields[1];
+ uint8_t a[4];
+
+ out_fields[0].num_bits = 32;
+ out_fields[0].out_value = a;
+ h_u32_to_be(a, address);
+ out_fields[0].in_value = NULL;
+
+ struct scan_field in_fields[1];
+ uint8_t d[2];
+
+ in_fields[0].num_bits = 16;
+ in_fields[0].out_value = NULL;
+ in_fields[0].in_value = d;
+
+ int retval = esirisc_jtag_send_and_recv(jtag_info, DEBUG_READ_HWORD,
+ ARRAY_SIZE(out_fields), out_fields, ARRAY_SIZE(in_fields), in_fields);
+ if (retval != ERROR_OK)
+ return retval;
+
+ *data = le_to_h_u16(d);
+
+ return ERROR_OK;
+}
+
+int esirisc_jtag_read_word(struct esirisc_jtag *jtag_info, uint32_t address, uint32_t *data)
+{
+ struct scan_field out_fields[1];
+ uint8_t a[4];
+
+ out_fields[0].num_bits = 32;
+ out_fields[0].out_value = a;
+ h_u32_to_be(a, address);
+ out_fields[0].in_value = NULL;
+
+ struct scan_field in_fields[1];
+ uint8_t d[4];
+
+ in_fields[0].num_bits = 32;
+ in_fields[0].out_value = NULL;
+ in_fields[0].in_value = d;
+
+ int retval = esirisc_jtag_send_and_recv(jtag_info, DEBUG_READ_WORD,
+ ARRAY_SIZE(out_fields), out_fields, ARRAY_SIZE(in_fields), in_fields);
+ if (retval != ERROR_OK)
+ return retval;
+
+ *data = le_to_h_u32(d);
+
+ return ERROR_OK;
+}
+
+int esirisc_jtag_write_byte(struct esirisc_jtag *jtag_info, uint32_t address, uint8_t data)
+{
+ struct scan_field out_fields[2];
+ uint8_t a[4];
+
+ out_fields[0].num_bits = 32;
+ out_fields[0].out_value = a;
+ h_u32_to_be(a, address);
+ out_fields[0].in_value = NULL;
+
+ out_fields[1].num_bits = 8;
+ out_fields[1].out_value = &data;
+ out_fields[1].in_value = NULL;
+
+ return esirisc_jtag_send_and_recv(jtag_info, DEBUG_WRITE_BYTE,
+ ARRAY_SIZE(out_fields), out_fields, 0, NULL);
+}
+
+int esirisc_jtag_write_hword(struct esirisc_jtag *jtag_info, uint32_t address, uint16_t data)
+{
+ struct scan_field out_fields[2];
+ uint8_t a[4], d[2];
+
+ out_fields[0].num_bits = 32;
+ out_fields[0].out_value = a;
+ h_u32_to_be(a, address);
+ out_fields[0].in_value = NULL;
+
+ out_fields[1].num_bits = 16;
+ out_fields[1].out_value = d;
+ h_u16_to_be(d, data);
+ out_fields[1].in_value = NULL;
+
+ return esirisc_jtag_send_and_recv(jtag_info, DEBUG_WRITE_HWORD,
+ ARRAY_SIZE(out_fields), out_fields, 0, NULL);
+}
+
+int esirisc_jtag_write_word(struct esirisc_jtag *jtag_info, uint32_t address, uint32_t data)
+{
+ struct scan_field out_fields[2];
+ uint8_t a[4], d[4];
+
+ out_fields[0].num_bits = 32;
+ out_fields[0].out_value = a;
+ h_u32_to_be(a, address);
+ out_fields[0].in_value = NULL;
+
+ out_fields[1].num_bits = 32;
+ out_fields[1].out_value = d;
+ h_u32_to_be(d, data);
+ out_fields[1].in_value = NULL;
+
+ return esirisc_jtag_send_and_recv(jtag_info, DEBUG_WRITE_WORD,
+ ARRAY_SIZE(out_fields), out_fields, 0, NULL);
+}
+
+int esirisc_jtag_read_reg(struct esirisc_jtag *jtag_info, uint8_t reg, uint32_t *data)
+{
+ struct scan_field out_fields[1];
+
+ out_fields[0].num_bits = 8;
+ out_fields[0].out_value = &reg;
+ out_fields[0].in_value = NULL;
+
+ struct scan_field in_fields[1];
+ uint8_t d[4];
+
+ in_fields[0].num_bits = 32;
+ in_fields[0].out_value = NULL;
+ in_fields[0].in_value = d;
+
+ int retval = esirisc_jtag_send_and_recv(jtag_info, DEBUG_READ_REG,
+ ARRAY_SIZE(out_fields), out_fields, ARRAY_SIZE(in_fields), in_fields);
+ if (retval != ERROR_OK)
+ return retval;
+
+ *data = le_to_h_u32(d);
+
+ return ERROR_OK;
+}
+
+int esirisc_jtag_write_reg(struct esirisc_jtag *jtag_info, uint8_t reg, uint32_t data)
+{
+ struct scan_field out_fields[2];
+ uint8_t d[4];
+
+ out_fields[0].num_bits = 8;
+ out_fields[0].out_value = &reg;
+ out_fields[0].in_value = NULL;
+
+ out_fields[1].num_bits = 32;
+ out_fields[1].out_value = d;
+ h_u32_to_be(d, data);
+ out_fields[1].in_value = NULL;
+
+ return esirisc_jtag_send_and_recv(jtag_info, DEBUG_WRITE_REG,
+ ARRAY_SIZE(out_fields), out_fields, 0, NULL);
+}
+
+int esirisc_jtag_read_csr(struct esirisc_jtag *jtag_info, uint8_t bank, uint8_t csr, uint32_t *data)
+{
+ struct scan_field out_fields[1];
+ uint8_t c[2];
+
+ out_fields[0].num_bits = 16;
+ out_fields[0].out_value = c;
+ h_u16_to_be(c, (csr << 5) | bank);
+ out_fields[0].in_value = NULL;
+
+ struct scan_field in_fields[1];
+ uint8_t d[4];
+
+ in_fields[0].num_bits = 32;
+ in_fields[0].out_value = NULL;
+ in_fields[0].in_value = d;
+
+ int retval = esirisc_jtag_send_and_recv(jtag_info, DEBUG_READ_CSR,
+ ARRAY_SIZE(out_fields), out_fields, ARRAY_SIZE(in_fields), in_fields);
+ if (retval != ERROR_OK)
+ return retval;
+
+ *data = le_to_h_u32(d);
+
+ return ERROR_OK;
+}
+
+int esirisc_jtag_write_csr(struct esirisc_jtag *jtag_info, uint8_t bank, uint8_t csr, uint32_t data)
+{
+ struct scan_field out_fields[2];
+ uint8_t c[2], d[4];
+
+ out_fields[0].num_bits = 16;
+ out_fields[0].out_value = c;
+ h_u16_to_be(c, (csr << 5) | bank);
+ out_fields[0].in_value = NULL;
+
+ out_fields[1].num_bits = 32;
+ out_fields[1].out_value = d;
+ h_u32_to_be(d, data);
+ out_fields[1].in_value = NULL;
+
+ return esirisc_jtag_send_and_recv(jtag_info, DEBUG_WRITE_CSR,
+ ARRAY_SIZE(out_fields), out_fields, 0, NULL);
+}
+
+/*
+ * Control commands affect CPU operation; these commands send no data
+ * and return a status byte.
+ */
+
+static inline int esirisc_jtag_send_ctrl(struct esirisc_jtag *jtag_info, uint8_t command)
+{
+ return esirisc_jtag_send_and_recv(jtag_info, command, 0, NULL, 0, NULL);
+}
+
+int esirisc_jtag_enable_debug(struct esirisc_jtag *jtag_info)
+{
+ return esirisc_jtag_send_ctrl(jtag_info, DEBUG_ENABLE_DEBUG);
+}
+
+int esirisc_jtag_disable_debug(struct esirisc_jtag *jtag_info)
+{
+ return esirisc_jtag_send_ctrl(jtag_info, DEBUG_DISABLE_DEBUG);
+}
+
+int esirisc_jtag_assert_reset(struct esirisc_jtag *jtag_info)
+{
+ return esirisc_jtag_send_ctrl(jtag_info, DEBUG_ASSERT_RESET);
+}
+
+int esirisc_jtag_deassert_reset(struct esirisc_jtag *jtag_info)
+{
+ return esirisc_jtag_send_ctrl(jtag_info, DEBUG_DEASSERT_RESET);
+}
+
+int esirisc_jtag_break(struct esirisc_jtag *jtag_info)
+{
+ return esirisc_jtag_send_ctrl(jtag_info, DEBUG_BREAK);
+}
+
+int esirisc_jtag_continue(struct esirisc_jtag *jtag_info)
+{
+ return esirisc_jtag_send_ctrl(jtag_info, DEBUG_CONTINUE);
+}
+
+int esirisc_jtag_flush_caches(struct esirisc_jtag *jtag_info)
+{
+ return esirisc_jtag_send_ctrl(jtag_info, DEBUG_FLUSH_CACHES);
+}