This is an automated email from Gerrit. Matthias Welwarsky (matth...@welwarsky.de) just uploaded a new patch set to Gerrit, which you can find at http://openocd.zylin.com/3166
-- gerrit commit 865b6451c9064496f56da633969c93009b4be2b1 Author: Matthias Welwarsky <matth...@welwarsky.de> Date: Thu Dec 10 22:43:21 2015 +0100 RFD: DAP WAIT support (WIP) Don't nitpick, just look at the concept and suggest changes. Change-Id: I2790070388cf1ab2e8c9a042d74eb3ef776aa583 Signed-off-by: Matthias Welwarsky <matth...@welwarsky.de> diff --git a/src/target/adi_v5_jtag.c b/src/target/adi_v5_jtag.c index b71fe38..8224816 100644 --- a/src/target/adi_v5_jtag.c +++ b/src/target/adi_v5_jtag.c @@ -39,6 +39,7 @@ #include "arm.h" #include "arm_adi_v5.h" #include <helper/time_support.h> +#include <helper/list.h> /* JTAG instructions/registers for JTAG-DP and SWJ-DP */ #define JTAG_DP_ABORT 0x8 @@ -52,12 +53,158 @@ static int jtag_ap_q_abort(struct adiv5_dap *dap, uint8_t *ack); +static const char *dap_reg_name(int instr, int reg_addr) +{ + char *reg_name = "UNK"; + + if (instr == JTAG_DP_DPACC) { + switch (reg_addr) { + case DP_ABORT: + reg_name = "ABORT"; + break; + case DP_CTRL_STAT: + reg_name = "CTRL/STAT"; + break; + case DP_SELECT: + reg_name = "SELECT"; + break; + case DP_RDBUFF: + reg_name = "RDBUFF"; + break; + case DP_WCR: + reg_name = "WCR"; + break; + default: + reg_name = "UNK"; + break; + } + } + + if (instr == JTAG_DP_APACC) { + switch (reg_addr) { + case MEM_AP_REG_CSW: + reg_name = "CSW"; + break; + case MEM_AP_REG_TAR: + reg_name = "TAR"; + break; + case MEM_AP_REG_DRW: + reg_name = "DRW"; + break; + case MEM_AP_REG_BD0: + reg_name = "BD0"; + break; + case MEM_AP_REG_BD1: + reg_name = "BD1"; + break; + case MEM_AP_REG_BD2: + reg_name = "BD2"; + break; + case MEM_AP_REG_BD3: + reg_name = "BD3"; + break; + case MEM_AP_REG_CFG: + reg_name = "CFG"; + break; + case MEM_AP_REG_BASE: + reg_name = "BASE"; + break; + case AP_REG_IDR: + reg_name = "IDR"; + break; + default: + reg_name = "UNK"; + break; + } + } + + return reg_name; +} + +struct dap_cmd { + struct list_head lh; + uint8_t instr; + uint8_t reg_addr; + uint8_t RnW; + uint8_t *invalue; + uint8_t outvalue[4]; + uint8_t ack; + uint32_t memaccess_tck; + + struct scan_field fields[2]; + uint8_t out_addr_buf; +}; + +static struct dap_cmd *dap_cmd_new(void) +{ + struct dap_cmd *cmd; + + cmd = (struct dap_cmd *)calloc(1, sizeof(struct dap_cmd)); + if (cmd != NULL) + INIT_LIST_HEAD(&cmd->lh); + + return cmd; +} + +static void flush_journal(struct list_head *lh) +{ + struct dap_cmd *el, *tmp; + + list_for_each_entry_safe(el, tmp, lh, lh) { + list_del(&el->lh); + free(el); + } +} + /*************************************************************************** * * DPACC and APACC scanchain access through JTAG-DP (or SWJ-DP) * ***************************************************************************/ +static int adi_jtag_dp_scan_cmd(struct adiv5_dap *dap, struct dap_cmd *cmd) +{ + struct jtag_tap *tap = dap->tap; + int retval; + + retval = arm_jtag_set_instr(tap, cmd->instr, NULL, TAP_IDLE); + if (retval != ERROR_OK) + return retval; + + /* Scan out a read or write operation using some DP or AP register. + * For APACC access with any sticky error flag set, this is discarded. + */ + cmd->fields[0].num_bits = 3; + buf_set_u32(&cmd->out_addr_buf, 0, 3, ((cmd->reg_addr >> 1) & 0x6) | (cmd->RnW & 0x1)); + cmd->fields[0].out_value = &cmd->out_addr_buf; + cmd->fields[0].in_value = &cmd->ack; + + /* NOTE: if we receive JTAG_ACK_WAIT, the previous operation did not + * complete; data we write is discarded, data we read is unpredictable. + * When overrun detect is active, STICKYORUN is set. + */ + + cmd->fields[1].num_bits = 32; + cmd->fields[1].out_value = cmd->outvalue; + cmd->fields[1].in_value = cmd->invalue; + + jtag_add_dr_scan(tap, 2, cmd->fields, TAP_IDLE); + + /* Add specified number of tck clocks after starting memory bus + * access, giving the hardware time to complete the access. + * They provide more time for the (MEM) AP to complete the read ... + * See "Minimum Response Time" for JTAG-DP, in the ADIv5 spec. + */ + if ((cmd->instr == JTAG_DP_APACC) + && ((cmd->reg_addr == MEM_AP_REG_DRW) + || ((cmd->reg_addr & 0xF0) == MEM_AP_REG_BD0)) + && (cmd->memaccess_tck != 0)) + jtag_add_runtest(cmd->memaccess_tck, + TAP_IDLE); + + return ERROR_OK; +} + /** * Scan DPACC or APACC using target ordered uint8_t buffers. No endianness * conversions are performed. See section 4.4.3 of the ADIv5 spec, which @@ -75,54 +222,32 @@ static int jtag_ap_q_abort(struct adiv5_dap *dap, uint8_t *ack); * @param outvalue points to a 32-bit (little-endian) integer * @param invalue NULL, or points to a 32-bit (little-endian) integer * @param ack points to where the three bit JTAG_ACK_* code will be stored - * @param memaccess_tck number of idle cycles to add after AP access */ static int adi_jtag_dp_scan(struct adiv5_dap *dap, uint8_t instr, uint8_t reg_addr, uint8_t RnW, - uint8_t *outvalue, uint8_t *invalue, uint8_t *ack, + uint8_t *outvalue, uint8_t *invalue, uint32_t memaccess_tck) { - struct jtag_tap *tap = dap->tap; - struct scan_field fields[2]; - uint8_t out_addr_buf; + struct dap_cmd *cmd; int retval; - retval = arm_jtag_set_instr(tap, instr, NULL, TAP_IDLE); - if (retval != ERROR_OK) - return retval; - - /* Scan out a read or write operation using some DP or AP register. - * For APACC access with any sticky error flag set, this is discarded. - */ - fields[0].num_bits = 3; - buf_set_u32(&out_addr_buf, 0, 3, ((reg_addr >> 1) & 0x6) | (RnW & 0x1)); - fields[0].out_value = &out_addr_buf; - fields[0].in_value = ack; - - /* NOTE: if we receive JTAG_ACK_WAIT, the previous operation did not - * complete; data we write is discarded, data we read is unpredictable. - * When overrun detect is active, STICKYORUN is set. - */ - - fields[1].num_bits = 32; - fields[1].out_value = outvalue; - fields[1].in_value = invalue; - - jtag_add_dr_scan(tap, 2, fields, TAP_IDLE); + cmd = dap_cmd_new(); + if (cmd != NULL) { + cmd->instr = instr; + cmd->reg_addr = reg_addr; + cmd->RnW = RnW; + memcpy(cmd->outvalue, outvalue, 4); + cmd->invalue = invalue; + cmd->memaccess_tck = memaccess_tck; + } else + return ERROR_JTAG_DEVICE_ERROR; - /* Add specified number of tck clocks after starting memory bus - * access, giving the hardware time to complete the access. - * They provide more time for the (MEM) AP to complete the read ... - * See "Minimum Response Time" for JTAG-DP, in the ADIv5 spec. - */ - if ((instr == JTAG_DP_APACC) - && ((reg_addr == MEM_AP_REG_DRW) - || ((reg_addr & 0xF0) == MEM_AP_REG_BD0)) - && memaccess_tck != 0) - jtag_add_runtest(memaccess_tck, TAP_IDLE); + retval = adi_jtag_dp_scan_cmd(dap, cmd); + if (retval == ERROR_OK) + list_add_tail(&cmd->lh, &dap->cmd_journal); - return ERROR_OK; + return retval; } /** @@ -133,7 +258,7 @@ static int adi_jtag_dp_scan(struct adiv5_dap *dap, */ static int adi_jtag_dp_scan_u32(struct adiv5_dap *dap, uint8_t instr, uint8_t reg_addr, uint8_t RnW, - uint32_t outvalue, uint32_t *invalue, uint8_t *ack, + uint32_t outvalue, uint32_t *invalue, uint32_t memaccess_tck) { uint8_t out_value_buf[4]; @@ -142,7 +267,7 @@ static int adi_jtag_dp_scan_u32(struct adiv5_dap *dap, buf_set_u32(out_value_buf, 0, 32, outvalue); retval = adi_jtag_dp_scan(dap, instr, reg_addr, RnW, - out_value_buf, (uint8_t *)invalue, ack, memaccess_tck); + out_value_buf, (uint8_t *)invalue, memaccess_tck); if (retval != ERROR_OK) return retval; @@ -160,7 +285,7 @@ static inline int adi_jtag_ap_write_check(struct adiv5_ap *ap, uint8_t reg_addr, uint8_t *outvalue) { return adi_jtag_dp_scan(ap->dap, JTAG_DP_APACC, reg_addr, DPAP_WRITE, - outvalue, NULL, NULL, ap->memaccess_tck); + outvalue, NULL, ap->memaccess_tck); } static int adi_jtag_scan_inout_check_u32(struct adiv5_dap *dap, @@ -171,7 +296,7 @@ static int adi_jtag_scan_inout_check_u32(struct adiv5_dap *dap, /* Issue the read or write */ retval = adi_jtag_dp_scan_u32(dap, instr, reg_addr, - RnW, outvalue, NULL, NULL, memaccess_tck); + RnW, outvalue, NULL, memaccess_tck); if (retval != ERROR_OK) return retval; @@ -180,7 +305,7 @@ static int adi_jtag_scan_inout_check_u32(struct adiv5_dap *dap, */ if ((RnW == DPAP_READ) && (invalue != NULL)) retval = adi_jtag_dp_scan_u32(dap, JTAG_DP_DPACC, - DP_RDBUFF, DPAP_READ, 0, invalue, &dap->ack, 0); + DP_RDBUFF, DPAP_READ, 0, invalue, 0); return retval; } @@ -188,6 +313,8 @@ static int jtagdp_transaction_endcheck(struct adiv5_dap *dap) { int retval; uint32_t ctrlstat; + struct dap_cmd *el, *tmp; + LIST_HEAD(replay_list); /* too expensive to call keep_alive() here */ @@ -216,6 +343,22 @@ static int jtagdp_transaction_endcheck(struct adiv5_dap *dap) * before the RC oscillator phase is not yet complete. */ + /* make sure all queued transactions are complete */ + retval = jtag_execute_queue(); + if (retval != ERROR_OK) + return retval; + + /* move all transactions over to the replay list */ + list_for_each_entry_safe(el, tmp, &dap->cmd_journal, lh) { + LOG_DEBUG("LOG: %2s %6s %5s 0x%08x %2s", + el->instr == JTAG_DP_APACC ? "AP" : "DP", + dap_reg_name(el->instr, el->reg_addr), + el->RnW == DPAP_READ ? "READ" : "WRITE", + el->RnW == DPAP_WRITE ? buf_get_u32(el->outvalue, 0, 32) : (el->invalue ? *(el->invalue) : (uint32_t)-1), + el->ack == JTAG_ACK_OK_FAULT ? "OK" : "WAIT"); + list_move_tail(&el->lh, &replay_list); + } + /* Post CTRL/STAT read; discard any previous posted read value * but collect its ACK status. */ @@ -227,8 +370,80 @@ static int jtagdp_transaction_endcheck(struct adiv5_dap *dap) if (retval != ERROR_OK) return retval; - dap->ack = dap->ack & 0x7; + LOG_DEBUG("CTRL/STAT %" PRIx32, ctrlstat); + + /* Check for STICKYERR */ + if (ctrlstat & SSTICKYERR) { + LOG_DEBUG("jtag-dp: CTRL/STAT error, 0x%" PRIx32, ctrlstat); + /* Check power to debug regions */ + if ((ctrlstat & 0x30000000) != 0x30000000) { + LOG_ERROR("CDBGPWRxxx signals are off, debug regions are not powered"); + retval = ERROR_JTAG_DEVICE_ERROR; + goto done; + } + + if (ctrlstat & SSTICKYERR) + LOG_ERROR("JTAG-DP STICKY ERROR"); + + /* Clear Sticky Error Bits */ + retval = adi_jtag_scan_inout_check_u32(dap, JTAG_DP_DPACC, + DP_CTRL_STAT, DPAP_WRITE, + dap->dp_ctrl_stat | SSTICKYERR, NULL, 0); + if (retval != ERROR_OK) + goto done; + + retval = adi_jtag_scan_inout_check_u32(dap, JTAG_DP_DPACC, + DP_CTRL_STAT, DPAP_READ, 0, &ctrlstat, 0); + if (retval != ERROR_OK) + goto done; + + retval = jtag_execute_queue(); + if (retval != ERROR_OK) + goto done; + + LOG_DEBUG("jtag-dp: CTRL/STAT 0x%" PRIx32, ctrlstat); + + retval = ERROR_JTAG_DEVICE_ERROR; + goto done; + } + + /* check for overrun condition in the last batch of transactions */ + if (ctrlstat & SSTICKYORUN) { + /* Clear STICKYORUN bit in order to replay the data */ + ctrlstat |= SSTICKYORUN; + retval = adi_jtag_scan_inout_check_u32(dap, JTAG_DP_DPACC, + DP_CTRL_STAT, DPAP_WRITE, ctrlstat, NULL, 0); + if (retval != ERROR_OK) + goto done; + retval = adi_jtag_scan_inout_check_u32(dap, JTAG_DP_DPACC, + DP_CTRL_STAT, DPAP_READ, 0, &ctrlstat, 0); + if (retval != ERROR_OK) + goto done; + retval = jtag_execute_queue(); + if (retval != ERROR_OK) + goto done; + + LOG_DEBUG("Recover: CTRL/STAT %" PRIx32, ctrlstat); + + list_for_each_entry_safe(el, tmp, &replay_list, lh) { + LOG_DEBUG("REC: %2s %6s %5s 0x%08x %2s", + el->instr == JTAG_DP_APACC ? "AP" : "DP", + dap_reg_name(el->instr, el->reg_addr), + el->RnW == DPAP_READ ? "READ" : "WRITE", + el->RnW == DPAP_WRITE ? buf_get_u32(el->outvalue, 0, 32) : (el->invalue ? *(el->invalue) : (uint32_t)-1), + el->ack == JTAG_ACK_OK_FAULT ? "OK" : "WAIT"); + + do { + adi_jtag_dp_scan_cmd(dap, el); + jtag_execute_queue(); + } while (el->ack == JTAG_ACK_WAIT); + list_del(&el->lh); + free(el); + } + } + +#if 0 /* common code path avoids calling timeval_ms() */ if (dap->ack != JTAG_ACK_OK_FAULT) { long long then = timeval_ms(); @@ -248,6 +463,7 @@ static int jtagdp_transaction_endcheck(struct adiv5_dap *dap) return ERROR_JTAG_DEVICE_ERROR; } + LOG_DEBUG("WAIT"); } else { LOG_WARNING("Invalid ACK %#x " "in JTAG-DP transaction", @@ -255,39 +471,9 @@ static int jtagdp_transaction_endcheck(struct adiv5_dap *dap) return ERROR_JTAG_DEVICE_ERROR; } + ctrlstat |= SSTICKYERR | SSTICKYORUN; retval = adi_jtag_scan_inout_check_u32(dap, JTAG_DP_DPACC, - DP_CTRL_STAT, DPAP_READ, 0, &ctrlstat, 0); - if (retval != ERROR_OK) - return retval; - retval = jtag_execute_queue(); - if (retval != ERROR_OK) - return retval; - dap->ack = dap->ack & 0x7; - } - } - - /* REVISIT also STICKYCMP, for pushed comparisons (nyet used) */ - - /* Check for STICKYERR and STICKYORUN */ - if (ctrlstat & (SSTICKYORUN | SSTICKYERR)) { - LOG_DEBUG("jtag-dp: CTRL/STAT error, 0x%" PRIx32, ctrlstat); - /* Check power to debug regions */ - if ((ctrlstat & 0xf0000000) != 0xf0000000) { - LOG_ERROR("Debug regions are unpowered, an unexpected reset might have happened"); - return ERROR_JTAG_DEVICE_ERROR; - } else { - if (ctrlstat & SSTICKYORUN) - LOG_ERROR("JTAG-DP OVERRUN - check clock, " - "memaccess, or reduce jtag speed"); - - if (ctrlstat & SSTICKYERR) - LOG_ERROR("JTAG-DP STICKY ERROR"); - - /* Clear Sticky Error Bits */ - retval = adi_jtag_scan_inout_check_u32(dap, JTAG_DP_DPACC, - DP_CTRL_STAT, DPAP_WRITE, - dap->dp_ctrl_stat | SSTICKYORUN - | SSTICKYERR, NULL, 0); + DP_CTRL_STAT, DPAP_WRITE, 0, &ctrlstat); if (retval != ERROR_OK) return retval; retval = adi_jtag_scan_inout_check_u32(dap, JTAG_DP_DPACC, @@ -297,16 +483,16 @@ static int jtagdp_transaction_endcheck(struct adiv5_dap *dap) retval = jtag_execute_queue(); if (retval != ERROR_OK) return retval; - - LOG_DEBUG("jtag-dp: CTRL/STAT 0x%" PRIx32, ctrlstat); + dap->ack = dap->ack & 0x7; } - retval = jtag_execute_queue(); - if (retval != ERROR_OK) - return retval; - return ERROR_JTAG_DEVICE_ERROR; } - - return ERROR_OK; +#endif + /* REVISIT also STICKYCMP, for pushed comparisons (nyet used) */ + done: + dap->ack = 0; + flush_journal(&replay_list); + flush_journal(&dap->cmd_journal); + return retval; } /*--------------------------------------------------------------------------*/ @@ -369,7 +555,7 @@ static int jtag_ap_q_abort(struct adiv5_dap *dap, uint8_t *ack) { /* for JTAG, this is the only valid ABORT register operation */ return adi_jtag_dp_scan_u32(dap, JTAG_DP_ABORT, - 0, DPAP_WRITE, 1, NULL, ack, 0); + 0, DPAP_WRITE, 1, NULL, 0); } static int jtag_dp_run(struct adiv5_dap *dap) diff --git a/src/target/arm_adi_v5.c b/src/target/arm_adi_v5.c index 02a91e3..8e212b4 100644 --- a/src/target/arm_adi_v5.c +++ b/src/target/arm_adi_v5.c @@ -76,6 +76,7 @@ #include "arm.h" #include "arm_adi_v5.h" #include <helper/time_support.h> +#include <helper/list.h> /* ARM ADI Specification requires at least 10 bits used for TAR autoincrement */ @@ -586,6 +587,7 @@ struct adiv5_dap *dap_init(void) /* Number of bits for tar autoincrement, impl. dep. at least 10 */ dap->ap[i].tar_autoincr_block = (1<<10); } + INIT_LIST_HEAD(&dap->cmd_journal); return dap; } diff --git a/src/target/arm_adi_v5.h b/src/target/arm_adi_v5.h index fed25ec..b296d65 100644 --- a/src/target/arm_adi_v5.h +++ b/src/target/arm_adi_v5.h @@ -31,6 +31,7 @@ * resources accessed through a MEM-AP. */ +#include <helper/list.h> #include "arm_jtag.h" /* FIXME remove these JTAG-specific decls when mem_ap_read_buf_u32() @@ -206,6 +207,8 @@ struct adiv5_ap { struct adiv5_dap { const struct dap_ops *ops; + struct list_head cmd_journal; + struct jtag_tap *tap; /* Control config */ uint32_t dp_ctrl_stat; -- ------------------------------------------------------------------------------ _______________________________________________ OpenOCD-devel mailing list OpenOCD-devel@lists.sourceforge.net https://lists.sourceforge.net/lists/listinfo/openocd-devel