// SPDX-License-Identifier: GPL-2.0-or-later /*************************************************************************** * Copyright (C) 2011 by Rodrigo L. Rosa * * rodrigorosa.LG@gmail.com * * * * Based on dsp563xx_once.h written by Mathias Kuester * * mkdorg@users.sourceforge.net * ***************************************************************************/ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "target.h" #include "target_type.h" #include "dsp5680xx.h" static struct dsp5680xx_common dsp5680xx_context; #define _E "DSP5680XX_ERROR:%d\nAt:%s:%d:%s" #define err_check(r, c, m) if (r != ERROR_OK) {LOG_ERROR(_E, c, __func__, __LINE__, m); return r; } #define err_check_propagate(retval) if (retval != ERROR_OK) return retval; #define DEBUG_MSG "Debug mode be enabled to read mem." #define DEBUG_FAIL { err_check(ERROR_FAIL, DSP5680XX_ERROR_NOT_IN_DEBUG, DEBUG_MSG) } #define CHECK_DBG if (!dsp5680xx_context.debug_mode_enabled) DEBUG_FAIL #define HALT_MSG "Target must be halted." #define HALT_FAIL { err_check(ERROR_FAIL, DSP5680XX_ERROR_TARGET_RUNNING, HALT_MSG) } #define CHECK_HALT(target) if (target->state != TARGET_HALTED) HALT_FAIL #define check_halt_and_debug(target) { CHECK_HALT(target); CHECK_DBG; } static int dsp5680xx_execute_queue(void) { int retval; retval = jtag_execute_queue(); return retval; } /** * Reset state machine */ static int reset_jtag(void) { int retval; tap_state_t states[2]; const char *cp = "RESET"; states[0] = tap_state_by_name(cp); retval = jtag_add_statemove(states[0]); err_check_propagate(retval); retval = jtag_execute_queue(); err_check_propagate(retval); jtag_add_pathmove(0, states + 1); retval = jtag_execute_queue(); return retval; } static int dsp5680xx_drscan(struct target *target, uint8_t *d_in, uint8_t *d_out, int len) { /* -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- * *Inputs: * - d_in: This is the data that will be shifted into the JTAG DR reg. * - d_out: The data that will be shifted out of the JTAG DR reg will stored here * - len: Length of the data to be shifted to JTAG DR. * *Note: If d_out == NULL, discard incoming bits. * *-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- */ int retval = ERROR_OK; if (!target->tap) { retval = ERROR_FAIL; err_check(retval, DSP5680XX_ERROR_JTAG_INVALID_TAP, "Invalid tap"); } if (len > 32) { retval = ERROR_FAIL; err_check(retval, DSP5680XX_ERROR_JTAG_DR_LEN_OVERFLOW, "dr_len overflow, maximum is 32"); } /* TODO what values of len are valid for jtag_add_plain_dr_scan? */ /* can i send as many bits as i want? */ /* is the casting necessary? */ jtag_add_plain_dr_scan(len, d_in, d_out, TAP_IDLE); if (dsp5680xx_context.flush) { retval = dsp5680xx_execute_queue(); err_check(retval, DSP5680XX_ERROR_JTAG_DRSCAN, "drscan failed!"); } if (d_out) LOG_DEBUG("Data read (%d bits): 0x%04X", len, *d_out); else LOG_DEBUG("Data read was discarded."); return retval; } /** * Test func * * @param target * @param d_in This is the data that will be shifted into the JTAG IR reg. * @param d_out The data that will be shifted out of the JTAG IR reg will be stored here. * @param ir_len Length of the data to be shifted to JTAG IR. * */ static int dsp5680xx_irscan(struct target *target, uint32_t *d_in, uint32_t *d_out, uint8_t ir_len) { int retval = ERROR_OK; uint16_t tap_ir_len = DSP5680XX_JTAG_MASTER_TAP_IRLEN; if (!target->tap) { retval = ERROR_FAIL; err_check(retval, DSP5680XX_ERROR_JTAG_INVALID_TAP, "Invalid tap"); } if (ir_len != target->tap->ir_length) { if (target->tap->enabled) { retval = ERROR_FAIL; err_check(retval, DSP5680XX_ERROR_INVALID_IR_LEN, "Invalid irlen"); } else { struct jtag_tap *t = jtag_tap_by_string("dsp568013.chp"); if ((!t) || ((t->enabled) && (ir_len != tap_ir_len))) { retval = ERROR_FAIL; err_check(retval, DSP5680XX_ERROR_INVALID_IR_LEN, "Invalid irlen"); } } } jtag_add_plain_ir_scan(ir_len, (uint8_t *) d_in, (uint8_t *) d_out, TAP_IDLE); if (dsp5680xx_context.flush) { retval = dsp5680xx_execute_queue(); err_check(retval, DSP5680XX_ERROR_JTAG_IRSCAN, "irscan failed!"); } return retval; } static int dsp5680xx_jtag_status(struct target *target, uint8_t *status) { uint32_t read_from_ir; uint32_t instr; int retval; instr = JTAG_INSTR_ENABLE_ONCE; retval = dsp5680xx_irscan(target, &instr, &read_from_ir, DSP5680XX_JTAG_CORE_TAP_IRLEN); err_check_propagate(retval); if (status) *status = (uint8_t) read_from_ir; return ERROR_OK; } static int jtag_data_read(struct target *target, uint8_t *data_read, int num_bits) { uint32_t bogus_instr = 0; int retval = dsp5680xx_drscan(target, (uint8_t *) &bogus_instr, data_read, num_bits); LOG_DEBUG("Data read (%d bits): 0x%04X", num_bits, *data_read); /** TODO remove this or move to jtagio? */ return retval; } #define jtag_data_read8(target, data_read) jtag_data_read(target, data_read, 8) #define jtag_data_read16(target, data_read) jtag_data_read(target, data_read, 16) #define jtag_data_read32(target, data_read) jtag_data_read(target, data_read, 32) static uint32_t data_read_dummy; static int jtag_data_write(struct target *target, uint32_t instr, int num_bits, uint32_t *data_read) { int retval; retval = dsp5680xx_drscan(target, (uint8_t *) &instr, (uint8_t *) &data_read_dummy, num_bits); err_check_propagate(retval); if (data_read) *data_read = data_read_dummy; return retval; } #define jtag_data_write8(target, instr, data_read) jtag_data_write(target, instr, 8, data_read) #define jtag_data_write16(target, instr, data_read) jtag_data_write(target, instr, 16, data_read) #define jtag_data_write24(target, instr, data_read) jtag_data_write(target, instr, 24, data_read) #define jtag_data_write32(target, instr, data_read) jtag_data_write(target, instr, 32, data_read) /** * Executes EOnCE instruction. * * @param target * @param instr Instruction to execute. * @param rw * @param go * @param ex * @param eonce_status Value read from the EOnCE status register. * * @return */ static int eonce_instruction_exec_single(struct target *target, uint8_t instr, uint8_t rw, uint8_t go, uint8_t ex, uint8_t *eonce_status) { int retval; uint32_t dr_out_tmp; uint8_t instr_with_flags = instr | (rw << 7) | (go << 6) | (ex << 5); retval = jtag_data_write(target, instr_with_flags, 8, &dr_out_tmp); err_check_propagate(retval); if (eonce_status) *eonce_status = (uint8_t) dr_out_tmp; return retval; } /* wrappers for multi opcode instructions */ #define dsp5680xx_exe_1(target, oc1, oc2, oc3) dsp5680xx_exe1(target, oc1) #define dsp5680xx_exe_2(target, oc1, oc2, oc3) dsp5680xx_exe2(target, oc1, oc2) #define dsp5680xx_exe_3(target, oc1, oc2, oc3) dsp5680xx_exe3(target, oc1, oc2, oc3) #define dsp5680xx_exe_generic(t, words, oc1, oc2, oc3) dsp5680xx_exe_##words(t, oc1, oc2, oc3) /* Executes one word DSP instruction */ static int dsp5680xx_exe1(struct target *target, uint16_t opcode) { int retval; retval = eonce_instruction_exec_single(target, 0x04, 0, 1, 0, NULL); err_check_propagate(retval); retval = jtag_data_write16(target, opcode, NULL); err_check_propagate(retval); return retval; } /* Executes two word DSP instruction */ static int dsp5680xx_exe2(struct target *target, uint16_t opcode1, uint16_t opcode2) { int retval; retval = eonce_instruction_exec_single(target, 0x04, 0, 0, 0, NULL); err_check_propagate(retval); retval = jtag_data_write16(target, opcode1, NULL); err_check_propagate(retval); retval = eonce_instruction_exec_single(target, 0x04, 0, 1, 0, NULL); err_check_propagate(retval); retval = jtag_data_write16(target, opcode2, NULL); err_check_propagate(retval); return retval; } /* Executes three word DSP instruction */ static int dsp5680xx_exe3(struct target *target, uint16_t opcode1, uint16_t opcode2, uint16_t opcode3) { int retval; retval = eonce_instruction_exec_single(target, 0x04, 0, 0, 0, NULL); err_check_propagate(retval); retval = jtag_data_write16(target, opcode1, NULL); err_check_propagate(retval); retval = eonce_instruction_exec_single(target, 0x04, 0, 0, 0, NULL); err_check_propagate(retval); retval = jtag_data_write16(target, opcode2, NULL); err_check_propagate(retval); retval = eonce_instruction_exec_single(target, 0x04, 0, 1, 0, NULL); err_check_propagate(retval); retval = jtag_data_write16(target, opcode3, NULL); err_check_propagate(retval); return retval; } /* *--------------- Real-time data exchange --------------- * The EOnCE Transmit (OTX) and Receive (ORX) registers are data memory mapped, each with an upper * and lower 16 bit word. * Transmit and receive directions are defined from the core’s perspective. * The core writes to the Transmit register and reads the Receive register, and the host through * JTAG writes to the Receive register and reads the Transmit register. * Both registers have a combined data memory mapped OTXRXSR which provides indication when * each may be accessed. * ref: eonce_rev.1.0_0208081.pdf@36 */ /* writes data into upper ORx register of the target */ static int core_tx_upper_data(struct target *target, uint16_t data, uint32_t *eonce_status_low) { int retval; retval = eonce_instruction_exec_single(target, DSP5680XX_ONCE_ORX1, 0, 0, 0, NULL); err_check_propagate(retval); retval = jtag_data_write16(target, data, eonce_status_low); err_check_propagate(retval); return retval; } /* writes data into lower ORx register of the target */ #define CMD1 eonce_instruction_exec_single(target, DSP5680XX_ONCE_ORX, 0, 0, 0, NULL); #define CMD2 jtag_data_write16((t, data) #define core_tx_lower_data(t, data) PT1\ PT2 /** * * @param target * @param data_read: Returns the data read from the upper OTX register via JTAG. * @return: Returns an error code (see error code documentation) */ static int core_rx_upper_data(struct target *target, uint8_t *data_read) { int retval; retval = eonce_instruction_exec_single(target, DSP5680XX_ONCE_OTX1, 1, 0, 0, NULL); err_check_propagate(retval); retval = jtag_data_read16(target, data_read); err_check_propagate(retval); return retval; } /** * * @param target * @param data_read: Returns the data read from the lower OTX register via JTAG. * @return: Returns an error code (see error code documentation) */ static int core_rx_lower_data(struct target *target, uint8_t *data_read) { int retval; retval = eonce_instruction_exec_single(target, DSP5680XX_ONCE_OTX, 1, 0, 0, NULL); err_check_propagate(retval); retval = jtag_data_read16(target, data_read); err_check_propagate(retval); return retval; } /* *-- -- -- -- --- -- -- -- --- -- -- -- --- -- -- -- --- -- -- -- --- -- *-- -- -- -- --- -- -- -Core Instructions- -- -- -- --- -- -- -- --- -- *-- -- -- -- --- -- -- -- --- -- -- -- --- -- -- -- --- -- -- -- --- -- */ #define exe(a, b, c, d, e) dsp5680xx_exe_generic(a, b, c, d, e) /* move.l #value, r0 */ #define core_move_long_to_r0(target, value) exe(target, 3, 0xe418, value&0xffff, value>>16) /* move.l #value, n */ #define core_move_long_to_n(target, value) exe(target, 3, 0xe41e, value&0xffff, value>>16) /* move x:(r0), y0 */ #define core_move_at_r0_to_y0(target) exe(target, 1, 0xF514, 0, 0) /* move x:(r0), y1 */ #define core_move_at_r0_to_y1(target) exe(target, 1, 0xF714, 0, 0) /* move.l x:(r0), y */ #define core_move_long_at_r0_y(target) exe(target, 1, 0xF734, 0, 0) /* move y0, x:(r0) */ #define core_move_y0_at_r0(target) exe(target, 1, 0xd514, 0, 0) /* bfclr #value, x:(r0) */ #define eonce_bfclr_at_r0(target, value) exe(target, 2, 0x8040, value, 0) /* move #value, y0 */ #define core_move_value_to_y0(target, value) exe(target, 2, 0x8745, value, 0) /* move.w y0, x:(r0)+ */ #define core_move_y0_at_r0_inc(target) exe(target, 1, 0xd500, 0, 0) /* move.w y0, p:(r0)+ */ #define core_move_y0_at_pr0_inc(target) exe(target, 1, 0x8560, 0, 0) /* move.w p:(r0)+, y0 */ #define core_move_at_pr0_inc_to_y0(target) exe(target, 1, 0x8568, 0, 0) /* move.w p:(r0)+, y1 */ #define core_move_at_pr0_inc_to_y1(target) exe(target, 1, 0x8768, 0, 0) /* move.l #value, r2 */ #define core_move_long_to_r2(target, value) exe(target, 3, 0xe41A, value&0xffff, value>>16) /* move y0, x:(r2) */ #define core_move_y0_at_r2(target) exe(target, 1, 0xd516, 0, 0) /* move.w #, x:(r2) */ #define core_move_value_at_r2(target, value) exe(target, 2, 0x8642, value, 0) /* move.w #, x:(r0) */ #define core_move_value_at_r0(target, value) exe(target, 2, 0x8640, value, 0) /* move.w #, x:(R2+) */ #define core_move_value_at_r2_disp(target, value, disp) exe(target, 3, 0x8646, value, disp) /* move.w x:(r2), Y0 */ #define core_move_at_r2_to_y0(target) exe(target, 1, 0xF516, 0, 0) /* move.w p:(r2)+, y0 */ #define core_move_at_pr2_inc_to_y0(target) exe(target, 1, 0x856A, 0, 0) /* move.l #value, r3 */ #define core_move_long_to_r1(target, value) exe(target, 3, 0xE419, value&0xffff, value>>16) /* move.l #value, r3 */ #define core_move_long_to_r3(target, value) exe(target, 3, 0xE41B, value&0xffff, value>>16) /* move.w y0, p:(r3)+ */ #define core_move_y0_at_pr3_inc(target) exe(target, 1, 0x8563, 0, 0) /* move.w y0, x:(r3) */ #define core_move_y0_at_r3(target) exe(target, 1, 0xD503, 0, 0) /* move.l #value, r4 */ #define core_move_long_to_r4(target, value) exe(target, 3, 0xE41C, value&0xffff, value>>16) /* move pc, r4 */ #define core_move_pc_to_r4(target) exe(target, 1, 0xE716, 0, 0) /* move.l r4, y */ #define core_move_r4_to_y(target) exe(target, 1, 0xe764, 0, 0) /* move.w p:(r0)+, y0 */ #define core_move_at_pr0_inc_to_y0(target) exe(target, 1, 0x8568, 0, 0) /* move.w x:(r0)+, y0 */ #define core_move_at_r0_inc_to_y0(target) exe(target, 1, 0xf500, 0, 0) /* move x:(r0), y0 */ #define core_move_at_r0_y0(target) exe(target, 1, 0xF514, 0, 0) /* nop */ #define eonce_nop(target) exe(target, 1, 0xe700, 0, 0) /* move.w x:(R2+), Y0 */ #define core_move_at_r2_disp_to_y0(target, disp) exe(target, 2, 0xF542, disp, 0) /* move.w y1, x:(r2) */ #define core_move_y1_at_r2(target) exe(target, 1, 0xd716, 0, 0) /* move.w y1, x:(r0) */ #define core_move_y1_at_r0(target) exe(target, 1, 0xd714, 0, 0) /* move.bp y0, x:(r0)+ */ #define core_move_byte_y0_at_r0(target) exe(target, 1, 0xd5a0, 0, 0) /* move.w y1, p:(r0)+ */ #define core_move_y1_at_pr0_inc(target) exe(target, 1, 0x8760, 0, 0) /* move.w y1, x:(r0)+ */ #define core_move_y1_at_r0_inc(target) exe(target, 1, 0xD700, 0, 0) /* move.l #value, y */ #define core_move_long_to_y(target, value) exe(target, 3, 0xe417, value&0xffff, value>>16) static int core_move_value_to_pc(struct target *target, uint32_t value) { check_halt_and_debug(target); int retval; retval = dsp5680xx_exe_generic(target, 3, 0xE71E, value & 0xffff, value >> 16); err_check_propagate(retval); return retval; } static int eonce_load_tx_rx_to_r0(struct target *target) { int retval; retval = core_move_long_to_r0(target, ((MC568013_EONCE_TX_RX_ADDR) + (MC568013_EONCE_OBASE_ADDR << 16))); return retval; } static int core_load_tx_rx_high_addr_to_r0(struct target *target) { int retval = 0; retval = core_move_long_to_r0(target, ((MC568013_EONCE_TX1_RX1_HIGH_ADDR) + (MC568013_EONCE_OBASE_ADDR << 16))); return retval; } static int dsp5680xx_read_core_reg(struct target *target, uint8_t reg_addr, uint16_t *data_read) { /* TODO implement a general version of this which matches what openocd uses. */ int retval; uint32_t dummy_data_to_shift_into_dr; retval = eonce_instruction_exec_single(target, reg_addr, 1, 0, 0, NULL); err_check_propagate(retval); retval = dsp5680xx_drscan(target, (uint8_t *) &dummy_data_to_shift_into_dr, (uint8_t *) data_read, 8); err_check_propagate(retval); LOG_DEBUG("Reg. data: 0x%02X.", *data_read); return retval; } static int eonce_read_status_reg(struct target *target, uint16_t *data) { int retval; retval = dsp5680xx_read_core_reg(target, DSP5680XX_ONCE_OSR, data); err_check_propagate(retval); return retval; } /** * Takes the core out of debug mode. * * @param target * @param eonce_status Data read from the EOnCE status register. * * @return */ static int eonce_exit_debug_mode(struct target *target, uint8_t *eonce_status) { int retval; retval = eonce_instruction_exec_single(target, 0x1F, 0, 0, 1, eonce_status); err_check_propagate(retval); return retval; } static int switch_tap(struct target *target, struct jtag_tap *master_tap, struct jtag_tap *core_tap) { int retval = ERROR_OK; uint32_t instr; uint32_t ir_out; /* not used, just to make jtag happy. */ if (!master_tap) { master_tap = jtag_tap_by_string("dsp568013.chp"); if (!master_tap) { retval = ERROR_FAIL; const char *msg = "Failed to get master tap."; err_check(retval, DSP5680XX_ERROR_JTAG_TAP_FIND_MASTER, msg); } } if (!core_tap) { core_tap = jtag_tap_by_string("dsp568013.cpu"); if (!core_tap) { retval = ERROR_FAIL; err_check(retval, DSP5680XX_ERROR_JTAG_TAP_FIND_CORE, "Failed to get core tap."); } } if (!(((int)master_tap->enabled) ^ ((int)core_tap->enabled))) { LOG_WARNING ("Master:%d\nCore:%d\nOnly 1 should be enabled.\n", (int)master_tap->enabled, (int)core_tap->enabled); } if (master_tap->enabled) { instr = 0x5; retval = dsp5680xx_irscan(target, &instr, &ir_out, DSP5680XX_JTAG_MASTER_TAP_IRLEN); err_check_propagate(retval); instr = 0x2; retval = dsp5680xx_drscan(target, (uint8_t *) &instr, (uint8_t *) &ir_out, 4); err_check_propagate(retval); core_tap->enabled = true; master_tap->enabled = false; } else { instr = 0x08; retval = dsp5680xx_irscan(target, &instr, &ir_out, DSP5680XX_JTAG_CORE_TAP_IRLEN); err_check_propagate(retval); instr = 0x1; retval = dsp5680xx_drscan(target, (uint8_t *) &instr, (uint8_t *) &ir_out, 4); err_check_propagate(retval); core_tap->enabled = false; master_tap->enabled = true; } return retval; } /** * Puts the core into debug mode, enabling the EOnCE module. * This will not always work, eonce_enter_debug_mode executes much * more complicated routine, which is guaranteed to work, but requires * a reset. This will complicate comm with the flash module, since * after a reset clock divisors must be set again. * This implementation works most of the time, and is not accessible to the * user. * * @param target * @param eonce_status Data read from the EOnCE status register. * * @return */ static int eonce_enter_debug_mode_without_reset(struct target *target, uint16_t *eonce_status) { int retval; uint32_t instr = JTAG_INSTR_DEBUG_REQUEST; uint32_t ir_out; /* not used, just to make jtag happy.*/ /* Debug request #1 */ retval = dsp5680xx_irscan(target, &instr, &ir_out, DSP5680XX_JTAG_CORE_TAP_IRLEN); err_check_propagate(retval); /* Enable EOnCE module */ instr = JTAG_INSTR_ENABLE_ONCE; /* Two rounds of jtag 0x6 (enable eonce) to enable EOnCE. */ retval = dsp5680xx_irscan(target, &instr, &ir_out, DSP5680XX_JTAG_CORE_TAP_IRLEN); err_check_propagate(retval); retval = dsp5680xx_irscan(target, &instr, &ir_out, DSP5680XX_JTAG_CORE_TAP_IRLEN); err_check_propagate(retval); if ((ir_out & JTAG_STATUS_MASK) == JTAG_STATUS_DEBUG) target->state = TARGET_HALTED; else { retval = ERROR_FAIL; err_check_propagate(retval); } /* Verify that debug mode is enabled */ uint16_t data_read_from_dr; retval = eonce_read_status_reg(target, &data_read_from_dr); err_check_propagate(retval); if ((data_read_from_dr & 0x30) == 0x30) { LOG_DEBUG("EOnCE successfully entered debug mode."); dsp5680xx_context.debug_mode_enabled = true; retval = ERROR_OK; } else { dsp5680xx_context.debug_mode_enabled = false; retval = ERROR_TARGET_FAILURE; /** *No error msg here, since there is still hope with full halting sequence */ err_check_propagate(retval); } if (eonce_status) *eonce_status = data_read_from_dr; return retval; } /** * Puts the core into debug mode, enabling the EOnCE module. * * @param target * @param eonce_status Data read from the EOnCE status register. * * @return */ static int eonce_enter_debug_mode(struct target *target, uint16_t *eonce_status) { int retval = ERROR_OK; uint32_t instr = JTAG_INSTR_DEBUG_REQUEST; uint32_t ir_out; /* not used, just to make jtag happy. */ uint16_t instr_16; uint16_t read_16; /* First try the easy way */ retval = eonce_enter_debug_mode_without_reset(target, eonce_status); if (retval == ERROR_OK) return retval; struct jtag_tap *tap_chp; struct jtag_tap *tap_cpu; tap_chp = jtag_tap_by_string("dsp568013.chp"); if (!tap_chp) { retval = ERROR_FAIL; err_check(retval, DSP5680XX_ERROR_JTAG_TAP_FIND_MASTER, "Failed to get master tap."); } tap_cpu = jtag_tap_by_string("dsp568013.cpu"); if (!tap_cpu) { retval = ERROR_FAIL; err_check(retval, DSP5680XX_ERROR_JTAG_TAP_FIND_CORE, "Failed to get master tap."); } /* Enable master tap */ tap_chp->enabled = true; tap_cpu->enabled = false; instr = MASTER_TAP_CMD_IDCODE; retval = dsp5680xx_irscan(target, &instr, &ir_out, DSP5680XX_JTAG_MASTER_TAP_IRLEN); err_check_propagate(retval); jtag_add_sleep(TIME_DIV_FREESCALE * 100 * 1000); /* Enable EOnCE module */ jtag_add_reset(0, 1); jtag_add_sleep(TIME_DIV_FREESCALE * 200 * 1000); instr = 0x0606ffff; /* This was selected experimentally. */ retval = dsp5680xx_drscan(target, (uint8_t *) &instr, (uint8_t *) &ir_out, 32); err_check_propagate(retval); /* ir_out now hold tap idcode */ /* Enable core tap */ tap_chp->enabled = true; retval = switch_tap(target, tap_chp, tap_cpu); err_check_propagate(retval); instr = JTAG_INSTR_ENABLE_ONCE; /* Two rounds of jtag 0x6 (enable eonce) to enable EOnCE. */ retval = dsp5680xx_irscan(target, &instr, &ir_out, DSP5680XX_JTAG_CORE_TAP_IRLEN); err_check_propagate(retval); instr = JTAG_INSTR_DEBUG_REQUEST; retval = dsp5680xx_irscan(target, &instr, &ir_out, DSP5680XX_JTAG_CORE_TAP_IRLEN); err_check_propagate(retval); instr_16 = 0x1; retval = dsp5680xx_drscan(target, (uint8_t *) &instr_16, (uint8_t *) &read_16, 8); err_check_propagate(retval); instr_16 = 0x20; retval = dsp5680xx_drscan(target, (uint8_t *) &instr_16, (uint8_t *) &read_16, 8); err_check_propagate(retval); jtag_add_sleep(TIME_DIV_FREESCALE * 100 * 1000); jtag_add_reset(0, 0); jtag_add_sleep(TIME_DIV_FREESCALE * 300 * 1000); instr = JTAG_INSTR_ENABLE_ONCE; /* Two rounds of jtag 0x6 (enable eonce) to enable EOnCE. */ for (int i = 0; i < 3; i++) { retval = dsp5680xx_irscan(target, &instr, &ir_out, DSP5680XX_JTAG_CORE_TAP_IRLEN); err_check_propagate(retval); } if ((ir_out & JTAG_STATUS_MASK) == JTAG_STATUS_DEBUG) target->state = TARGET_HALTED; else { retval = ERROR_FAIL; err_check(retval, DSP5680XX_ERROR_HALT, "Failed to halt target."); } for (int i = 0; i < 3; i++) { instr_16 = 0x86; dsp5680xx_drscan(target, (uint8_t *) &instr_16, (uint8_t *) &read_16, 16); instr_16 = 0xff; dsp5680xx_drscan(target, (uint8_t *) &instr_16, (uint8_t *) &read_16, 16); } /* Verify that debug mode is enabled */ uint16_t data_read_from_dr; retval = eonce_read_status_reg(target, &data_read_from_dr); err_check_propagate(retval); if ((data_read_from_dr & 0x30) == 0x30) { LOG_DEBUG("EOnCE successfully entered debug mode."); dsp5680xx_context.debug_mode_enabled = true; retval = ERROR_OK; } else { const char *msg = "Failed to set EOnCE module to debug mode"; retval = ERROR_TARGET_FAILURE; err_check(retval, DSP5680XX_ERROR_ENTER_DEBUG_MODE, msg); } if (eonce_status) *eonce_status = data_read_from_dr; return retval; } /** * Reads the current value of the program counter and stores it. * * @param target * * @return */ static int eonce_pc_store(struct target *target) { uint8_t tmp[2]; int retval; retval = core_move_pc_to_r4(target); err_check_propagate(retval); retval = core_move_r4_to_y(target); err_check_propagate(retval); retval = eonce_load_tx_rx_to_r0(target); err_check_propagate(retval); retval = core_move_y0_at_r0(target); err_check_propagate(retval); retval = core_rx_lower_data(target, tmp); err_check_propagate(retval); LOG_USER("PC value: 0x%X%X\n", tmp[1], tmp[0]); dsp5680xx_context.stored_pc = (tmp[0] | (tmp[1] << 8)); return ERROR_OK; } static int dsp5680xx_target_create(struct target *target, Jim_Interp *interp) { struct dsp5680xx_common *dsp5680xx = calloc(1, sizeof(struct dsp5680xx_common)); target->arch_info = dsp5680xx; return ERROR_OK; } static int dsp5680xx_init_target(struct command_context *cmd_ctx, struct target *target) { dsp5680xx_context.stored_pc = 0; dsp5680xx_context.flush = 1; dsp5680xx_context.debug_mode_enabled = false; LOG_DEBUG("target initiated!"); /* TODO core tap must be enabled before running these commands, currently * this is done in the .cfg tcl script. */ return ERROR_OK; } static int dsp5680xx_arch_state(struct target *target) { LOG_USER("%s not implemented yet.", __func__); return ERROR_OK; } static int dsp5680xx_assert_reset(struct target *target) { target->state = TARGET_RESET; return ERROR_OK; } static int dsp5680xx_deassert_reset(struct target *target) { target->state = TARGET_RUNNING; return ERROR_OK; } static int dsp5680xx_halt(struct target *target) { int retval; uint16_t eonce_status = 0xbeef; if ((target->state == TARGET_HALTED) && (dsp5680xx_context.debug_mode_enabled)) { LOG_USER("Target already halted and in debug mode."); return ERROR_OK; } else { if (target->state == TARGET_HALTED) LOG_USER ("Target already halted, re attempting to enter debug mode."); } retval = eonce_enter_debug_mode(target, &eonce_status); err_check_propagate(retval); retval = eonce_pc_store(target); err_check_propagate(retval); if (dsp5680xx_context.debug_mode_enabled) { retval = eonce_pc_store(target); err_check_propagate(retval); } return retval; } static int dsp5680xx_poll(struct target *target) { int retval; uint8_t jtag_status; uint8_t eonce_status; uint16_t read_tmp; retval = dsp5680xx_jtag_status(target, &jtag_status); err_check_propagate(retval); if (jtag_status == JTAG_STATUS_DEBUG) if (target->state != TARGET_HALTED) { retval = eonce_enter_debug_mode(target, &read_tmp); err_check_propagate(retval); eonce_status = (uint8_t) read_tmp; if ((eonce_status & EONCE_STAT_MASK) != DSP5680XX_ONCE_OSCR_DEBUG_M) { const char *msg = "%s: Failed to put EOnCE in debug mode.Flash locked?..."; LOG_WARNING(msg, __func__); return ERROR_TARGET_FAILURE; } else { target->state = TARGET_HALTED; return ERROR_OK; } } if (jtag_status == JTAG_STATUS_NORMAL) { if (target->state == TARGET_RESET) { retval = dsp5680xx_halt(target); err_check_propagate(retval); retval = eonce_exit_debug_mode(target, &eonce_status); err_check_propagate(retval); if ((eonce_status & EONCE_STAT_MASK) != DSP5680XX_ONCE_OSCR_NORMAL_M) { const char *msg = "%s: JTAG running, but EOnCE run failed.Try resetting.."; LOG_WARNING(msg, __func__); return ERROR_TARGET_FAILURE; } else { target->state = TARGET_RUNNING; return ERROR_OK; } } if (target->state != TARGET_RUNNING) { retval = eonce_read_status_reg(target, &read_tmp); err_check_propagate(retval); eonce_status = (uint8_t) read_tmp; if ((eonce_status & EONCE_STAT_MASK) != DSP5680XX_ONCE_OSCR_NORMAL_M) { LOG_WARNING ("Inconsistent target status. Restart!"); return ERROR_TARGET_FAILURE; } } target->state = TARGET_RUNNING; return ERROR_OK; } if (jtag_status == JTAG_STATUS_DEAD) { LOG_ERROR ("%s: Cannot communicate with JTAG. Check connection...", __func__); target->state = TARGET_UNKNOWN; return ERROR_TARGET_FAILURE; } if (target->state == TARGET_UNKNOWN) { LOG_ERROR("%s: Target status invalid - communication failure", __func__); return ERROR_TARGET_FAILURE; } return ERROR_OK; } static int dsp5680xx_resume(struct target *target, int current, target_addr_t address, int hb, int d) { if (target->state == TARGET_RUNNING) { LOG_USER("Target already running."); return ERROR_OK; } int retval; uint8_t eonce_status; uint8_t jtag_status; if (dsp5680xx_context.debug_mode_enabled) { if (!current) { retval = core_move_value_to_pc(target, address); err_check_propagate(retval); } int retry = 20; while (retry-- > 1) { retval = eonce_exit_debug_mode(target, &eonce_status); err_check_propagate(retval); if (eonce_status == DSP5680XX_ONCE_OSCR_NORMAL_M) break; } if (retry == 0) { retval = ERROR_TARGET_FAILURE; err_check(retval, DSP5680XX_ERROR_EXIT_DEBUG_MODE, "Failed to exit debug mode..."); } else { target->state = TARGET_RUNNING; dsp5680xx_context.debug_mode_enabled = false; } LOG_DEBUG("EOnCE status: 0x%02X.", eonce_status); } else { /* * If debug mode was not enabled but target was halted, then it is most likely that * access to eonce registers is locked. * Reset target to make it run again. */ jtag_add_reset(0, 1); jtag_add_sleep(TIME_DIV_FREESCALE * 200 * 1000); retval = reset_jtag(); err_check(retval, DSP5680XX_ERROR_JTAG_RESET, "Failed to reset JTAG state machine"); jtag_add_sleep(TIME_DIV_FREESCALE * 100 * 1000); jtag_add_reset(0, 0); jtag_add_sleep(TIME_DIV_FREESCALE * 300 * 1000); retval = dsp5680xx_jtag_status(target, &jtag_status); err_check_propagate(retval); if ((jtag_status & JTAG_STATUS_MASK) == JTAG_STATUS_NORMAL) { target->state = TARGET_RUNNING; dsp5680xx_context.debug_mode_enabled = false; } else { retval = ERROR_TARGET_FAILURE; err_check(retval, DSP5680XX_ERROR_RESUME, "Failed to resume target"); } } return ERROR_OK; } /** * The value of @a address determines if it corresponds to P: (program) or X: (dat) memory. * If the address is over 0x200000 then it is considered X: memory, and @a pmem = 0. * The special case of 0xFFXXXX is not modified, since it allows to read out the * memory mapped EOnCE registers. * * @param address * @param pmem * * @return */ static int dsp5680xx_convert_address(uint32_t *address, int *pmem) { /* * Distinguish data memory (x) from program memory (p) by the address. * Addresses over S_FILE_DATA_OFFSET are considered (x) memory. */ if (*address >= S_FILE_DATA_OFFSET) { *pmem = 0; if (((*address) & 0xff0000) != 0xff0000) *address -= S_FILE_DATA_OFFSET; } return ERROR_OK; } static int dsp5680xx_read_16_single(struct target *t, uint32_t a, uint8_t *data_read, int r_pmem) { struct target *target = t; uint32_t address = a; int retval; retval = core_move_long_to_r0(target, address); err_check_propagate(retval); if (r_pmem) retval = core_move_at_pr0_inc_to_y0(target); else retval = core_move_at_r0_to_y0(target); err_check_propagate(retval); retval = eonce_load_tx_rx_to_r0(target); err_check_propagate(retval); retval = core_move_y0_at_r0(target); err_check_propagate(retval); /* at this point the data i want is at the reg eonce can read */ retval = core_rx_lower_data(target, data_read); err_check_propagate(retval); LOG_DEBUG("%s:Data read from 0x%06" PRIX32 ": 0x%02X%02X", __func__, address, data_read[1], data_read[0]); return retval; } static int dsp5680xx_read_32_single(struct target *t, uint32_t a, uint8_t *data_read, int r_pmem) { struct target *target = t; uint32_t address = a; int retval; address = (address & 0xFFFFF); /* Get data to an intermediate register */ retval = core_move_long_to_r0(target, address); err_check_propagate(retval); if (r_pmem) { retval = core_move_at_pr0_inc_to_y0(target); err_check_propagate(retval); retval = core_move_at_pr0_inc_to_y1(target); err_check_propagate(retval); } else { retval = core_move_at_r0_inc_to_y0(target); err_check_propagate(retval); retval = core_move_at_r0_to_y1(target); err_check_propagate(retval); } /* Get lower part of data to TX/RX */ retval = eonce_load_tx_rx_to_r0(target); err_check_propagate(retval); retval = core_move_y0_at_r0_inc(target); /* This also load TX/RX high to r0 */ err_check_propagate(retval); /* Get upper part of data to TX/RX */ retval = core_move_y1_at_r0(target); err_check_propagate(retval); /* at this point the data i want is at the reg eonce can read */ retval = core_rx_lower_data(target, data_read); err_check_propagate(retval); retval = core_rx_upper_data(target, data_read + 2); err_check_propagate(retval); return retval; } static int dsp5680xx_read(struct target *t, target_addr_t a, uint32_t size, uint32_t count, uint8_t *buf) { struct target *target = t; uint32_t address = a; uint8_t *buffer = buf; check_halt_and_debug(target); int retval = ERROR_OK; int pmem = 1; retval = dsp5680xx_convert_address(&address, &pmem); err_check_propagate(retval); dsp5680xx_context.flush = 0; int counter = FLUSH_COUNT_READ_WRITE; for (unsigned i = 0; i < count; i++) { if (--counter == 0) { dsp5680xx_context.flush = 1; counter = FLUSH_COUNT_READ_WRITE; } switch (size) { case 1: if (!(i % 2)) retval = dsp5680xx_read_16_single(target, address + i / 2, buffer + i, pmem); break; case 2: retval = dsp5680xx_read_16_single(target, address + i, buffer + 2 * i, pmem); break; case 4: retval = dsp5680xx_read_32_single(target, address + 2 * i, buffer + 4 * i, pmem); break; default: LOG_USER("%s: Invalid read size.", __func__); break; } err_check_propagate(retval); dsp5680xx_context.flush = 0; } dsp5680xx_context.flush = 1; retval = dsp5680xx_execute_queue(); err_check_propagate(retval); return retval; } static int dsp5680xx_write_16_single(struct target *t, uint32_t a, uint16_t data, uint8_t w_pmem) { struct target *target = t; uint32_t address = a; int retval = 0; retval = core_move_long_to_r0(target, address); err_check_propagate(retval); if (w_pmem) { retval = core_move_value_to_y0(target, data); err_check_propagate(retval); retval = core_move_y0_at_pr0_inc(target); err_check_propagate(retval); } else { retval = core_move_value_at_r0(target, data); err_check_propagate(retval); } return retval; } static int dsp5680xx_write_32_single(struct target *t, uint32_t a, uint32_t data, int w_pmem) { struct target *target = t; uint32_t address = a; int retval = ERROR_OK; retval = core_move_long_to_r0(target, address); err_check_propagate(retval); retval = core_move_long_to_y(target, data); err_check_propagate(retval); if (w_pmem) retval = core_move_y0_at_pr0_inc(target); else retval = core_move_y0_at_r0_inc(target); err_check_propagate(retval); if (w_pmem) retval = core_move_y1_at_pr0_inc(target); else retval = core_move_y1_at_r0_inc(target); err_check_propagate(retval); return retval; } static int dsp5680xx_write_8(struct target *t, uint32_t a, uint32_t c, const uint8_t *d, int pmem) { struct target *target = t; uint32_t address = a; uint32_t count = c; const uint8_t *data = d; int retval = 0; uint16_t data_16; uint32_t iter; int counter = FLUSH_COUNT_READ_WRITE; for (iter = 0; iter < count / 2; iter++) { if (--counter == 0) { dsp5680xx_context.flush = 1; counter = FLUSH_COUNT_READ_WRITE; } data_16 = (data[2 * iter] | (data[2 * iter + 1] << 8)); retval = dsp5680xx_write_16_single(target, address + iter, data_16, pmem); if (retval != ERROR_OK) { LOG_ERROR("%s: Could not write to p:0x%04" PRIX32, __func__, address); dsp5680xx_context.flush = 1; return retval; } dsp5680xx_context.flush = 0; } dsp5680xx_context.flush = 1; /* Only one byte left, let's not overwrite the other byte (mem is 16bit) */ /* Need to retrieve the part we do not want to overwrite. */ uint16_t data_old; if ((count == 1) || (count % 2)) { retval = dsp5680xx_read(target, address + iter, 1, 1, (uint8_t *) &data_old); err_check_propagate(retval); if (count == 1) data_old = (((data_old & 0xff) << 8) | data[0]); /* preserve upper byte */ else data_old = (((data_old & 0xff) << 8) | data[2 * iter + 1]); retval = dsp5680xx_write_16_single(target, address + iter, data_old, pmem); err_check_propagate(retval); } return retval; } static int dsp5680xx_write_16(struct target *t, uint32_t a, uint32_t c, const uint8_t *d, int pmem) { struct target *target = t; uint32_t address = a; uint32_t count = c; const uint8_t *data = d; int retval = ERROR_OK; uint32_t iter; int counter = FLUSH_COUNT_READ_WRITE; for (iter = 0; iter < count; iter++) { if (--counter == 0) { dsp5680xx_context.flush = 1; counter = FLUSH_COUNT_READ_WRITE; } retval = dsp5680xx_write_16_single(target, address + iter, data[iter], pmem); if (retval != ERROR_OK) { LOG_ERROR("%s: Could not write to p:0x%04" PRIX32, __func__, address); dsp5680xx_context.flush = 1; return retval; } dsp5680xx_context.flush = 0; } dsp5680xx_context.flush = 1; return retval; } static int dsp5680xx_write_32(struct target *t, uint32_t a, uint32_t c, const uint8_t *d, int pmem) { struct target *target = t; uint32_t address = a; uint32_t count = c; const uint8_t *data = d; int retval = ERROR_OK; uint32_t iter; int counter = FLUSH_COUNT_READ_WRITE; for (iter = 0; iter < count; iter++) { if (--counter == 0) { dsp5680xx_context.flush = 1; counter = FLUSH_COUNT_READ_WRITE; } retval = dsp5680xx_write_32_single(target, address + (iter << 1), data[iter], pmem); if (retval != ERROR_OK) { LOG_ERROR("%s: Could not write to p:0x%04" PRIX32, __func__, address); dsp5680xx_context.flush = 1; return retval; } dsp5680xx_context.flush = 0; } dsp5680xx_context.flush = 1; return retval; } /** * Writes @a buffer to memory. * The parameter @a address determines whether @a buffer should be written to * P: (program) memory or X: (dat) memory. * * @param target * @param a address * @param size Bytes (1), Half words (2), Words (4). * @param count In bytes. * @param b buffer * * @return */ static int dsp5680xx_write(struct target *target, target_addr_t a, uint32_t size, uint32_t count, const uint8_t *b) { /* TODO Cannot write 32bit to odd address, will write 0x12345678 as 0x5678 0x0012 */ uint32_t address = a; uint8_t const *buffer = b; check_halt_and_debug(target); int retval = 0; int p_mem = 1; retval = dsp5680xx_convert_address(&address, &p_mem); err_check_propagate(retval); switch (size) { case 1: retval = dsp5680xx_write_8(target, address, count, buffer, p_mem); break; case 2: retval = dsp5680xx_write_16(target, address, count, buffer, p_mem); break; case 4: retval = dsp5680xx_write_32(target, address, count, buffer, p_mem); break; default: retval = ERROR_TARGET_DATA_ABORT; err_check(retval, DSP5680XX_ERROR_INVALID_DATA_SIZE_UNIT, "Invalid data size."); break; } return retval; } static int dsp5680xx_write_buffer(struct target *t, target_addr_t a, uint32_t size, const uint8_t *b) { check_halt_and_debug(t); return dsp5680xx_write(t, a, 1, size, b); } /** * This function is called by verify_image, it is used to read data from memory. * * @param target * @param address Word addressing. * @param size In bytes. * @param buffer * * @return */ static int dsp5680xx_read_buffer(struct target *target, target_addr_t address, uint32_t size, uint8_t *buffer) { check_halt_and_debug(target); /* The "/2" solves the byte/word addressing issue.*/ return dsp5680xx_read(target, address, 2, size / 2, buffer); } /** * This function is not implemented. * It returns an error in order to get OpenOCD to do read out the data * and calculate the CRC, or try a binary comparison. * * @param target * @param address Start address of the image. * @param size In bytes. * @param checksum * * @return */ static int dsp5680xx_checksum_memory(struct target *target, target_addr_t address, uint32_t size, uint32_t *checksum) { return ERROR_FAIL; } /** * Calculates a signature over @a word_count words in the data from @a buff8. * The algorithm used is the same the FM uses, so the @a return may be used to compare * with the one generated by the FM module, and check if flashing was successful. * This algorithm is based on the perl script available from the Freescale website at FAQ 25630. * * @param buff8 * @param word_count * * @return */ static int perl_crc(const uint8_t *buff8, uint32_t word_count) { uint16_t checksum = 0xffff; uint16_t data, fbmisr; uint32_t i; for (i = 0; i < word_count; i++) { data = (buff8[2 * i] | (buff8[2 * i + 1] << 8)); fbmisr = (checksum & 2) >> 1 ^ (checksum & 4) >> 2 ^ (checksum & 16) >> 4 ^ (checksum & 0x8000) >> 15; checksum = (data ^ ((checksum << 1) | fbmisr)); } i--; for (; !(i & 0x80000000); i--) { data = (buff8[2 * i] | (buff8[2 * i + 1] << 8)); fbmisr = (checksum & 2) >> 1 ^ (checksum & 4) >> 2 ^ (checksum & 16) >> 4 ^ (checksum & 0x8000) >> 15; checksum = (data ^ ((checksum << 1) | fbmisr)); } return checksum; } /** * Resets the SIM. (System Integration Modul). * * @param target * * @return */ static int dsp5680xx_f_sim_reset(struct target *target) { int retval = ERROR_OK; uint16_t sim_cmd = SIM_CMD_RESET; uint32_t sim_addr; if (strcmp(target->tap->chip, "dsp568013") == 0) { sim_addr = MC568013_SIM_BASE_ADDR + S_FILE_DATA_OFFSET; retval = dsp5680xx_write(target, sim_addr, 1, 2, (const uint8_t *)&sim_cmd); err_check_propagate(retval); } return retval; } /** * Halts the core and resets the SIM. (System Integration Modul). * * @param target * * @return */ static int dsp5680xx_soft_reset_halt(struct target *target) { /* TODO is this what this function is expected to do...? */ int retval; retval = dsp5680xx_halt(target); err_check_propagate(retval); retval = dsp5680xx_f_sim_reset(target); err_check_propagate(retval); return retval; } int dsp5680xx_f_protect_check(struct target *target, uint16_t *protected) { int retval; check_halt_and_debug(target); if (!protected) { const char *msg = "NULL pointer not valid."; err_check(ERROR_FAIL, DSP5680XX_ERROR_PROTECT_CHECK_INVALID_ARGS, msg); } retval = dsp5680xx_read_16_single(target, HFM_BASE_ADDR | HFM_PROT, (uint8_t *) protected, 0); err_check_propagate(retval); return retval; } /** * Executes a command on the FM module. * Some commands use the parameters @a address and @a data, others ignore them. * * @param target * @param c Command to execute. * @param address Command parameter. * @param data Command parameter. * @param hfm_ustat FM status register. * @param pmem Address is P: (program) memory (@a pmem == 1) or X: (dat) memory (@a pmem == 0) * * @return */ static int dsp5680xx_f_ex(struct target *target, uint16_t c, uint32_t address, uint32_t data, uint16_t *hfm_ustat, int pmem) { uint32_t command = c; int retval; retval = core_load_tx_rx_high_addr_to_r0(target); err_check_propagate(retval); retval = core_move_long_to_r2(target, HFM_BASE_ADDR); err_check_propagate(retval); uint8_t i[2]; int watchdog = 100; do { retval = core_move_at_r2_disp_to_y0(target, HFM_USTAT); /* read HMF_USTAT */ err_check_propagate(retval); retval = core_move_y0_at_r0(target); err_check_propagate(retval); retval = core_rx_upper_data(target, i); err_check_propagate(retval); if ((watchdog--) == 1) { retval = ERROR_TARGET_FAILURE; const char *msg = "Timed out waiting for FM to finish old command."; err_check(retval, DSP5680XX_ERROR_FM_BUSY, msg); } } while (!(i[0] & 0x40)); /* wait until current command is complete */ dsp5680xx_context.flush = 0; /* write to HFM_CNFG (lock=0,select bank) - flash_desc.bank&0x03, 0x01 == 0x00, 0x01 ??? */ retval = core_move_value_at_r2_disp(target, 0x00, HFM_CNFG); err_check_propagate(retval); /* write to HMF_USTAT, clear PVIOL, ACCERR &BLANK bits */ retval = core_move_value_at_r2_disp(target, 0x04, HFM_USTAT); err_check_propagate(retval); /* clear only one bit at a time */ retval = core_move_value_at_r2_disp(target, 0x10, HFM_USTAT); err_check_propagate(retval); retval = core_move_value_at_r2_disp(target, 0x20, HFM_USTAT); err_check_propagate(retval); /* write to HMF_PROT, clear protection */ retval = core_move_value_at_r2_disp(target, 0x00, HFM_PROT); err_check_propagate(retval); /* write to HMF_PROTB, clear protection */ retval = core_move_value_at_r2_disp(target, 0x00, HFM_PROTB); err_check_propagate(retval); retval = core_move_value_to_y0(target, data); err_check_propagate(retval); /* write to the flash block */ retval = core_move_long_to_r3(target, address); err_check_propagate(retval); if (pmem) { retval = core_move_y0_at_pr3_inc(target); err_check_propagate(retval); } else { retval = core_move_y0_at_r3(target); err_check_propagate(retval); } /* write command to the HFM_CMD reg */ retval = core_move_value_at_r2_disp(target, command, HFM_CMD); err_check_propagate(retval); /* start the command */ retval = core_move_value_at_r2_disp(target, 0x80, HFM_USTAT); err_check_propagate(retval); dsp5680xx_context.flush = 1; retval = dsp5680xx_execute_queue(); err_check_propagate(retval); watchdog = 100; do { /* read HMF_USTAT */ retval = core_move_at_r2_disp_to_y0(target, HFM_USTAT); err_check_propagate(retval); retval = core_move_y0_at_r0(target); err_check_propagate(retval); retval = core_rx_upper_data(target, i); err_check_propagate(retval); if ((watchdog--) == 1) { retval = ERROR_TARGET_FAILURE; err_check(retval, DSP5680XX_ERROR_FM_CMD_TIMED_OUT, "FM execution did not finish."); } } while (!(i[0] & 0x40)); /* wait until the command is complete */ *hfm_ustat = ((i[0] << 8) | (i[1])); if (i[0] & HFM_USTAT_MASK_PVIOL_ACCER) { retval = ERROR_TARGET_FAILURE; const char *msg = "pviol and/or accer bits set. HFM command execution error"; err_check(retval, DSP5680XX_ERROR_FM_EXEC, msg); } return ERROR_OK; } /** * Prior to the execution of any Flash module command, the Flash module Clock * Divider (CLKDIV) register must be initialized. The values of this register * determine the speed of the internal Flash Clock (FCLK). FCLK must be in the * range of 150kHz ≤ FCLK ≤ 200kHz for proper operation of the Flash module. * (Running FCLK too slowly wears out the module, while running it too fast * under programs Flash leading to bit errors.) * * @param target * * @return */ static int set_fm_ck_div(struct target *target) { uint8_t i[2]; int retval; retval = core_move_long_to_r2(target, HFM_BASE_ADDR); err_check_propagate(retval); retval = core_load_tx_rx_high_addr_to_r0(target); err_check_propagate(retval); /* read HFM_CLKD */ retval = core_move_at_r2_to_y0(target); err_check_propagate(retval); retval = core_move_y0_at_r0(target); err_check_propagate(retval); retval = core_rx_upper_data(target, i); err_check_propagate(retval); unsigned int hfm_at_wrong_value = 0; if ((i[0] & 0x7f) != HFM_CLK_DEFAULT) { LOG_DEBUG("HFM CLK divisor contained incorrect value (0x%02X).", i[0] & 0x7f); hfm_at_wrong_value = 1; } else { LOG_DEBUG ("HFM CLK divisor was already set to correct value (0x%02X).", i[0] & 0x7f); return ERROR_OK; } /* write HFM_CLKD */ retval = core_move_value_at_r2(target, HFM_CLK_DEFAULT); err_check_propagate(retval); /* verify HFM_CLKD */ retval = core_move_at_r2_to_y0(target); err_check_propagate(retval); retval = core_move_y0_at_r0(target); err_check_propagate(retval); retval = core_rx_upper_data(target, i); err_check_propagate(retval); if (i[0] != (0x80 | (HFM_CLK_DEFAULT & 0x7f))) { retval = ERROR_TARGET_FAILURE; err_check(retval, DSP5680XX_ERROR_FM_SET_CLK, "Unable to set HFM CLK divisor."); } if (hfm_at_wrong_value) LOG_DEBUG("HFM CLK divisor set to 0x%02x.", i[0] & 0x7f); return ERROR_OK; } /** * Executes the FM calculate signature command. The FM will calculate over the * data from @a address to @a address + @a words -1. The result is written to a * register, then read out by this function and returned in @a signature. The * value @a signature may be compared to the one returned by perl_crc to * verify the flash was written correctly. * * @param target * @param address Start of flash array where the signature should be calculated. * @param words Number of words over which the signature should be calculated. * @param signature Value calculated by the FM. * * @return */ static int dsp5680xx_f_signature(struct target *target, uint32_t address, uint32_t words, uint16_t *signature) { int retval; uint16_t hfm_ustat; if (!dsp5680xx_context.debug_mode_enabled) { retval = eonce_enter_debug_mode_without_reset(target, NULL); /* * Generate error here, since it is not done in eonce_enter_debug_mode_without_reset */ err_check(retval, DSP5680XX_ERROR_HALT, "Failed to halt target."); } retval = dsp5680xx_f_ex(target, HFM_CALCULATE_DATA_SIGNATURE, address, words, &hfm_ustat, 1); err_check_propagate(retval); retval = dsp5680xx_read_16_single(target, HFM_BASE_ADDR | HFM_DATA, (uint8_t *) signature, 0); return retval; } int dsp5680xx_f_erase_check(struct target *target, uint8_t *erased, uint32_t sector) { int retval; uint16_t hfm_ustat; uint32_t tmp; if (!dsp5680xx_context.debug_mode_enabled) { retval = dsp5680xx_halt(target); err_check_propagate(retval); } retval = set_fm_ck_div(target); err_check_propagate(retval); /* * Check if chip is already erased. */ tmp = HFM_FLASH_BASE_ADDR + sector * HFM_SECTOR_SIZE / 2; retval = dsp5680xx_f_ex(target, HFM_ERASE_VERIFY, tmp, 0, &hfm_ustat, 1); err_check_propagate(retval); if (erased) *erased = (uint8_t) (hfm_ustat & HFM_USTAT_MASK_BLANK); return retval; } /** * Executes the FM page erase command. * * @param target * @param sector Page to erase. * @param hfm_ustat FM module status register. * * @return */ static int erase_sector(struct target *target, int sector, uint16_t *hfm_ustat) { int retval; uint32_t tmp = HFM_FLASH_BASE_ADDR + sector * HFM_SECTOR_SIZE / 2; retval = dsp5680xx_f_ex(target, HFM_PAGE_ERASE, tmp, 0, hfm_ustat, 1); err_check_propagate(retval); return retval; } /** * Executes the FM mass erase command. Erases the flash array completely. * * @param target * @param hfm_ustat FM module status register. * * @return */ static int mass_erase(struct target *target, uint16_t *hfm_ustat) { int retval; retval = dsp5680xx_f_ex(target, HFM_MASS_ERASE, 0, 0, hfm_ustat, 1); return retval; } int dsp5680xx_f_erase(struct target *target, int first, int last) { int retval; if (!dsp5680xx_context.debug_mode_enabled) { retval = dsp5680xx_halt(target); err_check_propagate(retval); } /* * Reset SIM * */ retval = dsp5680xx_f_sim_reset(target); err_check_propagate(retval); /* * Set hfmdiv * */ retval = set_fm_ck_div(target); err_check_propagate(retval); uint16_t hfm_ustat; int do_mass_erase = ((!(first | last)) || ((first == 0) && (last == (HFM_SECTOR_COUNT - 1)))); if (do_mass_erase) { /* Mass erase */ retval = mass_erase(target, &hfm_ustat); err_check_propagate(retval); } else { for (int i = first; i <= last; i++) { retval = erase_sector(target, i, &hfm_ustat); err_check_propagate(retval); } } return ERROR_OK; } /* * Algorithm for programming normal p: flash * Follow state machine from "56F801x Peripheral Reference Manual"@163. * Registers to set up before calling: * r0: TX/RX high address. * r2: FM module base address. * r3: Destination address in flash. * * hfm_wait: // wait for buffer empty * brclr #0x80, x:(r2+0x13), hfm_wait * rx_check: // wait for input buffer full * brclr #0x01, x:(r0-2), rx_check * move.w x:(r0), y0 // read from Rx buffer * move.w y0, p:(r3)+ * move.w #0x20, x:(r2+0x14) // write PGM command * move.w #0x80, x:(r2+0x13) // start the command * move.w X:(R2+0x13), A // Read USTAT register * brclr #0x20, A, accerr_check // protection violation check * bfset #0x20, X:(R2+0x13) // clear pviol * bra hfm_wait * accerr_check: * brclr #0x10, A, hfm_wait // access error check * bfset #0x10, X:(R2+0x13) // clear accerr * bra hfm_wait // loop * 0x00000000 0x8A460013807D brclr #0x80, X:(R2+0x13),*+0 * 0x00000003 0xE700 nop * 0x00000004 0xE700 nop * 0x00000005 0x8A44FFFE017B brclr #1, X:(R0-2),*-2 * 0x00000008 0xE700 nop * 0x00000009 0xF514 move.w X:(R0), Y0 * 0x0000000A 0x8563 move.w Y0, P:(R3)+ * 0x0000000B 0x864600200014 move.w #32, X:(R2+0x14) * 0x0000000E 0x864600800013 move.w #128, X:(R2+0x13) * 0x00000011 0xF0420013 move.w X:(R2+0x13), A * 0x00000013 0x8B402004 brclr #0x20, A,*+6 * 0x00000015 0x824600130020 bfset #0x20, X:(R2+0x13) * 0x00000018 0xA967 bra *-24 * 0x00000019 0x8B401065 brclr #0x10, A,*-25 * 0x0000001B 0x824600130010 bfset #0x10, X:(R2+0x13) * 0x0000001E 0xA961 bra *-30 */ static const uint16_t pgm_write_pflash[] = { 0x8A46, 0x0013, 0x807D, 0xE700, 0xE700, 0x8A44, 0xFFFE, 0x017B, 0xE700, 0xF514, 0x8563, 0x8646, 0x0020, 0x0014, 0x8646, 0x0080, 0x0013, 0xF042, 0x0013, 0x8B40, 0x2004, 0x8246, 0x0013, 0x0020, 0xA967, 0x8B40, 0x1065, 0x8246, 0x0013, 0x0010, 0xA961 }; static const uint32_t pgm_write_pflash_length = 31; int dsp5680xx_f_wr(struct target *t, const uint8_t *b, uint32_t a, uint32_t count, int is_flash_lock) { struct target *target = t; uint32_t address = a; const uint8_t *buffer = b; int retval = ERROR_OK; if (!dsp5680xx_context.debug_mode_enabled) { retval = eonce_enter_debug_mode(target, NULL); err_check_propagate(retval); } /* * Download the pgm that flashes. * */ const uint32_t len = pgm_write_pflash_length; uint32_t ram_addr = 0x8700; /* * This seems to be a safe address. * This one is the one used by codewarrior in 56801x_flash.cfg */ if (!is_flash_lock) { retval = dsp5680xx_write(target, ram_addr, 1, len * 2, (uint8_t *) pgm_write_pflash); err_check_propagate(retval); retval = dsp5680xx_execute_queue(); err_check_propagate(retval); } /* * Set hfmdiv * */ retval = set_fm_ck_div(target); err_check_propagate(retval); /* * Setup registers needed by pgm_write_pflash * */ dsp5680xx_context.flush = 0; retval = core_move_long_to_r3(target, address); /* Destination address to r3 */ err_check_propagate(retval); core_load_tx_rx_high_addr_to_r0(target); /* TX/RX reg address to r0 */ err_check_propagate(retval); retval = core_move_long_to_r2(target, HFM_BASE_ADDR); /* FM base address to r2 */ err_check_propagate(retval); /* * Run flashing program. * */ /* write to HFM_CNFG (lock=0, select bank) */ retval = core_move_value_at_r2_disp(target, 0x00, HFM_CNFG); err_check_propagate(retval); /* write to HMF_USTAT, clear PVIOL, ACCERR &BLANK bits */ retval = core_move_value_at_r2_disp(target, 0x04, HFM_USTAT); err_check_propagate(retval); /* clear only one bit at a time */ retval = core_move_value_at_r2_disp(target, 0x10, HFM_USTAT); err_check_propagate(retval); retval = core_move_value_at_r2_disp(target, 0x20, HFM_USTAT); err_check_propagate(retval); /* write to HMF_PROT, clear protection */ retval = core_move_value_at_r2_disp(target, 0x00, HFM_PROT); err_check_propagate(retval); /* write to HMF_PROTB, clear protection */ retval = core_move_value_at_r2_disp(target, 0x00, HFM_PROTB); err_check_propagate(retval); if (count % 2) { /* TODO implement handling of odd number of words. */ retval = ERROR_FAIL; const char *msg = "Cannot handle odd number of words."; err_check(retval, DSP5680XX_ERROR_FLASHING_INVALID_WORD_COUNT, msg); } dsp5680xx_context.flush = 1; retval = dsp5680xx_execute_queue(); err_check_propagate(retval); uint32_t drscan_data; uint16_t tmp = (buffer[0] | (buffer[1] << 8)); retval = core_tx_upper_data(target, tmp, &drscan_data); err_check_propagate(retval); retval = dsp5680xx_resume(target, 0, ram_addr, 0, 0); err_check_propagate(retval); int counter = FLUSH_COUNT_FLASH; dsp5680xx_context.flush = 0; uint32_t i; for (i = 1; (i < count / 2) && (i < HFM_SIZE_WORDS); i++) { if (--counter == 0) { dsp5680xx_context.flush = 1; counter = FLUSH_COUNT_FLASH; } tmp = (buffer[2 * i] | (buffer[2 * i + 1] << 8)); retval = core_tx_upper_data(target, tmp, &drscan_data); if (retval != ERROR_OK) { dsp5680xx_context.flush = 1; err_check_propagate(retval); } dsp5680xx_context.flush = 0; } dsp5680xx_context.flush = 1; if (!is_flash_lock) { /* *Verify flash (skip when exec lock sequence) * */ uint16_t signature; uint16_t pc_crc; retval = dsp5680xx_f_signature(target, address, i, &signature); err_check_propagate(retval); pc_crc = perl_crc(buffer, i); if (pc_crc != signature) { retval = ERROR_FAIL; const char *msg = "Flashed data failed CRC check, flash again!"; err_check(retval, DSP5680XX_ERROR_FLASHING_CRC, msg); } } return retval; } int dsp5680xx_f_unlock(struct target *target) { int retval = ERROR_OK; uint16_t eonce_status; uint32_t instr; uint32_t ir_out; struct jtag_tap *tap_chp; struct jtag_tap *tap_cpu; tap_chp = jtag_tap_by_string("dsp568013.chp"); if (!tap_chp) { retval = ERROR_FAIL; err_check(retval, DSP5680XX_ERROR_JTAG_TAP_ENABLE_MASTER, "Failed to get master tap."); } tap_cpu = jtag_tap_by_string("dsp568013.cpu"); if (!tap_cpu) { retval = ERROR_FAIL; err_check(retval, DSP5680XX_ERROR_JTAG_TAP_ENABLE_CORE, "Failed to get master tap."); } retval = eonce_enter_debug_mode_without_reset(target, &eonce_status); if (retval == ERROR_OK) LOG_WARNING("Memory was not locked."); jtag_add_reset(0, 1); jtag_add_sleep(TIME_DIV_FREESCALE * 200 * 1000); retval = reset_jtag(); err_check(retval, DSP5680XX_ERROR_JTAG_RESET, "Failed to reset JTAG state machine"); jtag_add_sleep(150); /* Enable core tap */ tap_chp->enabled = true; retval = switch_tap(target, tap_chp, tap_cpu); err_check_propagate(retval); instr = JTAG_INSTR_DEBUG_REQUEST; retval = dsp5680xx_irscan(target, &instr, &ir_out, DSP5680XX_JTAG_CORE_TAP_IRLEN); err_check_propagate(retval); jtag_add_sleep(TIME_DIV_FREESCALE * 100 * 1000); jtag_add_reset(0, 0); jtag_add_sleep(TIME_DIV_FREESCALE * 300 * 1000); /* Enable master tap */ tap_chp->enabled = false; retval = switch_tap(target, tap_chp, tap_cpu); err_check_propagate(retval); /* Execute mass erase to unlock */ instr = MASTER_TAP_CMD_FLASH_ERASE; retval = dsp5680xx_irscan(target, &instr, &ir_out, DSP5680XX_JTAG_MASTER_TAP_IRLEN); err_check_propagate(retval); instr = HFM_CLK_DEFAULT; retval = dsp5680xx_drscan(target, (uint8_t *) &instr, (uint8_t *) &ir_out, 16); err_check_propagate(retval); jtag_add_sleep(TIME_DIV_FREESCALE * 150 * 1000); jtag_add_reset(0, 1); jtag_add_sleep(TIME_DIV_FREESCALE * 200 * 1000); retval = reset_jtag(); err_check(retval, DSP5680XX_ERROR_JTAG_RESET, "Failed to reset JTAG state machine"); jtag_add_sleep(150); instr = 0x0606ffff; retval = dsp5680xx_drscan(target, (uint8_t *) &instr, (uint8_t *) &ir_out, 32); err_check_propagate(retval); /* enable core tap */ instr = 0x5; retval = dsp5680xx_irscan(target, &instr, &ir_out, DSP5680XX_JTAG_MASTER_TAP_IRLEN); err_check_propagate(retval); instr = 0x2; retval = dsp5680xx_drscan(target, (uint8_t *) &instr, (uint8_t *) &ir_out, 4); err_check_propagate(retval); tap_cpu->enabled = true; tap_chp->enabled = false; target->state = TARGET_RUNNING; dsp5680xx_context.debug_mode_enabled = false; return retval; } int dsp5680xx_f_lock(struct target *target) { int retval; struct jtag_tap *tap_chp; struct jtag_tap *tap_cpu; uint16_t lock_word = HFM_LOCK_FLASH; retval = dsp5680xx_f_wr(target, (uint8_t *)&lock_word, HFM_LOCK_ADDR_L, 2, 1); err_check_propagate(retval); jtag_add_reset(0, 1); jtag_add_sleep(TIME_DIV_FREESCALE * 200 * 1000); retval = reset_jtag(); err_check(retval, DSP5680XX_ERROR_JTAG_RESET, "Failed to reset JTAG state machine"); jtag_add_sleep(TIME_DIV_FREESCALE * 100 * 1000); jtag_add_reset(0, 0); jtag_add_sleep(TIME_DIV_FREESCALE * 300 * 1000); tap_chp = jtag_tap_by_string("dsp568013.chp"); if (!tap_chp) { retval = ERROR_FAIL; err_check(retval, DSP5680XX_ERROR_JTAG_TAP_ENABLE_MASTER, "Failed to get master tap."); } tap_cpu = jtag_tap_by_string("dsp568013.cpu"); if (!tap_cpu) { retval = ERROR_FAIL; err_check(retval, DSP5680XX_ERROR_JTAG_TAP_ENABLE_CORE, "Failed to get master tap."); } target->state = TARGET_RUNNING; dsp5680xx_context.debug_mode_enabled = false; tap_cpu->enabled = false; tap_chp->enabled = true; retval = switch_tap(target, tap_chp, tap_cpu); return retval; } static int dsp5680xx_step(struct target *target, int current, target_addr_t address, int handle_breakpoints) { err_check(ERROR_FAIL, DSP5680XX_ERROR_NOT_IMPLEMENTED_STEP, "Not implemented yet."); } /** Holds methods for dsp5680xx targets. */ struct target_type dsp5680xx_target = { .name = "dsp5680xx", .poll = dsp5680xx_poll, .arch_state = dsp5680xx_arch_state, .halt = dsp5680xx_halt, .resume = dsp5680xx_resume, .step = dsp5680xx_step, .write_buffer = dsp5680xx_write_buffer, .read_buffer = dsp5680xx_read_buffer, .assert_reset = dsp5680xx_assert_reset, .deassert_reset = dsp5680xx_deassert_reset, .soft_reset_halt = dsp5680xx_soft_reset_halt, .read_memory = dsp5680xx_read, .write_memory = dsp5680xx_write, .checksum_memory = dsp5680xx_checksum_memory, .target_create = dsp5680xx_target_create, .init_target = dsp5680xx_init_target, };