esirisc: support eSi-RISC targets
[openocd.git] / src / target / esirisc_jtag.c
diff --git a/src/target/esirisc_jtag.c b/src/target/esirisc_jtag.c
new file mode 100644 (file)
index 0000000..8ab47fa
--- /dev/null
@@ -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);
+}

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)