diff options
author | Steven Stallion <stallion@squareup.com> | 2018-08-28 17:18:01 -0700 |
---|---|---|
committer | Matthias Welwarsky <matthias@welwarsky.de> | 2018-10-16 11:58:24 +0100 |
commit | 4ab75a3634901c4e3897d771e2c75a64c7353c28 (patch) | |
tree | 475731fa20dae25c39a88804e894b69c69900e2f /src/target/esirisc_jtag.c | |
parent | e72b2601e71f49af10f72c4bb6220ee2061ef173 (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.c | 514 |
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 = ® + 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 = ® + 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); +} |