/* * $Id$ * * SWD Transport Core Body File for OpenOCD. * * Copyright (C) 2011-2012 Tomasz Boleslaw CEDRO * cederom@tlen.pl, http://www.tomek.cedro.info * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * 3. Neither the name of the Tomasz Boleslaw CEDRO nor the names of its * contributors may be used to endorse or promote products derived from this * software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED * OF THE POSSIBILITY OF SUCH DAMAGE.* * * Written by Tomasz Boleslaw CEDRO , 2010-2011; * */ /** \file swd_core.c SWD Transport Core Body File for OpenOCD. * SWD Transport Layer creates bridge between target and the interface driver * functions. Target functions create high level operations on the device's * DAP (Debug Access Port), while interface driver passes electrical signals * in and out of the physical device. Transport is implemented using LibSWD, * and external open-source SWD framework. */ #include ///Unfortunalety OpenOCD use globals to pass information so we need to use it too. extern struct jtag_interface *jtag_interface; /** @{ swd_arm_adi_v5 Function set to support existing ARM ADI v5 target's * infrastructure. */ int oocd_swd_queue_idcode_read(struct adiv5_dap *dap, uint8_t *ack, uint32_t *data){ int retval; retval=swd_dp_read_idcode(dap->ctx, SWD_OPERATION_ENQUEUE, (int**) &data); if (retval<0) { LOG_ERROR("swd_dp_read_idcode() error: %s ", swd_error_string(retval)); return ERROR_FAIL; } else return ERROR_OK; } int oocd_swd_queue_dp_read(struct adiv5_dap *dap, unsigned reg, uint32_t *data){ int retval, *rdata; retval=swd_dp_read((swd_ctx_t *)dap->ctx, SWD_OPERATION_ENQUEUE, reg, &rdata); if (retval<0){ LOG_ERROR("swd_dp_read() error: %s ", swd_error_string(retval)); return ERROR_FAIL; } if (data!=NULL) data=(uint32_t*)rdata; return ERROR_OK; } int oocd_swd_queue_dp_write(struct adiv5_dap *dap, unsigned reg, uint32_t data){ int retval; retval=swd_dp_write((swd_ctx_t *)dap->ctx, SWD_OPERATION_ENQUEUE, (char) reg, (int *) &data); if (retval<0){ LOG_ERROR("swd_dp_write() error: %s ", swd_error_string(retval)); return ERROR_FAIL; } return ERROR_OK; } int oocd_swd_queue_ap_read(struct adiv5_dap *dap, unsigned reg, uint32_t *data){ int retval, *rdata; retval=swd_ap_read((swd_ctx_t *)dap->ctx, SWD_OPERATION_ENQUEUE, (char) reg, &rdata); if (retval<0){ LOG_ERROR("swd_ap_read() error: %s ", swd_error_string(retval)); return ERROR_FAIL; } if (data!=NULL) data=(uint32_t*)rdata; return ERROR_OK; } int oocd_swd_queue_ap_write(struct adiv5_dap *dap, unsigned reg, uint32_t data){ int retval; retval=swd_ap_write((swd_ctx_t *)dap->ctx, SWD_OPERATION_ENQUEUE, (char) reg, (int *) &data); if (retval<0){ LOG_ERROR("swd_ap_write() error: %s ", swd_error_string(retval)); return ERROR_FAIL; } return ERROR_OK; } int oocd_swd_queue_ap_abort(struct adiv5_dap *dap, uint8_t *ack){ int retval; int abort_flags = SWD_DP_ABORT_ORUNERRCLR | SWD_DP_ABORT_WDERRCLR | SWD_DP_ABORT_STKERRCLR \ | SWD_DP_ABORT_STKCMPCLR | SWD_DP_ABORT_DAPABORT; retval=swd_dp_write((swd_ctx_t *)dap->ctx, SWD_OPERATION_ENQUEUE, SWD_DP_ABORT_ADDR, &abort_flags); if (retval<0){ LOG_ERROR("swd_dp_write(ABORT) error: %s ", swd_error_string(retval)); return ERROR_FAIL; } return ERROR_OK; } /** This function flushes all enqueued operations into a hardware interface. * swd_cmdq_flush() is called that calls swd_drv_transmit() which is using * application specific drivers that are linked into target application binary. * Because in SWD each operation is confirmed by Target with ACK answer * we need to react on errors here. OpenOCD was constructed for use with JTAG * and most functions use series of enqueue functions that are later flushed * into a hardware interface with high level dap_run() / oocd_swd_run(), so * this is the only sensible place to place error handling (otherwise code * would need to be changed in lots of places). Caller function simply want * to know if transfer succeeded, so we can perform handling such as retry * on ACK=WAIT unless transfer fail with ACK={FAIL, UNKNOWN}. */ int oocd_swd_run(struct adiv5_dap *dap){ int retval, elmcnt=0; swd_ctx_t *swdctx = (swd_ctx_t *)dap->ctx; // This is risky to loop forever... but lets give it a try. while (1) { retval=swd_cmdq_flush(swdctx, &swdctx->cmdq, SWD_OPERATION_EXECUTE); if (retval<0) { LOG_INFO("==========oocd_swd_run(): swd_cmdq_flush() error: %s, trying to recover...", swd_error_string(retval)); retval = swd_error_handle((swd_ctx_t*)dap->ctx); if (retval==SWD_ERROR_UNHANDLED){ printf("UNANDLED EXCEPTION!\n"); sleep(100); return retval; } } else elmcnt+=retval; // retval==0 when nothing more to send. if (retval==0) break; } return ERROR_OK; } // Transport select prepares selected transport for later use and bus/target initialization. // TODO: We are operating on global interface pointer, change it into function parameter asap. int oocd_swd_transport_init(struct command_context *ctx){ LOG_DEBUG("entering function..."); int retval, *idcode; struct target *target = get_current_target(ctx); struct arm *arm = target_to_arm(target); struct adiv5_dap *dap = arm->dap; dap->ops=&oocd_dap_ops_swd; // Create SWD_CTX if nesessary if (!dap->ctx){ /** Transport was not yet initialized. */ dap->ctx=swd_init(); if (dap->ctx==NULL) { LOG_ERROR("Cannot initialize SWD context!"); return ERROR_FAIL; } LOG_INFO("New SWD context initialized at 0x%p", (void *)dap->ctx); /* Now inherit the log level from OpenOCD settings. */ retval=swd_log_level_inherit((swd_ctx_t *)dap->ctx, debug_level); if (retval<0){ LOG_ERROR("Unable to set log level: %s", swd_error_string(retval)); return ERROR_FAIL; } } else LOG_INFO("Working on existing transport context at 0x%p...", (void *)dap->ctx); /** We disable automatic truncating of queue on error */ swd_ctx_t *swdctx=(swd_ctx_t *)dap->ctx; swdctx->config.trunccmdqonerror=0; /** * Initialize driver and detect target working with selected transport. * Because we can work on existing context there is no need to destroy it, * as it can be used on next try. */ retval=swd_dap_detect((swd_ctx_t *)dap->ctx, SWD_OPERATION_EXECUTE, &idcode); if (retval<0) { LOG_ERROR("swd_dap_detect() error %d (%s)", retval, swd_error_string(retval)); return retval; } LOG_INFO("SWD transport initialization complete. Found IDCODE=0x%08X.", *idcode); return ERROR_OK; } /** * Select SWD transport on interface pointed by global *jtag_interface structure. * Select is assumed to be called before transport init. It prepares everything, * including context memory and command set for higher layers, but not hardware * and does not interrogate target device (with IDCODE read that is done by * transport init call). This function does not touch the hardware because * hardware use signals that are not yet read from config file at this point! */ int oocd_swd_transport_select(struct command_context *ctx){ LOG_DEBUG("entering function..."); int retval; jtag_interface->transport=(struct transport *)&oocd_transport_swd; retval=swd_register_commands(ctx); if (retval!=ERROR_OK){ LOG_ERROR("Unable to register SWD commands!"); return retval; } LOG_DEBUG("SWD Transport selection complete..."); return ERROR_OK; } struct transport oocd_transport_swd = { .name = "swd", .select = oocd_swd_transport_select, .init = oocd_swd_transport_init, .ctx = NULL, .next = NULL, }; const struct dap_ops oocd_dap_ops_swd = { .is_swd = true, .queue_idcode_read = oocd_swd_queue_idcode_read, .queue_dp_read = oocd_swd_queue_dp_read, .queue_dp_write = oocd_swd_queue_dp_write, .queue_ap_read = oocd_swd_queue_ap_read, .queue_ap_write = oocd_swd_queue_ap_write, .queue_ap_abort = oocd_swd_queue_ap_abort, .run = oocd_swd_run, }; /** Register SWD Transport at program startup. */ static void swd_constructor(void) __attribute__((constructor)); static void swd_constructor(void) { transport_register((struct transport *)&oocd_transport_swd); } /** Returns true if the current debug session * is using SWD as its transport. */ bool transport_is_swd(void) { return get_current_transport() == &oocd_transport_swd; } /////////////////////////////////////////////////////////////////////////////// // BELOW UGLY FUNCTIONS TO MAKE OLD THINGS WORK AND COMPILE, REMOVE THEM ASAP /////////////////////////////////////////////////////////////////////////////// #include /* * This represents the bits which must be sent out on TMS/SWDIO to * switch a DAP implemented using an SWJ-DP module into SWD mode. * These bits are stored (and transmitted) LSB-first. * * See the DAP-Lite specification, section 2.2.5 for information * about making the debug link select SWD or JTAG. (Similar info * is in a few other ARM documents.) */ static const uint8_t jtag2swd_bitseq[] = { /* More than 50 TCK/SWCLK cycles with TMS/SWDIO high, * putting both JTAG and SWD logic into reset state. */ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* Switching sequence enables SWD and disables JTAG * NOTE: bits in the DP's IDCODE may expose the need for * an old/obsolete/deprecated sequence (0xb6 0xed). */ 0x9e, 0xe7, /* More than 50 TCK/SWCLK cycles with TMS/SWDIO high, * putting both JTAG and SWD logic into reset state. */ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, }; /** * Put the debug link into SWD mode, if the target supports it. * The link's initial mode may be either JTAG (for example, * with SWJ-DP after reset) or SWD. * * @param target Enters SWD mode (if possible). * * Note that targets using the JTAG-DP do not support SWD, and that * some targets which could otherwise support it may have have been * configured to disable SWD signaling * * @return ERROR_OK or else a fault code. */ int dap_to_swd(struct target *target) { LOG_INFO("dap_to_swd()"); return ERROR_OK; } /** @} */