|
From: <ge...@op...> - 2017-02-10 09:27:04
|
This is an automated email from Gerrit. Angelo Dureghello (an...@sy...) just uploaded a new patch set to Gerrit, which you can find at http://openocd.zylin.com/3973 -- gerrit commit 4309b9507671bd75ff8ca315fd47105dd6a15b39 Author: Angelo Dureghello <an...@sy...> Date: Fri Feb 10 10:26:09 2017 +0100 add support for Coldfire BDM (Coldfire v2-3-4) and mcf5441x - add support for BDM 26pin transport (known as v2-3-4) - add support for P&E Multilink Universal USB programmer - add support for mcf5441x coldfire target family Change-Id: Icbde349771e3ea35c055201d922259e5169f3e14 Signed-off-by: Angelo Dureghello <an...@sy...> diff --git a/configure.ac b/configure.ac index 2543eca..4984f78 100644 --- a/configure.ac +++ b/configure.ac @@ -118,6 +118,7 @@ m4_define([USB1_ADAPTERS], m4_define([USB_ADAPTERS], [[[osbdm], [OSBDM (JTAG only) Programmer], [OSBDM]], + [[pemu], [P&E Multilink Universal BDM Programmer], [PEMU]], [[opendous], [eStick/opendous JTAG Programmer], [OPENDOUS]], [[aice], [Andes JTAG Programmer], [AICE]]]) diff --git a/src/helper/binarybuffer.h b/src/helper/binarybuffer.h index f1da8c4..0001d5a 100644 --- a/src/helper/binarybuffer.h +++ b/src/helper/binarybuffer.h @@ -31,6 +31,33 @@ /** * Sets @c num bits in @c _buffer, starting at the @c first bit, * using the bits in @c value. This routine fast-paths writes + * of little-endian, byte-aligned, 16-bit words. + * @param _buffer The buffer whose bits will be set. + * @param first The bit offset in @c _buffer to start writing (0-15). + * @param num The number of bits from @c value to copy (1-16). + * @param value Up to 16 bits that will be copied to _buffer. + */ +static inline void buf_set_u16(void *_buffer, + unsigned first, unsigned num, uint32_t value) +{ + uint8_t *buffer = _buffer; + + if ((num == 16) && (first == 0)) { + buffer[1] = (value >> 8) & 0xff; + buffer[0] = (value >> 0) & 0xff; + } else { + for (unsigned i = first; i < first + num; i++) { + if (((value >> (i - first)) & 1) == 1) + buffer[i / 8] |= 1 << (i % 8); + else + buffer[i / 8] &= ~(1 << (i % 8)); + } + } +} + +/** + * Sets @c num bits in @c _buffer, starting at the @c first bit, + * using the bits in @c value. This routine fast-paths writes * of little-endian, byte-aligned, 32-bit words. * @param _buffer The buffer whose bits will be set. * @param first The bit offset in @c _buffer to start writing (0-31). @@ -97,6 +124,33 @@ static inline void buf_set_u64(uint8_t *_buffer, /** * Retrieves @c num bits from @c _buffer, starting at the @c first bit, + * returning the bits in a 16-bit word. This routine fast-paths reads + * of little-endian, byte-aligned, 16-bit words. + * @param _buffer The buffer whose bits will be read. + * @param first The bit offset in @c _buffer to start reading (0-15). + * @param num The number of bits from @c _buffer to read (1-16). + * @returns Up to 32-bits that were read from @c _buffer. + */ +static inline uint16_t buf_get_u16(const void *_buffer, + unsigned first, unsigned num) +{ + const uint8_t *buffer = _buffer; + + if ((num == 16) && (first == 0)) { + return ((uint16_t)buffer[1]) << 8 | + ((uint16_t)buffer[0]) << 0; + } else { + uint16_t result = 0; + for (unsigned i = first; i < first + num; i++) { + if (((buffer[i / 8] >> (i % 8)) & 1) == 1) + result |= 1 << (i - first); + } + return result; + } +} + +/** + * Retrieves @c num bits from @c _buffer, starting at the @c first bit, * returning the bits in a 32-bit word. This routine fast-paths reads * of little-endian, byte-aligned, 32-bit words. * @param _buffer The buffer whose bits will be read. diff --git a/src/jtag/core.c b/src/jtag/core.c index 8c79eb2..f0e870d 100644 --- a/src/jtag/core.c +++ b/src/jtag/core.c @@ -57,6 +57,9 @@ static void jtag_add_scan_check(struct jtag_tap *active, tap_state_t state), int in_num_fields, struct scan_field *in_fields, tap_state_t state); + +bool transport_is_bdm_cf26(void); + /** * The jtag_error variable is set when an error occurs while executing * the queue. Application code may set this using jtag_set_error(), @@ -1483,6 +1486,52 @@ int adapter_quit(void) return ERROR_OK; } +void bdm_cf26_add_reset(int req_srst) +{ + /* Maybe change SRST signal state */ + if (jtag_srst != req_srst) { + int retval; + + retval = interface_jtag_add_reset(0, req_srst); + if (retval != ERROR_OK) + jtag_set_error(retval); + else + retval = jtag_execute_queue(); + + if (retval != ERROR_OK) { + LOG_ERROR("TRST/SRST error"); + return; + } + + /* SRST resets everything hooked up to that signal */ + jtag_srst = req_srst; + if (jtag_srst) { + LOG_DEBUG("SRST line asserted"); + if (adapter_nsrst_assert_width) + jtag_add_sleep(adapter_nsrst_assert_width * 1000); + } else { + LOG_DEBUG("SRST line released"); + if (adapter_nsrst_delay) + jtag_add_sleep(adapter_nsrst_delay * 1000); + } + } +} + +int bdm_cf26_init_reset(struct command_context *cmd_ctx) +{ + int ret = adapter_init(cmd_ctx); + if (ret != ERROR_OK) + return ret; + + LOG_DEBUG("Initializing with hard reset"); + + bdm_cf26_add_reset(1); + bdm_cf26_add_reset(0); + ret = jtag_execute_queue(); + + return ret; +} + int swd_init_reset(struct command_context *cmd_ctx) { int retval = adapter_init(cmd_ctx); @@ -1809,6 +1858,8 @@ void adapter_assert_reset(void) jtag_add_reset(0, 1); } else if (transport_is_swd()) swd_add_reset(1); + else if (transport_is_bdm_cf26()) + bdm_cf26_add_reset(1); else if (get_current_transport() != NULL) LOG_ERROR("reset is not supported on %s", get_current_transport()->name); @@ -1822,6 +1873,8 @@ void adapter_deassert_reset(void) jtag_add_reset(0, 0); else if (transport_is_swd()) swd_add_reset(0); + else if (transport_is_bdm_cf26()) + bdm_cf26_add_reset(0); else if (get_current_transport() != NULL) LOG_ERROR("reset is not supported on %s", get_current_transport()->name); diff --git a/src/jtag/drivers/Makefile.am b/src/jtag/drivers/Makefile.am index e411412..b7ad2d3 100644 --- a/src/jtag/drivers/Makefile.am +++ b/src/jtag/drivers/Makefile.am @@ -135,6 +135,9 @@ endif if OSBDM DRIVERFILES += %D%/osbdm.c endif +if PEMU +DRIVERFILES += %D%/pe_mu_usb.c +endif if OPENDOUS DRIVERFILES += %D%/opendous.c endif diff --git a/src/jtag/drivers/pe_mu_usb.c b/src/jtag/drivers/pe_mu_usb.c new file mode 100644 index 0000000..99736c9 --- /dev/null +++ b/src/jtag/drivers/pe_mu_usb.c @@ -0,0 +1,747 @@ +/*************************************************************************** + * * + * Copyright (C) 2017 by Angelo Dureghello * + * an...@sy... * * + * * + * 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 <jtag/drivers/libusb_common.h> +#include <jtag/interface.h> +#include <jtag/tcl.h> +#include <helper/log.h> +#include <target/bdm_cf26.h> + +#include "libusb_common.h" + +/* size of usb communication buffers */ +#define PEMU_MAX_PKT_SIZE 1280 +#define PEMU_MAX_BIG_BLOCK 0x4a8 +#define PEMU_STD_PKT_SIZE 256 + +#define PEMU_USB_BUFSIZE (PEMU_MAX_PKT_SIZE + 1) +/* initialize P&E Multilink Universal device */ +#define PEMU_CMD_INIT 0x11 +/* timeout */ +#define PEMU_USB_TIMEOUT 1000 +/* len of reply */ +#define PEMU_CMD_REPLY_LEN 4 + +static const uint16_t pemu_vid[] = { 0x1357, 0 }; +static const uint16_t pemu_pid[] = { 0x0503, 0 }; + +static const char REP_VERSION_INFO[] = {0x99, 0x66, 0x00, 0x64}; + +enum pemu_pkt_types { + PEMU_PT_GENERIC = 0xaa55, + PEMU_PT_CMD = 0xab56, + PEMU_PT_WBLOCK = 0xbc67, +}; + +enum { + CMD_TYPE_GENERIC = 0x01, + CMD_TYPE_IFACE = 0x04, + CMD_TYPE_DATA = 0x07, +}; + +enum { + CMD_PEMU_RESET = 0x01, + CMD_PEMU_GO = 0x02, + CMD_PEMU_BDM_MEM_R = 0x11, + CMD_PEMU_BDM_MEM_W = 0x15, + CMD_PEMU_BDM_REG_R = 0x13, + CMD_PEMU_BDM_REG_W = 0x16, + CMD_PEMU_BDM_SCR_W = 0x14, + CMD_PEMU_GET_ALL_CPU_REGS = 0x18, + CMD_PEMU_W_MEM_BLOCK = 0x19, + CMD_PEMU_GET_VERSION_STR = 0x0b, +}; + +struct pemu { + struct bdm_cf26_driver bdm_cf26; + /* usb handle */ + struct jtag_libusb_device_handle *devh; + /* send and receive buffer */ + uint8_t buffer[PEMU_USB_BUFSIZE]; + /* count data to send and to read */ + unsigned int count; + /* endpoints */ + unsigned int endpoint_in; + unsigned int endpoint_out; +}; + +#define OFFS_BDM_BUFF 5 + +/* pemu instance */ +static struct pemu pemu_context; + +extern struct jtag_interface pemu_interface; + +static int_least32_t pemu_read_pc(const struct bdm_cf26_driver *bdm); +static int pemu_write_pc(const struct bdm_cf26_driver *bdm, uint32_t value); + +static int get_version_field(uint8_t *buff, int pos, char *res) +{ + int p = 0; + char *c = (char *)buff, *q; + + for (pos--; p < pos; p++) { + c = strchr(c, ','); + if (!c) + return ERROR_FAIL; + c++; + } + + q = strchr(c, ','); + memcpy(res, c, q - c); + buff[q - c] = 0; + + return ERROR_OK; +} + +static int pemu_send_and_recv(struct pemu *pemu) +{ + unsigned int count; + + /* send request */ + count = jtag_libusb_bulk_write(pemu->devh, + pemu->endpoint_out | LIBUSB_ENDPOINT_OUT, + (char *)pemu->buffer, pemu->count, PEMU_USB_TIMEOUT); + + if (count != pemu->count) { + LOG_ERROR("PEMU communication error: can't write"); + return ERROR_FAIL; + } + + count = jtag_libusb_bulk_read(pemu->devh, + pemu->endpoint_in | LIBUSB_ENDPOINT_IN , + (char *)pemu->buffer, + PEMU_STD_PKT_SIZE, PEMU_USB_TIMEOUT * 3); + + if (count != PEMU_STD_PKT_SIZE) { + LOG_ERROR("PEMU communication error: can't read"); + return ERROR_FAIL; + } + + return ERROR_OK; +} + +static int pemu_open(struct pemu *pemu) +{ + (void)memset(pemu, 0, sizeof(*pemu)); + + if (jtag_libusb_open(pemu_vid, pemu_pid, NULL, &pemu->devh) + != ERROR_OK) { + + LOG_ERROR("P&E Multilink Universal interface not found"); + + return ERROR_FAIL; + } + + LOG_INFO("P&E Multilink Universal interface detected"); + + if (jtag_libusb_claim_interface(pemu->devh, 0) != ERROR_OK) + return ERROR_FAIL; + + return ERROR_OK; +} + +static inline uint8_t *pemu_get_bdm_buff(struct pemu *bdev) +{ + return bdev->buffer + OFFS_BDM_BUFF; +} + +static int pemu_send_generic(struct pemu *bdev, uint8_t cmd_type, uint16_t len) +{ + h_u16_to_be(&bdev->buffer[0], PEMU_PT_CMD); + h_u16_to_be(&bdev->buffer[2], len + 1); + bdev->buffer[4] = cmd_type; + bdev->count = PEMU_STD_PKT_SIZE; + + if (pemu_send_and_recv(bdev) != ERROR_OK) + return ERROR_FAIL; + + return ERROR_OK; +} + +static int pemu_assert_reset(const struct bdm_cf26_driver *bdm) +{ + struct pemu *bdev = container_of(bdm, struct pemu, bdm_cf26); + uint8_t *buff = pemu_get_bdm_buff(bdev); + + buff[0] = CMD_PEMU_RESET; + buff[1] = 0xf0; + + return pemu_send_generic(bdev, CMD_TYPE_DATA, 2); +} + +static int pemu_deassert_reset(const struct bdm_cf26_driver *bdm) +{ + struct pemu *bdev = container_of(bdm, struct pemu, bdm_cf26); + uint8_t *buff = pemu_get_bdm_buff(bdev); + + buff[0] = CMD_PEMU_RESET; + buff[1] = 0xf8; + + return pemu_send_generic(bdev, CMD_TYPE_DATA, 2); +} + +static int pemu_halt(const struct bdm_cf26_driver *bdm) +{ + struct pemu *bdev = container_of(bdm, struct pemu, bdm_cf26); + uint8_t *buff = pemu_get_bdm_buff(bdev); + + buff[0] = CMD_PEMU_RESET; + buff[1] = 0xf8; + + return pemu_send_generic(bdev, CMD_TYPE_DATA, 2); +} + +static int pemu_go(const struct bdm_cf26_driver *bdm) +{ + struct pemu *bdev = container_of(bdm, struct pemu, bdm_cf26); + uint8_t *buff = pemu_get_bdm_buff(bdev); + + buff[0] = CMD_PEMU_GO; + buff[1] = 0xfc; + h_u16_to_be(&buff[2], CMD_BDM_GO); + + pemu_send_generic(bdev, CMD_TYPE_DATA, 4); + + buff[0] = CMD_PEMU_BDM_REG_R; + h_u16_to_be(&buff[1], CMD_BDM_READ_BDM_REG); + + pemu_send_generic(bdev, CMD_TYPE_DATA, 3); + + return ERROR_OK; +} + +static int pemu_get_versions(struct pemu *bdev) +{ + char version_fw[16]; + + h_u16_to_be(&bdev->buffer[0], PEMU_PT_GENERIC); + h_u16_to_be(&bdev->buffer[2], 2); + bdev->buffer[4] = CMD_TYPE_GENERIC; + bdev->buffer[5] = CMD_PEMU_GET_VERSION_STR; + + bdev->count = PEMU_STD_PKT_SIZE; + + if (pemu_send_and_recv(bdev) != ERROR_OK) + return ERROR_FAIL; + + if (memcmp(bdev->buffer, + REP_VERSION_INFO, sizeof(REP_VERSION_INFO)) != 0) { + LOG_ERROR("PEMU communication error: can't get version msg"); + return ERROR_FAIL; + } + + if (get_version_field(&bdev->buffer[PEMU_CMD_REPLY_LEN], + 7, version_fw) != ERROR_OK) { + LOG_ERROR("PEMU communication error: can't get version info"); + return ERROR_FAIL; + } + + LOG_INFO("P&E Multilink Universal fw version %s", version_fw); + + if (strcmp(version_fw, "9.60") != 0) { + LOG_ERROR("P&E Multilink Universal wrong FW version, " + "please flash correct CFv234 firmware." + ); + return ERROR_FAIL; + } + + return ERROR_OK; +} + +static int_least32_t pemu_read_scr_reg(const struct bdm_cf26_driver *bdm, + uint16_t reg) +{ + uint32_t value; + struct pemu *bdev = container_of(bdm, struct pemu, bdm_cf26); + uint8_t *buff = pemu_get_bdm_buff(bdev); + + buff[0] = CMD_PEMU_BDM_MEM_R; + h_u16_to_be(&buff[1], CMD_BDM_READ_SCM_REG); + h_u32_to_be(&buff[3], reg); + + pemu_send_generic(bdev, CMD_TYPE_DATA, 7); + + value = be_to_h_u32(buff); + + return value; +} + +static int pemu_write_scr_reg(const struct bdm_cf26_driver *bdm, + uint16_t reg, uint32_t value) +{ + struct pemu *bdev = container_of(bdm, struct pemu, bdm_cf26); + uint8_t *buff = pemu_get_bdm_buff(bdev); + + buff[0] = CMD_PEMU_BDM_SCR_W; + h_u16_to_be(&buff[1], CMD_BDM_WRITE_SCM_REG); + h_u32_to_be(&buff[3], reg); + h_u32_to_be(&buff[7], value); + + pemu_send_generic(bdev, CMD_TYPE_DATA, 11); + + return ERROR_OK; +} + +static int_least32_t pemu_read_pc(const struct bdm_cf26_driver *bdm) +{ + return pemu_read_scr_reg(bdm, SYSC_PC); +} + +static int pemu_write_pc(const struct bdm_cf26_driver *bdm, + uint32_t value) +{ + return pemu_write_scr_reg(bdm, SYSC_PC, value); +} + +/** + * Read all regs in one shot + * @param bdm_cf26 pointer to a gnenric BDM deivce + * @returns memory buffer with all regs + */ +static int pemu_read_all_cpu_regs(const struct bdm_cf26_driver *bdm, + uint8_t **reg_buf) +{ + struct pemu *bdev = container_of(bdm, struct pemu, bdm_cf26); + uint8_t *buff = pemu_get_bdm_buff(bdev); + + buff[0] = CMD_PEMU_GET_ALL_CPU_REGS; + + if (pemu_send_generic(bdev, CMD_TYPE_DATA, 1) == ERROR_OK) { + + *reg_buf = buff; + + return ERROR_OK; + } + + return ERROR_FAIL; +} + +/** + * Read a BDM module register + * @param bdm_cf26 pointer to a gnenric BDM deivce + * @param reg 00 to 1f register to read + * @returns 8-bit memory contents, negative error code failure + */ +static int_least32_t pemu_read_dm_reg(const struct bdm_cf26_driver *bdm, + uint8_t reg) +{ + struct pemu *bdev = container_of(bdm, struct pemu, bdm_cf26); + uint8_t *buff = pemu_get_bdm_buff(bdev); + + buff[0] = CMD_PEMU_BDM_REG_R; + h_u16_to_be(&buff[1], CMD_BDM_READ_BDM_REG | reg); + + pemu_send_generic(bdev, CMD_TYPE_DATA, 3); + + return be_to_h_u32(buff); +} + +/** + * Read a A/D CPU register + * @param bdm_cf26 pointer to a gnenric BDM deivce + * @param reg AD register to read + * @returns 8-bit memory contents, negative error code failure + */ +static int_least32_t pemu_read_ad_reg(const struct bdm_cf26_driver *bdm, + uint8_t reg) +{ + struct pemu *bdev = container_of(bdm, struct pemu, bdm_cf26); + uint8_t *buff = pemu_get_bdm_buff(bdev); + + buff[0] = CMD_PEMU_BDM_REG_R; + h_u16_to_be(&buff[1], CMD_BDM_READ_CPU_AD_REG | reg); + + pemu_send_generic(bdev, CMD_TYPE_DATA, 3); + + return be_to_h_u32(buff); +} + +/** + * Read a byte from CPU memory location + * @param bdm_cf26 pointer to a gnenric BDM deivce + * @param address address to read the data form + * @returns 8-bit memory contents, negative error code failure + */ +static int_least8_t pemu_read_mem_byte(const struct bdm_cf26_driver *bdm, + uint32_t address) +{ + int value; + struct pemu *bdev = container_of(bdm, struct pemu, bdm_cf26); + uint8_t *buff = pemu_get_bdm_buff(bdev); + + buff[0] = CMD_PEMU_BDM_MEM_R; + h_u16_to_be(&buff[1], CMD_BDM_READ_MEM_BYTE); + h_u32_to_be(&buff[3], address); + + pemu_send_generic(bdev, CMD_TYPE_DATA, 7); + + /* + * heh, this command (read byte) returns anyway a short, not a byte + */ + value = be_to_h_u16(buff); + + return (uint8_t)(value); +} + +/** + * Read a word (16 bit) from CPU memory location + * @param bdm_cf26 pointer to a gnenric BDM deivce + * @param address address to read the data form + * @returns 16-bit memory contents, negative error code failure + */ +static int_least16_t pemu_read_mem_word(const struct bdm_cf26_driver *bdm, + uint32_t address) +{ + int value; + struct pemu *bdev = container_of(bdm, struct pemu, bdm_cf26); + uint8_t *buff = pemu_get_bdm_buff(bdev); + + buff[0] = CMD_PEMU_BDM_MEM_R; + h_u16_to_be(&buff[1], CMD_BDM_READ_MEM_WORD); + h_u32_to_be(&buff[3], address); + + pemu_send_generic(bdev, CMD_TYPE_DATA, 7); + + /* + * heh, this command (read byte) returns anyway a short, not a byte + */ + value = be_to_h_u16(buff); + + return (uint16_t)value; +} + +/** + * Read a long word (32-bit) from CPU memory location + * @param bdm_cf26 pointer to a gnenric BDM deivce + * @param address address to read the data form + * @returns 328-bit memory contents, negative error code failure + */ +static int_least32_t pemu_read_mem_long(const struct bdm_cf26_driver *bdm, + uint32_t address) +{ + struct pemu *bdev = container_of(bdm, struct pemu, bdm_cf26); + uint8_t *buff = pemu_get_bdm_buff(bdev); + + buff[0] = CMD_PEMU_BDM_MEM_R; + h_u16_to_be(&buff[1], CMD_BDM_READ_MEM_LONG); + h_u32_to_be(&buff[3], address); + + pemu_send_generic(bdev, CMD_TYPE_DATA, 7); + + return be_to_h_u32(buff); +} + +/** + * Write a BDM module register + * @param reg of bdm module to write + * @param value address to write the data to + * @returns error code + */ +static int pemu_write_dm_reg(const struct bdm_cf26_driver *bdm, + uint8_t reg, uint32_t value) +{ + struct pemu *bdev = container_of(bdm, struct pemu, bdm_cf26); + uint8_t *buff = pemu_get_bdm_buff(bdev); + + buff[0] = CMD_PEMU_BDM_REG_W; + h_u16_to_be(&buff[1], CMD_BDM_WRITE_BDM_REG | reg); + h_u32_to_be(&buff[3], value); + + pemu_send_generic(bdev, CMD_TYPE_DATA, 7); + + return ERROR_OK; +} + +/** + * Write a cpu A/D register + * @param bdm_cf26 pointer to a gnenric BDM deivce + * @param address address to write the data to + * @returns error code + */ +static int pemu_write_ad_reg(const struct bdm_cf26_driver *bdm, + uint8_t reg, uint32_t value) +{ + struct pemu *bdev = container_of(bdm, struct pemu, bdm_cf26); + uint8_t *buff = pemu_get_bdm_buff(bdev); + + buff[0] = CMD_PEMU_BDM_REG_W; + h_u16_to_be(&buff[1], CMD_BDM_WRITE_CPU_AD_REG | reg); + h_u32_to_be(&buff[3], value); + + pemu_send_generic(bdev, CMD_TYPE_DATA, 7); + + return ERROR_OK; +} + +/** + * Write a byte at CPU memory location + * @param bdm_cf26 pointer to a gnenric BDM deivce + * @param address address to write the data to + * @returns error code + */ +static int pemu_write_mem_byte(const struct bdm_cf26_driver *bdm, + uint32_t address, uint8_t value) +{ + struct pemu *bdev = container_of(bdm, struct pemu, bdm_cf26); + uint8_t *buff = pemu_get_bdm_buff(bdev); + + buff[0] = CMD_PEMU_BDM_MEM_W; + h_u16_to_be(&buff[1], CMD_BDM_WRITE_MEM_BYTE); + h_u32_to_be(&buff[3], address); + /* + * heh, pemu wants a 16 bit value here + */ + h_u16_to_be(&buff[7], value); + + pemu_send_generic(bdev, CMD_TYPE_DATA, 11); + + return ERROR_OK; +} + +/** + * Write a word at CPU memory location + * @param bdm_cf26 pointer to a gnenric BDM deivce + * @param address address to write the data to + * @param value value to write + * @returns error code + */ +static int pemu_write_mem_word(const struct bdm_cf26_driver *bdm, + uint32_t address, uint16_t value) +{ + struct pemu *bdev = container_of(bdm, struct pemu, bdm_cf26); + uint8_t *buff = pemu_get_bdm_buff(bdev); + + buff[0] = CMD_PEMU_BDM_MEM_W; + h_u16_to_be(&buff[1], CMD_BDM_WRITE_MEM_WORD); + h_u32_to_be(&buff[3], address); + /* + * heh, pemu wants a 16 bit value here + */ + h_u16_to_be(&buff[7], value); + + pemu_send_generic(bdev, CMD_TYPE_DATA, 11); + + return ERROR_OK; +} + +/** + * Write a long at CPU memory location + * @param bdm_cf26 pointer to a gnenric BDM deivce + * @param address address to write the data to + * @param value value to write + * @returns error code + */ +static int pemu_write_mem_long(const struct bdm_cf26_driver *bdm, + uint32_t address, uint32_t value) +{ + struct pemu *bdev = container_of(bdm, struct pemu, bdm_cf26); + uint8_t *buff = pemu_get_bdm_buff(bdev); + + buff[0] = CMD_PEMU_BDM_MEM_W; + h_u16_to_be(&buff[1], CMD_BDM_WRITE_MEM_LONG); + h_u32_to_be(&buff[3], address); + h_u32_to_be(&buff[7], value); + + pemu_send_generic(bdev, CMD_TYPE_DATA, 11); + + return ERROR_OK; +} + +/** + * Dump memory block, using specific BDM commands + * @param bdm_cf26 pointer to a gnenric BDM deivce + * @param address address to read the data form + * @returns 8-bit memory contents, negative error code failure + */ +int pemu_read_memory(const struct bdm_cf26_driver *bdm, uint32_t address, + uint32_t size, uint32_t count, uint8_t *buffer) +{ + int len, longs, bytes; + + len = size * count; + longs = len / 4; + bytes = len % 4; + + while (longs--) { + h_u32_to_be(buffer, pemu_read_mem_long(bdm, address)); + address += sizeof(uint32_t); + buffer += sizeof(uint32_t); + } + while (bytes--) + *buffer++ = pemu_read_mem_byte(bdm, address++); + + return ERROR_OK; +} + +/** + * Write a memory buffer to a specific location + * @param bdm_cf26 pointer to a gnenric BDM deivce + * @param address address where to write the data (target sram) + * @param dlen length of data to write (in bytes) + * @param buffer source data buffer + * + * P&E m.u. has propertary all-inclusive bdm-fahter commands for memory write, + * i am assuming they are more performant then their bdm dump/write friends, + * so i am using them + */ +static void pemu_send_bigbuff(const struct bdm_cf26_driver *bdm, uint32_t address, + uint32_t dlen, const uint8_t *buffer) +{ + struct pemu *bdev = container_of(bdm, struct pemu, bdm_cf26); + uint16_t to_send, remainder; + + remainder = dlen % 4; + + while (dlen >= 4) { + if (dlen > PEMU_MAX_BIG_BLOCK) + to_send = PEMU_MAX_BIG_BLOCK; + else + to_send = dlen - remainder; + + h_u16_to_be(&bdev->buffer[0], PEMU_PT_WBLOCK); + bdev->buffer[4] = CMD_TYPE_DATA; + bdev->buffer[5] = CMD_PEMU_W_MEM_BLOCK; + h_u16_to_be(&bdev->buffer[2], to_send + 8); + h_u16_to_be(&bdev->buffer[6], to_send); + h_u32_to_be(&bdev->buffer[8], address); + + memcpy(&bdev->buffer[12], buffer, to_send); + + /* pemu wants a padded packet */ + bdev->count = PEMU_MAX_PKT_SIZE; + + pemu_send_and_recv(bdev); + + dlen -= to_send; + buffer += to_send; + address += to_send; + } + + while (remainder--) + pemu_write_mem_byte(bdm, address++, *buffer++); +} + +int pemu_write_memory(const struct bdm_cf26_driver *bdm, uint32_t address, + uint32_t size, uint32_t count, const uint8_t *buffer) +{ + pemu_send_bigbuff(bdm, address, count * size, buffer); + + return ERROR_OK; +} + +static int pemu_interface_init(void) +{ + int err; + + if (pemu_open(&pemu_context) != ERROR_OK) { + LOG_ERROR("Can't open PEMU device"); + return ERROR_FAIL; + } else { + LOG_DEBUG("PEMU init success"); + } + + err = jtag_libusb_choose_interface(pemu_context.devh, + &pemu_context.endpoint_in, + &pemu_context.endpoint_out, -1, -1, -1); + + if (!err) { + LOG_INFO("P&E Multilink Universal endpoints, in %u, out %u", + pemu_context.endpoint_in, + pemu_context.endpoint_out + ); + } else { + LOG_ERROR("P&E Multilink Universal, can't get endpoints"); + return ERROR_FAIL; + } + + pemu_assert_reset((const struct bdm_cf26_driver *)&pemu_context); + usleep(100000); + pemu_deassert_reset((const struct bdm_cf26_driver *)&pemu_context); + usleep(100000); + + if (pemu_get_versions(&pemu_context) != ERROR_OK) + return ERROR_FAIL; + + pemu_context.bdm_cf26.read_ad_reg = pemu_read_ad_reg; + pemu_context.bdm_cf26.read_dm_reg = pemu_read_dm_reg; + pemu_context.bdm_cf26.read_sc_reg = pemu_read_scr_reg; + pemu_context.bdm_cf26.read_mem_byte = pemu_read_mem_byte; + pemu_context.bdm_cf26.read_mem_word = pemu_read_mem_word; + pemu_context.bdm_cf26.read_mem_long = pemu_read_mem_long; + pemu_context.bdm_cf26.write_ad_reg = pemu_write_ad_reg; + pemu_context.bdm_cf26.write_dm_reg = pemu_write_dm_reg; + pemu_context.bdm_cf26.write_sc_reg = pemu_write_scr_reg; + pemu_context.bdm_cf26.write_mem_byte = pemu_write_mem_byte; + pemu_context.bdm_cf26.write_mem_word = pemu_write_mem_word; + pemu_context.bdm_cf26.write_mem_long = pemu_write_mem_long; + pemu_context.bdm_cf26.read_pc = pemu_read_pc; + pemu_context.bdm_cf26.write_pc = pemu_write_pc; + pemu_context.bdm_cf26.assert_reset = pemu_assert_reset; + pemu_context.bdm_cf26.deassert_reset = pemu_deassert_reset; + pemu_context.bdm_cf26.halt = pemu_halt; + pemu_context.bdm_cf26.go = pemu_go; + pemu_context.bdm_cf26.read_memory = pemu_read_memory; + pemu_context.bdm_cf26.write_memory = pemu_write_memory; + pemu_context.bdm_cf26.get_all_cpu_regs = pemu_read_all_cpu_regs; + + /* link interface <-> transport */ + pemu_interface.bdm_cf26 = (const struct bdm_cf26_driver *)&pemu_context; + + return ERROR_OK; +} + +static int pemu_interface_quit(void) +{ + jtag_libusb_close(pemu_context.devh); + + return ERROR_OK; +} + +static int pemu_interface_execute_queue(void) +{ + return ERROR_OK; +} + +static const struct command_registration pemu_interface_command_handlers[] = { + { + .name = "jtag", + .mode = COMMAND_ANY, + .usage = "", + }, + + COMMAND_REGISTRATION_DONE +}; + +const char *pemu_transports[] = { "bdm_cf26", NULL }; + +struct jtag_interface pemu_interface = { + .name = "pemu", + .supported = 0, + .init = pemu_interface_init, + .quit = pemu_interface_quit, + .transports = pemu_transports, + .commands = pemu_interface_command_handlers, + .execute_queue = pemu_interface_execute_queue, +}; diff --git a/src/jtag/interface.h b/src/jtag/interface.h index cdfc676..07a1549 100644 --- a/src/jtag/interface.h +++ b/src/jtag/interface.h @@ -183,6 +183,8 @@ static inline tap_state_t jtag_debug_state_machine(const void *tms_buf, } #endif /* _DEBUG_JTAG_IO_ */ +struct bdm_cf26_driver; + /** * Represents a driver for a debugging interface. * @@ -209,6 +211,7 @@ struct jtag_interface { const char * const *transports; const struct swd_driver *swd; + const struct bdm_cf26_driver *bdm_cf26; /** * Execute queued commands. diff --git a/src/jtag/interfaces.c b/src/jtag/interfaces.c index ad656a8..d86da24 100644 --- a/src/jtag/interfaces.c +++ b/src/jtag/interfaces.c @@ -108,6 +108,9 @@ extern struct jtag_interface hl_interface; #if BUILD_OSBDM == 1 extern struct jtag_interface osbdm_interface; #endif +#if BUILD_PEMU == 1 +extern struct jtag_interface pemu_interface; +#endif #if BUILD_OPENDOUS == 1 extern struct jtag_interface opendous_interface; #endif @@ -201,6 +204,9 @@ struct jtag_interface *jtag_interfaces[] = { #if BUILD_OSBDM == 1 &osbdm_interface, #endif +#if BUILD_PEMU == 1 + &pemu_interface, +#endif #if BUILD_OPENDOUS == 1 &opendous_interface, #endif diff --git a/src/target/Makefile.am b/src/target/Makefile.am index eb0d62e..64cec33 100644 --- a/src/target/Makefile.am +++ b/src/target/Makefile.am @@ -20,6 +20,7 @@ noinst_LTLIBRARIES += %D%/libtarget.la $(MIPS32_SRC) \ $(NDS32_SRC) \ $(INTEL_IA32_SRC) \ + $(BDM_CF26_SOURCES) \ %D%/avrt.c \ %D%/dsp563xx.c \ %D%/dsp563xx_once.c \ @@ -118,6 +119,10 @@ INTEL_IA32_SRC = \ %D%/lakemont.c \ %D%/x86_32_common.c +BDM_CF26_SOURCES = \ + %D%/bdm_cf26.c \ + %D%/mcf5441x.c + %C%_libtarget_la_SOURCES += \ %D%/algorithm.h \ %D%/arm.h \ diff --git a/src/target/bdm_cf26.c b/src/target/bdm_cf26.c new file mode 100644 index 0000000..3e185d9 --- /dev/null +++ b/src/target/bdm_cf26.c @@ -0,0 +1,331 @@ +/*************************************************************************** + * * + * Copyright (C) 2017 by Angelo Dureghello * + * an...@sy... * + * * + * 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; see the file COPYING. If not see * + * <http://www.gnu.org/licenses/> * + * * + ***************************************************************************/ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <transport/transport.h> +#include <jtag/jtag.h> +#include <jtag/interface.h> + +#include "target.h" +#include "bdm_cf26.h" +#include "log.h" + + +/* + * bdm 26 pin versions are A B B+ C D D+ D+PSTB + */ + +static const struct bdm_cf26_driver *target_to_bdm_cf26(struct target *target) +{ + return target->tap->priv; +} + +int_least32_t bdm_cf26_read_dm_reg(struct target *target, + enum bdm_cf26_registers reg) +{ + const struct bdm_cf26_driver *bdm = target_to_bdm_cf26(target); + + if (bdm->read_dm_reg) + return bdm->read_dm_reg(bdm, reg); + else + return ERROR_FAIL; +} + +int_least32_t bdm_cf26_read_ad_reg(struct target *target, + enum bdm_cf26_registers reg) +{ + const struct bdm_cf26_driver *bdm = target_to_bdm_cf26(target); + + if (bdm->read_ad_reg) + return bdm->read_ad_reg(bdm, reg); + else + return ERROR_FAIL; +} + +int_least32_t bdm_cf26_read_sc_reg(struct target *target, + enum cf_sysctl_registers reg) +{ + const struct bdm_cf26_driver *bdm = target_to_bdm_cf26(target); + + if (bdm->read_sc_reg) + return bdm->read_sc_reg(bdm, reg); + else + return ERROR_FAIL; +} + +int_least8_t bdm_cf26_read_mem_byte(struct target *target, uint32_t address) +{ + const struct bdm_cf26_driver *bdm = target_to_bdm_cf26(target); + + if (bdm->read_mem_byte) + return bdm->read_mem_byte(bdm, address); + + else + return ERROR_FAIL; +} + +int_least16_t bdm_cf26_read_mem_word(struct target *target, uint32_t address) +{ + const struct bdm_cf26_driver *bdm = target_to_bdm_cf26(target); + + if (bdm->read_mem_word) + return bdm->read_mem_word(bdm, address); + else + return ERROR_FAIL; +} + +int_least32_t bdm_cf26_read_mem_long(struct target *target, uint32_t address) +{ + const struct bdm_cf26_driver *bdm = target_to_bdm_cf26(target); + + if (bdm->read_mem_long) + return bdm->read_mem_long(bdm, address); + else + return ERROR_FAIL; +} + +int bdm_cf26_write_dm_reg(struct target *target, + enum bdm_cf26_registers reg, uint32_t value) +{ + const struct bdm_cf26_driver *bdm = target_to_bdm_cf26(target); + + if (bdm->write_dm_reg) + return bdm->write_dm_reg(bdm, reg, value); + else + return ERROR_FAIL; +} + +int bdm_cf26_write_ad_reg(struct target *target, + enum bdm_cf26_registers reg, uint32_t value) +{ + const struct bdm_cf26_driver *bdm = target_to_bdm_cf26(target); + + if (bdm->write_ad_reg) + return bdm->write_ad_reg(bdm, reg, value); + else + return ERROR_FAIL; +} + +int bdm_cf26_write_sc_reg(struct target *target, + enum cf_sysctl_registers reg, uint32_t value) +{ + const struct bdm_cf26_driver *bdm = target_to_bdm_cf26(target); + + if (bdm->write_sc_reg) + return bdm->write_sc_reg(bdm, reg, value); + else + return ERROR_FAIL; +} + +int bdm_cf26_write_mem_byte(struct target *target, uint32_t address, + uint8_t value) +{ + const struct bdm_cf26_driver *bdm = target_to_bdm_cf26(target); + + if (bdm->write_mem_byte) + return bdm->write_mem_byte(bdm, address, value); + + else + return ERROR_FAIL; +} + +int bdm_cf26_write_mem_word(struct target *target, uint32_t address, + uint16_t value) +{ + const struct bdm_cf26_driver *bdm = target_to_bdm_cf26(target); + + if (bdm->write_mem_word) + return bdm->write_mem_word(bdm, address, value); + + else + return ERROR_FAIL; +} + +int bdm_cf26_write_mem_long(struct target *target, uint32_t address, + uint32_t value) +{ + const struct bdm_cf26_driver *bdm = target_to_bdm_cf26(target); + + if (bdm->write_mem_long) + return bdm->write_mem_long(bdm, address, value); + + else + return ERROR_FAIL; +} + +int bdm_cf26_assert_reset(struct target *target) +{ + const struct bdm_cf26_driver *bdm = target_to_bdm_cf26(target); + + if (bdm->assert_reset) + return bdm->assert_reset(bdm); + else + return ERROR_FAIL; +} + +int bdm_cf26_deassert_reset(struct target *target) +{ + const struct bdm_cf26_driver *bdm = target_to_bdm_cf26(target); + + if (bdm->deassert_reset) + return bdm->deassert_reset(bdm); + else + return ERROR_FAIL; +} + +int bdm_cf26_halt(struct target *target) +{ + const struct bdm_cf26_driver *bdm = target_to_bdm_cf26(target); + + if (bdm->halt) + return bdm->halt(bdm); + else + return ERROR_FAIL; +} + +int bdm_cf26_go(struct target *target) +{ + const struct bdm_cf26_driver *bdm = target_to_bdm_cf26(target); + + if (bdm->go) + return bdm->go(bdm); + else + return ERROR_FAIL; +} + +int bdm_cf26_get_all_cpu_regs(struct target *target, uint8_t **reg_buff) +{ + const struct bdm_cf26_driver *bdm = target_to_bdm_cf26(target); + + if (bdm->get_all_cpu_regs) + return bdm->get_all_cpu_regs(bdm, reg_buff); + else + return ERROR_FAIL; +} + +int bdm_cf26_read_memory(struct target *target, uint32_t address, + uint32_t size, uint32_t count, uint8_t *buffer) +{ + const struct bdm_cf26_driver *bdm = target_to_bdm_cf26(target); + + if (bdm->read_memory) + return bdm->read_memory(bdm, address, size, count, buffer); + else + return ERROR_FAIL; +} + +int bdm_cf26_write_memory(struct target *target, uint32_t address, + uint32_t size, uint32_t count, const uint8_t *buffer) +{ + const struct bdm_cf26_driver *bdm = target_to_bdm_cf26(target); + + if (bdm->write_memory) + return bdm->write_memory(bdm, address, size, count, buffer); + else + return ERROR_FAIL; +} + +int32_t bdm_cf26_read_pc(struct target *target) +{ + const struct bdm_cf26_driver *bdm = target_to_bdm_cf26(target); + + if (bdm->read_pc) + return bdm->read_pc(bdm); + else + return ERROR_FAIL; +} + +int bdm_cf26_write_pc(struct target *target, uint32_t value) +{ + const struct bdm_cf26_driver *bdm = target_to_bdm_cf26(target); + + if (bdm->write_pc) + return bdm->write_pc(bdm, value); + else + return ERROR_FAIL; +} + +static const struct command_registration bdm_cf26_commands[] = { + { + .name = "newdap", + .jim_handler = jim_jtag_newtap, + .mode = COMMAND_CONFIG, + .help = "declare a new BDM DAP" + }, + COMMAND_REGISTRATION_DONE +}; + +static const struct command_registration bdm_cf26_handlers[] = { + { + .name = "bdm_cf26", + .mode = COMMAND_ANY, + .help = "bdm_cf26 command group", + .chain = bdm_cf26_commands, + }, + COMMAND_REGISTRATION_DONE +}; + +extern struct jtag_interface *jtag_interface; + +static int bdm_cf26_transport_init(struct command_context *cmd_ctx) +{ + const struct bdm_cf26_driver *bdm = jtag_interface->bdm_cf26; + + struct target *target = get_current_target(cmd_ctx); + if (!target) { + LOG_ERROR("no current target"); + return ERROR_FAIL; + } + + target->tap->priv = (void *)bdm; + target->tap->hasidcode = 0; + + return ERROR_OK; +} + +static int bdm_cf26_transport_select(struct command_context *cmd_ctx) +{ + return register_commands(cmd_ctx, NULL, bdm_cf26_handlers); +} + +static struct transport bdm_cf26_transport = { + .name = "bdm_cf26", + .select = bdm_cf26_transport_select, + .init = bdm_cf26_transport_init, +}; + +static void bdm_cf26_transport_constructor(void) __attribute__ ((constructor, used)); +static void bdm_cf26_transport_constructor(void) +{ + transport_register(&bdm_cf26_transport); +} + +/* + * Returns true if the current debug session + * is using BDM 26 pins as its transport. + */ +bool transport_is_bdm_cf26(void) +{ + return get_current_transport() == &bdm_cf26_transport; +} diff --git a/src/target/bdm_cf26.h b/src/target/bdm_cf26.h new file mode 100644 index 0000000..21f9a62 --- /dev/null +++ b/src/target/bdm_cf26.h @@ -0,0 +1,359 @@ +/*************************************************************************** + * * + * Copyright (C) 2017 by Angelo Dureghello * + * an...@sy... * + * * + * 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; see the file COPYING. If not see * + * <http://www.gnu.org/licenses/> * + * * + ***************************************************************************/ + +#ifndef OPENOCD_TARGET_BDM_CF26_H +#define OPENOCD_TARGET_BDM_CF26_H + + +enum pemu_commands { + CMD_BDM_GO = 0x0c00, + CMD_BDM_READ_CPU_AD_REG = 0x2180, + CMD_BDM_READ_BDM_REG = 0x2d80, + CMD_BDM_READ_SCM_REG = 0x2980, + CMD_BDM_READ_MEM_BYTE = 0x1900, + CMD_BDM_READ_MEM_WORD = 0x1940, + CMD_BDM_READ_MEM_LONG = 0x1980, + CMD_BDM_WRITE_CPU_AD_REG = 0x2080, + CMD_BDM_WRITE_BDM_REG = 0x2c80, + CMD_BDM_WRITE_SCM_REG = 0x2880, + CMD_BDM_WRITE_MEM_BYTE = 0x1800, + CMD_BDM_WRITE_MEM_WORD = 0x1840, + CMD_BDM_WRITE_MEM_LONG = 0x1880, +}; + +/** + * BDM registers + */ +enum bdm_cf26_registers { + BDM_REG_CSR = 0x00, + BDM_REG_XCSR = 0x01, +}; + +/* + * system control register, BDM access + */ +enum cf_sysctl_registers { + SYSC_CACR = 0x02, + SYSC_PC = 0x80f, +}; + +/** + * Issues READ BDM debug module register + * + * @param target The target to issue BDM command to + * @param reg One of the debug module registers + * @returns Negative error code on failure, retrived value on success + */ +int_least32_t bdm_cf26_read_dm_reg(struct target *target, + enum bdm_cf26_registers reg); + +/** + * Issues READ cpu AD register + * + * @param target The target to issue BDM command to + * @param reg One of the A/D cpu registers + * @returns Negative error code on failure, retrived value on success + */ +int_least32_t bdm_cf26_read_ad_reg(struct target *target, + enum bdm_cf26_registers reg); + + +/** + * Issues READ system register + * + * @param target The target to issue BDM command to + * @param reg One of the system registers + * @returns Negative error code on failure, retrived value on success + */ +int_least32_t bdm_cf26_read_sc_reg(struct target *target, + enum cf_sysctl_registers reg); + +/** + * Issues bdm READ memory byte + * + * @param target The target to issue BDM command to + * @param address The target memory address + * @returns Negative error code on failure, retrived value on success + */ +int_least8_t bdm_cf26_read_mem_byte(struct target *target, + uint32_t address); + +/** + * Issues bdm READ memory word + * + * @param target The target to issue BDM command to + * @param address The target memory address + * @returns Negative error code on failure, retrived value on success + */ +int_least16_t bdm_cf26_read_mem_word(struct target *target, uint32_t address); + +/** + * Issues bdm READ memory long word + * + * @param target The target to issue BDM command to + * @param address The target memory address + * @returns Negative error code on failure, retrived value on success + */ +int_least32_t bdm_cf26_read_mem_long(struct target *target, uint32_t address); + +/** + * Issues WRITE BDM debug module register + * + * @param target The target to issue BDM command to + * @param reg One of the debug module registers + * @returns Negative error code on failure, retrived value on success + */ +int bdm_cf26_write_dm_reg(struct target *, enum bdm_cf26_registers reg, + uint32_t value); + +/** + * Issues WRITE cpu A/D register + * + * @param target The target to issue BDM command to + * @param reg One of the A/D cpu registers + * @returns Negative error code on failure, retrived value on success + */ +int bdm_cf26_write_ad_reg(struct target *, enum bdm_cf26_registers reg, + uint32_t value); + +/** + * Issues WRITE systemctrl reg + * + * @param target The target to issue BDM command to + * @param reg One of the system registers + * @returns Negative error code on failure, retrived value on success + */ +int bdm_cf26_write_sc_reg(struct target *, enum cf_sysctl_registers reg, + uint32_t value); + +/** + * Issues bdm WRITE memory byte + * + * @param target The target to issue BDM command to + * @param address The target memory address + * @param value The value to write + * @returns Negative error code on failure, retrived value on success + */ +int bdm_cf26_write_mem_byte(struct target *target, uint32_t address, + uint8_t value); + +/** + * Issues bdm WRITE memory word + * + * @param target The target to issue BDM command to + * @param address The target memory address + * @param value The value to write + * @returns Negative error code on failure, retrived value on success + */ +int bdm_cf26_write_mem_word(struct target *target, uint32_t address, + uint16_t value); + +/** + * Issues bdm WRITE memory long word + * + * @param target The target to issue BDM command to + * @param address The target memory address + * @param value The value to write + * @returns Negative error code on failure, retrived value on success + */ +int bdm_cf26_write_mem_long(struct target *target, uint32_t address, + uint32_t value); + +/** + * Dongle specific commands + */ + +/** + * Asserts(active low) reset pin of the BDM dongle + * + * @param target The target + * @returns ERROR_OK on success, negative error code on failure + */ +int bdm_cf26_assert_reset(struct target *target); + +/** + * Deasserts reset pin of the BDM dongle + * + * @param target The target + * @returns ERROR_OK on success, negative error code on failure + */ +int bdm_cf26_deassert_reset(struct target *target); + +/** + * Halt cpu + * + * @param target The target + * @returns ERROR_OK on success, negative error code on failure + */ +int bdm_cf26_halt(struct target *target); + + +/****************************************************************************** + * BDM Firmware commands(accessible only in BDM acive mode) + ******************************************************************************/ + +/** + * Issues READ_PC command to the target to read the contents of the + * program counter. + * + * @param target The target to issue BDM command to + * @returns The value of PC register on success, negative error code + * on failure + */ +int_least32_t bdm_cf26_read_pc(struct target *target); + +/** + * Issues READ_SP command to the target to read the contents of the + * stack pointer. + * + * @param target The target to issue BDM command to + * @returns The value of SP register on success, negative error code + * on failure + */ +int_least32_t bdm_cf26_read_sp(struct target *target); + +/** + * Issues WRITE_PC command to the target to set the contents of the + * program counter. + * + * @param target The target to issue BDM command to + * @param value Value to write to PC register + * @returns ERROR_OK on success, negative error code on failure + */ +int bdm_cf26_write_pc(struct target *target, uint32_t value); + +/** + * Issues WRITE_SP command to the target to set the contents of the + * stack pointer. + * + * @param target The target to issue BDM command to + * @param value Value to write to SP register + * @returns ERROR_OK on success, negative error code on failure + */ +int bdm_cf26_write_sp(struct target *target, uint16_t value); + +/** + * Issues GO command to the target which causes it to get out of + * active BDM mode and continue normal program exceution. + * + * @param target The target to issue BDM command to + * @returns ERROR_OK on success, negative error code on failure + */ +int bdm_cf26_go(struct target *target); + +/****************************************************************************** + * Dongle specific commands non-mandatory, convinience commands + ******************************************************************************/ + +/** + * Reads all cpu regs + * + * @param target The target to issue BDM command to + * @param reg_buff buffer with all registers + * @returns ERROR_OK on success, negative error code on failure + */ +int bdm_cf26_get_all_cpu_regs(struct target *target, uint8_t **reg_buff); + +/** + * Reads a block of target's memory(BDM not mapped) + * + * @param target The target to issue BDM command to + * @param address Start of the read block address + * @param size Size of the individual element to read + * @param count Number of individual elemnets to read + * @param buffer Buffer to read the data to + * @returns ERROR_OK on success, negative error code on failure + */ +int bdm_cf26_read_memory(struct target *target, uint32_t address, + uint32_t size, uint32_t count, uint8_t *buffer); + +/** + * Write a block to target's memory(BDM not mapped) + * + * @param target The target to issue BDM command to + * @param address Start of the read block address + * @param size Size of the individual element to read + * @param count Number of individual elemnets to read + * @param buffer Buffer to read the data to + * @returns ERROR_OK on success, negative error code on failure + */ +int bdm_cf26_write_memory(struct target *target, uint32_t address, + uint32_t size, uint32_t count, const uint8_t *buffer); + +enum bdm_cf26_reg_bdmsts_bits { + BDM_REG_BDMSTS_ENBDM = (1 << 7), + BDM_REG_BDMSTS_BDMACT = (1 << 6), + BDM_REG_BDMSTS_SDV = (1 << 4), + BDM_REG_BDMSTS_TRACE = (1 << 3), + BDM_REG_BDMSTS_CLKSW = (1 << 2), + BDM_REG_BDMSTS_UNSEC = (1 << 1), +}; + +enum bdm_cf26_reg_bdmgpr_bits { + BDM_REG_BDMGPR_BGAE = (1 << 7), +}; + +/** + * Represents a generic BDM adapter + * + * This a vtable of sorts and is not expected to be used on its own. + * Specific adapter drivers should incorporate this structure as a + * part of the driver-specific strucure(should be placed at the very + * beginning) + */ +struct bdm_cf26_driver { + /** + * Hardware commands + */ + int_least32_t (*read_dm_reg)(const struct bdm_cf26_driver *, uint8_t); + int_least32_t (*read_ad_reg)(const struct bdm_cf26_driver *, uint8_t); + int_least32_t (*read_sc_reg)(const struct bdm_cf26_driver *, uint16_t); + int_least8_t (*read_mem_byte)(const struct bdm_cf26_driver *, uint32_t); + int_least16_t (*read_mem_word)(const struct bdm_cf26_driver *, uint32_t); + int_least32_t (*read_mem_long)(const struct bdm_cf26_driver *, uint32_t); + + int (*write_dm_reg)(const struct bdm_cf26_driver *, uint8_t, uint32_t); + int (*write_ad_reg)(const struct bdm_cf26_driver *, uint8_t, uint32_t); + int (*write_sc_reg)(const struct bdm_cf26_driver *, uint16_t, uint32_t); + int (*write_mem_byte)(const struct bdm_cf26_driver *, uint32_t, uint8_t); + int (*write_mem_word)(const struct bdm_cf26_driver *, uint32_t, uint16_t); + int (*write_mem_long)(const struct bdm_cf26_driver *, uint32_t, uint32_t); + + /* + Dongle firmware commands(not a part of BDM specification) + */ + int (*assert_reset)(const struct bdm_cf26_driver *); + int (*deassert_reset)(const struct bdm_cf26_driver *); + int (*halt)(const struct bdm_cf26_driver *); + + int (*read_memory)(const struct bdm_cf26_driver *, uint32_t, uint32_t, uint32_t, uint8_t *); + int (*write_memory)(const struct bdm_cf26_driver *, uint32_t, uint32_t, uint32_t, const uint8_t *); + + /* + Firmware commands + */ + int_least32_t (*read_pc)(const struct bdm_cf26_driver *); + int (*write_pc)(const struct bdm_cf26_driver *, uint32_t); + int (*go)(const struct bdm_cf26_driver *); + int (*get_all_cpu_regs)(const struct bdm_cf26_driver *, uint8_t **); +}; + +#endif /* OPENOCD_TARGET_BDM_CF26_H */ diff --git a/src/target/mcf5441x.c b/src/target/mcf5441x.c new file mode 100644 index 0000000..48c8c4d --- /dev/null +++ b/src/target/mcf5441x.c @@ -0,0 +1,777 @@ +/*************************************************************************** + * * * + * Copyright (C) 2017 by Angelo Dureghello * + * an...@sy... * + * * + * 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; see the file COPYING. If not see * + * <http://www.gnu.org/licenses/> * + * * + ***************************************************************************/ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <jtag/jtag.h> +#include <jtag/interface.h> +#include <binarybuffer.h> + +#include "breakpoints.h" +#include "target.h" +#include "algorithm.h" +#include "register.h" +#include "target_type.h" +#include "bdm_cf26.h" +#include "mcf5441x.h" +#include "log.h" + +static const struct { + unsigned id; + const char *name; + unsigned bits; + enum reg_type type; + const char *group; + const char *feature; +} mcf5441x_registers_table[] = { + { CF_D0, "d0", 32, REG_TYPE_INT, "general", "org.gnu.gdb.coldfire.core" }, + { CF_D1, "d1", 32, REG_TYPE_INT, "general", "org.gnu.gdb.coldfire.core" }, + { CF_D2, "d2", 32, REG_TYPE_INT, "general", "org.gnu.gdb.coldfire.core" }, + { CF_D3, "d3", 32, REG_TYPE_INT, "general", "org.gnu.gdb.coldfire.core" }, + { CF_D4, "d4", 32, REG_TYPE_INT, "general", "org.gnu.gdb.coldfire.core" }, + { CF_D5, "d5", 32, REG_TYPE_INT, "general", "org.gnu.gdb.coldfire.core" }, + { CF_D6, "d6", 32, REG_TYPE_INT, "general", "org.gnu.gdb.coldfire.core" }, + { CF_D7, "d7", 32, REG_TYPE_INT, "general", "org.gnu.gdb.coldfire.core" }, + { CF_A0, "a0", 32, REG_TYPE_DATA_PTR, "data_ptr", "org.gnu.gdb.coldfire.core" }, + { CF_A1, "a1", 32, REG_TYPE_DATA_PTR, "data_ptr", "org.gnu.gdb.coldfire.core" }, + { CF_A2, "a2", 32, REG_TYPE_DATA_PTR, "data_ptr", "org.gnu.gdb.coldfire.core" }, + { CF_A3, "a3", 32, REG_TYPE_DATA_PTR, "data_ptr", "org.gnu.gdb.coldfire.core" }, + { CF_A4, "a4", 32, REG_TYPE_DATA_PTR, "data_ptr", "org.gnu.gdb.coldfire.core" }, + { CF_A5, "a5", 32, REG_TYPE_DATA_PTR, "data_ptr", "org.gnu.gdb.coldfire.core" }, + { CF_FP, "fp", 32, REG_TYPE_DATA_PTR, "data_ptr", "org.gnu.gdb.coldfire.core" }, + { CF_SP, "sp", 32, REG_TYPE_DATA_PTR, "data_ptr", "org.gnu.gdb.coldfire.core" }, + { CF_PS, "ps", 32, REG_TYPE_INT, "general", "org.gnu.gdb.coldfire.core" }, + { CF_PC, "pc", 32, REG_TYPE_CODE_PTR, "code_ptr", "org.gnu.gdb.coldfire.core" }, + { CF_VBR, "vbr", 32, REG_TYPE_DATA_PTR, "data_ptr", "org.gnu.gdb.coldfire.core" }, +}; + +#define CF_NUM_REGS ARRAY_SIZE(mcf5441x_registers_table) + +struct mcf5441x_reg { + struct target *target; +}; + +static int mcf5441x_poll(struct target *target) +{ + enum target_state prev_target_state = target->state; + uint32_t csr; + + csr = bdm_cf26_read_dm_reg(target, BDM_REG_CSR); + + if ((csr & CSR_BPKT) || (csr & CSR_HALT)) { + + target->state = TARGET_HALTED; + + if ((prev_target_state == TARGET_RUNNING) || + (prev_target_state == TARGET_RESET)) { + + target_call_event_callbacks(target, TARGET_EVENT_HALTED); + + if (mcf5441x_debug_entry(target) != ERROR_OK) + LOG_WARNING("can't set debug entry"); + } + } + + return ERROR_OK; +} + +static int mcf5441x_read_core_reg(struct mcf5441x *mcf5441x, struct reg *reg) +{ + int32_t ret; + struct target *target = mcf5441x->target; + + assert(reg->number < mcf5441x->core_cache->num_regs); + + if (target->state != TARGET_HALTED) + return ERROR_TARGET_NOT_HALTED; + + /* special cases */ + if (reg->number == CF_PC) + ret = bdm_cf26_read_pc(target); + else + ret = bdm_cf26_read_ad_reg(target, reg->number); + + buf_set_u32(reg->value, 0, 32, ret); + reg->valid = 1; + reg->dirty = 0; + + return ERROR_OK; +} + +static int mcf5441x_write_core_reg(struct mcf5441x *mcf5441x, + struct reg *reg, uint8_t *value) +{ + int ret, regval; + struct target *target = mcf5441x->target; + + if (target->state != TARGET_HALTED) + LOG_WARNING("target not halted"); + + regval = buf_get_u32(value, 0, 32); + + assert(reg->number < mcf5441x->core_cache->num_regs); + + /* special cases */ + if (reg->number == CF_PC) + ret = bdm_cf26_write_pc(target, regval); + else + ret = bdm_cf26_write_ad_reg(target, reg->number, regval); + + if (ret != ERROR_OK) { + LOG_ERROR("BDM failure"); + reg->dirty = reg->valid; + return ERROR_JTAG_DEVICE_ERROR; + } + + reg->valid = 1; + reg->dirty = 0; + + return ERROR_OK; +} + +static int mcf5441x_get_core_reg(struct reg *reg) +{ + struct mcf5441x_reg *mcfreg; + struct target *target; + struct mcf5441x *cpu; + + mcfreg = reg->arch_info; + target = mcfreg->target; + cpu = target_to_mcf5441x(target); + + if (target->state != TARGET_HALTED) + return ERROR_TARGET_NOT_HALTED; + + return cpu->read_core_reg(cpu, reg); +} + +static int mcf5441x_set_core_reg(struct reg *reg, uint8_t *buf) +{ + struct mcf5441x_reg *mcfreg; + struct target *target; + uint32_t value; + + mcfreg = reg->arch_info; + target = mcfreg->target; + + if (target->state != TARGET_HALTED) + return ERROR_TARGET_NOT_HALTED; + + value = buf_get_u32(buf, 0, 32); + buf_set_u32(reg->value, 0, 32, value); + + reg->dirty = 1; + reg->valid = 1; + + return ERROR_OK; +} + +static const struct reg_arch_type mcf5441x_reg_type = { + .get = mcf5441x_get_core_reg, + .set = mcf5441x_set_core_reg, +}; + +/** + * Restores target context using the cache of core registers set up + * by mcf5441x_build_reg_cache(), calling optional core-specific hooks. + */ +static int mcf5441x_restore_context(struct target *target) +{ + int i; + struct mcf5441x *cpu = target_to_mcf5441x(target); + struct reg_cache *cache = cpu->core_cache; + + for (i = cache->num_regs - 1; i >= 0; i--) { + if (cache->reg_list[i].dirty) { + cpu->write_core_reg(cpu, + &cache->reg_list[i], + (uint8_t *)cache->reg_list[i].value); + } + } + + return ERROR_OK; +} + +static int mcf5441x_disable_breakpoints(struct target *target) +{ + int ret; + struct breakpoint *breakpoint = target->breakpoints; + + /* set any pending breakpoints */ + while (breakpoint) { + if (breakpoint->set) { + ret = mcf5441x_unset_breakpoint(target, breakpoint); + if (ret != ERROR_OK) { + LOG_ERROR("Failed to unset breakpoint"); + break; + } + } + breakpoint = breakpoint->next; + } + + return ERROR_OK; +} + +static int mcf5441x_enable_breakpoints(struct target *target) +{ + int ret; + struct breakpoint *breakpoint = target->breakpoints; + + /* set any pending breakpoints */ + while (breakpoint) { + if (!breakpoint->set) { + ret = mcf5441x_set_breakpoint(target, breakpoint); + if (ret != ERROR_OK) { + LOG_ERROR("Failed to set breakpoint"); + break; + } + } + breakpoint = breakpoint->next; + } + + return ERROR_OK; +} + +static int mcf5441x_step(struct target *target, int current, uint32_t address, + int handle_breakpoints) +{ + struct mcf5441x *cpu = target_to_mcf5441x(target); + uint32_t value; + int ret; + + if (target->state != TARGET_HALTED) { + LOG_WARNING("target not halted"); + return ERROR_TARGET_NOT_HALTED; + } + + /* current = 1: continue on current pc, + * otherwise continue at <address> + */ + if (!current) { + LOG_INFO("mcf5441x_step() not current"); + buf_set_u32(cpu->pc->value, 0, 32, address); + cpu->pc->dirty = 1; + } + + target->debug_reason = DBG_REASON_SINGLESTEP; + + /* + * restoring context now, before go + */ + mcf5441x_restore_context(target); + + target_call_event_callbacks(target, TARGET_EVENT_RESUMED); + + /* + * getting CRS and re-setting for step + */ + value = bdm_cf26_read_dm_reg(target, BDM_REG_CSR); + /* setting in step, no interrupt */ + value |= (CSR_SSM | CSR_IPI); + /* emulator mode */ + value |= CSR_EMULATION; + bdm_cf26_write_dm_reg(target, BDM_REG_CSR, value); + + /* + * ColdFire is magic, is magic, oh-oh-oh, the summer is magic + * + * With BDM in STEP + EMULATION mode, cpu caches somewhere a group + * of program instructions just after each go-step (go performs a step). + * We need to remove all the breakpoints so, or it will remember + * of them even if they are not there anymore. + */ + mcf5441x_disable_breakpoints(target); + + /* ok, go now acts as single step */ + ret = bdm_cf26_go(target); + if (ret < 0) { + LOG_ERROR("Failed to resume execution"); + return ret; + } + + if (mcf5441x_debug_entry(target) != ERROR_OK) + LOG_WARNING("can't set debug entry"); + + /* now we should be halted with new reg read */ + target_call_event_callbacks(target, TARGET_EVENT_HALTED); + + return ERROR_OK; +} + +static int mcf5441x_set_breakpoint(struct target *target, + struct breakpoint *breakpoint) +{ + int retval; + + if (breakpoint->set) { + LOG_WARNING("breakpoint (BPID: %" PRIu32 ") already set", + breakpoint->unique_id); + return ERROR_OK; + } + + if (breakpoint->t... [truncated message content] |