lm32: initial version (somehow working)
authorMichael Walle <michael@walle.cc>
Mon, 27 Sep 2010 20:21:48 +0000 (22:21 +0200)
committerMichael Walle <michael@walle.cc>
Mon, 27 Sep 2010 20:21:48 +0000 (22:21 +0200)
src/target/Makefile.am
src/target/lm32.c [new file with mode: 0644]
src/target/target.c

index e01e077..ceffc3f 100644 (file)
@@ -36,7 +36,8 @@ libtarget_la_SOURCES = \
        $(MIPS32_SRC) \
        avrt.c \
        dsp563xx.c \
-       dsp563xx_once.c
+       dsp563xx_once.c \
+       lm32.c
 
 TARGET_CORE_SRC = \
        algorithm.c \
@@ -106,6 +107,8 @@ MIPS32_SRC = \
        mips32_dmaacc.c \
        mips_ejtag.c
 
+LM32_SRC = \
+       lm32.c
 
 noinst_HEADERS = \
        algorithm.h \
diff --git a/src/target/lm32.c b/src/target/lm32.c
new file mode 100644 (file)
index 0000000..b4751c8
--- /dev/null
@@ -0,0 +1,1321 @@
+/***************************************************************************
+ *   Copyright (C) 2010 by Michael Walle                                   *
+ *   michael@walle.cc                                                      *
+ *                                                                         *
+ *   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, write to the                         *
+ *   Free Software Foundation, Inc.,                                       *
+ *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
+ ***************************************************************************/
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "breakpoints.h"
+#include "register.h"
+#include <helper/time_support.h>
+#include <helper/jim.h>
+#include <jtag/jtag.h>
+#include "target.h"
+#include "target_type.h"
+
+#define LM32_ADDR_CMD             0
+#define LM32_ADDR_TX              1
+#define LM32_ADDR_RX              2
+
+#define LM32_CMD_NOP              (0 << 7)
+#define LM32_CMD_READ_MEMORY      (1 << 7)
+#define LM32_CMD_WRITE_MEMORY     (2 << 7)
+#define LM32_CMD_READ_SEQUENTIAL  (3 << 7)
+#define LM32_CMD_WRITE_SEQUENTIAL (4 << 7)
+#define LM32_CMD_WRITE_CSR        (5 << 7)
+#define LM32_CMD_BREAK            (6 << 7)
+#define LM32_CMD_RESET            (7 << 7)
+
+#define LM32_STAT_RX_READY        (1 << 0)
+#define LM32_STAT_TX_FULL         (1 << 1)
+#define LM32_STAT_PROCESSING      (1 << 2)
+
+#define LM32_MONITOR_CMD_REG_ADDR     0
+#define LM32_MONITOR_CMD_READ_CSR     1
+#define LM32_MONITOR_CMD_WRITE_CSR    2
+#define LM32_MONITOR_CMD_READ_MEM     3
+#define LM32_MONITOR_CMD_WRITE_MEM    4
+#define LM32_MONITOR_CMD_CONTINUE     5
+
+#define LM32_MONITOR_READ_CSR_CFG     0
+
+#define LM32_MONITOR_WRITE_CSR_EBA    0
+#define LM32_MONITOR_WRITE_CSR_DC     1
+#define LM32_MONITOR_WRITE_CSR_DEBA   2
+#define LM32_MONITOR_WRITE_CSR_BP0    3
+#define LM32_MONITOR_WRITE_CSR_BP1    4
+#define LM32_MONITOR_WRITE_CSR_BP2    5
+#define LM32_MONITOR_WRITE_CSR_BP3    6
+#define LM32_MONITOR_WRITE_CSR_WP0    7
+#define LM32_MONITOR_WRITE_CSR_WP1    8
+#define LM32_MONITOR_WRITE_CSR_WP2    9
+#define LM32_MONITOR_WRITE_CSR_WP3   10
+
+#define LM32_INST_BREAK              0xac000002UL
+
+struct lm32
+{
+       int lm32_magic;
+       struct reg_cache *reg_cache;
+       uint32_t reg_base;
+
+       /* current state of the debug handler */
+       int handler_running;
+
+       int num_bps_avail;
+       /* bitfield, bit 0 corresponds to bp0 */
+       uint32_t bps_used;
+
+       uint32_t ir_insn;
+};
+
+struct lm32_reg
+{
+       uint32_t num;
+       struct target *target;
+};
+
+static char* lm32_reg_list[] =
+{
+       "r0", "r1", "r2", "r3", "r4", "r5", "r6", "r7", "r8", "r9", "r10", "r11",
+       "r12", "r13", "r14", "r15", "r16", "r17", "r18", "r19", "r20", "r21",
+       "r22", "r23", "r24", "r25", "gp", "fp", "sp", "ra", "ea", "ba", "pc",
+       "eid", "eba", "deba"
+};
+
+enum
+{
+       LM32_R0 = 0, LM32_R1, LM32_R2, LM32_R3, LM32_R4, LM32_R5, LM32_R6, LM32_R7, LM32_R8,
+       LM32_R9, LM32_R10, LM32_R11, LM32_R12, LM32_R13, LM32_R14, LM32_R15, LM32_R16,
+       LM32_R17, LM32_R18, LM32_R19, LM32_R20, LM32_R21, LM32_R22, LM32_R23, LM32_R24,
+       LM32_R25, LM32_GP, LM32_FP, LM32_SP, LM32_RA, LM32_EA, LM32_BA, LM32_PC, LM32_EID,
+       LM32_EBA, LM32_DEBA, LM32_NUM_REGS
+};
+
+static const struct lm32_reg lm32_reg_arch_info[] =
+{
+       { LM32_R0, NULL }, { LM32_R1, NULL }, { LM32_R2, NULL }, { LM32_R3, NULL },
+       { LM32_R4, NULL }, { LM32_R5, NULL }, { LM32_R6, NULL }, { LM32_R7, NULL },
+       { LM32_R8, NULL }, { LM32_R9, NULL }, { LM32_R10, NULL }, { LM32_R11, NULL },
+       { LM32_R12, NULL }, { LM32_R13, NULL }, { LM32_R14, NULL }, { LM32_R15, NULL },
+       { LM32_R16, NULL }, { LM32_R17, NULL }, { LM32_R18, NULL }, { LM32_R19, NULL },
+       { LM32_R20, NULL }, { LM32_R21, NULL }, { LM32_R22, NULL }, { LM32_R23, NULL },
+       { LM32_R24, NULL }, { LM32_R25, NULL }, { LM32_GP, NULL }, { LM32_FP, NULL },
+       { LM32_SP, NULL }, { LM32_RA, NULL }, { LM32_EA, NULL }, { LM32_BA, NULL },
+       { LM32_PC, NULL }, { LM32_EID, NULL }, { LM32_EBA, NULL }, { LM32_DEBA, NULL },
+};
+
+/*
+ * jtag functions
+ */
+
+static int
+lm32_jtag_set_instr(struct jtag_tap *tap, uint32_t new_instr, tap_state_t end_state)
+{
+       assert(tap);
+
+       if (buf_get_u32(tap->cur_instr, 0, tap->ir_length) != new_instr)
+       {
+               struct scan_field field;
+               uint8_t scratch[4];
+
+               memset(&field, 0, sizeof(field));
+               field.num_bits = tap->ir_length;
+               field.out_value = scratch;
+               buf_set_u32(scratch, 0, field.num_bits, new_instr);
+
+               jtag_add_ir_scan(tap, &field, end_state);
+       }
+
+       return ERROR_OK;
+}
+
+static int
+lm32_jtag_datar(struct target *target, uint32_t data_in, uint32_t *data_out)
+{
+       int ret;
+       uint8_t out_value[2];
+       struct scan_field field;
+
+       buf_set_u32(out_value, 0, 11, data_in);
+
+       memset(&field, 0, sizeof(field));
+
+       field.num_bits = 11;
+       field.out_value = out_value;
+       field.in_value = (void*)data_out;
+
+       jtag_add_dr_scan(target->tap, 1, &field, TAP_IDLE);
+
+       if (data_out)
+       {
+               if ((ret = jtag_execute_queue()) != ERROR_OK)
+               {
+                       LOG_ERROR("register read failed");
+                       return ret;
+               }
+       }
+       
+       return ERROR_OK;
+}
+
+/*
+ * helper functions
+ */
+
+static int
+lm32_send_command(struct target *target, uint32_t cmd, int flush)
+{
+       if (lm32_jtag_datar(target, cmd | LM32_ADDR_CMD, NULL) != ERROR_OK)
+               goto err;
+
+       if (flush)
+               if (jtag_execute_queue() != ERROR_OK)
+                       goto err;
+
+       return ERROR_OK;
+err:
+       LOG_ERROR("error sending JTAG command");
+       return ERROR_FAIL;
+}
+
+static int
+lm32_get_status(struct target *target, uint32_t *status)
+{
+       return lm32_jtag_datar(target, LM32_CMD_NOP | LM32_ADDR_CMD, status);
+}
+
+static int
+lm32_read_rx(struct target *target, uint8_t *data)
+{
+       struct timeval timeout, now;
+       uint32_t status;
+       uint32_t tmp_data;
+       int ret;
+
+       gettimeofday(&timeout, NULL);
+       timeval_add_time(&timeout, 1, 0);
+
+       for (;;)
+       {
+               if ((ret = lm32_get_status(target, &status)) != ERROR_OK) {
+                       LOG_ERROR("error reading jtag status");
+                       return ret;
+               }
+
+               if (status & LM32_STAT_RX_READY)
+               {
+                       lm32_jtag_datar(target, LM32_ADDR_RX, NULL);
+                       lm32_jtag_datar(target, LM32_ADDR_CMD | LM32_CMD_NOP, &tmp_data);
+                       goto done;
+               }
+
+               gettimeofday(&now, NULL);
+               if (timercmp(&now, &timeout, >))
+               {
+                       LOG_ERROR("timeout reading RX register");
+                       return ERROR_TARGET_TIMEOUT;
+               }
+
+               if (debug_level >= 3)
+               {
+                       LOG_DEBUG("waiting 100ms");
+                       alive_sleep(100);
+               } else {
+                       keep_alive();
+               }
+       }
+done:
+
+       if ((ret = jtag_execute_queue()) != ERROR_OK) {
+               return ret;
+       }
+
+       *data = (tmp_data >> 3) & 0xff;
+
+//     LOG_DEBUG("read %02x", *data);
+
+       return ERROR_OK;
+}
+
+static int
+lm32_write_tx(struct target *target, uint8_t data)
+{
+       struct timeval timeout, now;
+       uint32_t status;
+       int ret;
+//     LOG_DEBUG("sending %02x", data);
+
+       gettimeofday(&timeout, NULL);
+       timeval_add_time(&timeout, 1, 0);
+
+       for (;;)
+       {
+               if ((ret = lm32_get_status(target, &status)) != ERROR_OK) {
+                       LOG_ERROR("error reading jtag status");
+                       return ret;
+               }
+
+               if (!(status & LM32_STAT_TX_FULL))
+               {
+                       lm32_jtag_datar(target, (data << 3) | LM32_ADDR_TX, NULL);
+                       goto done;
+               }
+
+               gettimeofday(&now, NULL);
+               if (timercmp(&now, &timeout, >))
+               {
+                       LOG_ERROR("timeout writing TX register");
+                       return ERROR_TARGET_TIMEOUT;
+               }
+
+               if (debug_level >= 3)
+               {
+                       LOG_DEBUG("waiting 100ms");
+                       alive_sleep(100);
+               } else {
+                       keep_alive();
+               }
+       }
+done:
+
+       return jtag_execute_queue();
+}
+
+static int
+lm32_send_u32(struct target *target, uint32_t data)
+{
+       /* XXX endianness */
+       int i, ret;
+       uint8_t *buf = (uint8_t*)(&data);
+
+       for (i = 0; i < 4; i++)
+       {
+               if ((ret = lm32_write_tx(target, buf[i])) != ERROR_OK)
+                       return ret;
+       }
+
+       return ERROR_OK;
+}
+
+static int
+lm32_recv_u32(struct target *target, uint32_t *data)
+{
+       /* XXX endianness */
+       int i, ret;
+       uint8_t *buf = (uint8_t*)data;
+
+       for (i = 0; i < 4; i++)
+       {
+               if ((ret = lm32_read_rx(target, buf+i)) != ERROR_OK)
+                       return ret;
+       }
+
+       return ERROR_OK;
+}
+
+static int
+lm32_read_memory(struct target *target, uint32_t address, uint32_t size, uint32_t count, uint8_t *buffer)
+{
+       uint32_t u;
+       int ret;
+
+       LOG_DEBUG("address: 0x%8.8x, size: 0x%8.8x, count: 0x%8.8x", address, size, count);
+
+       if (target->state != TARGET_HALTED)
+       {
+               LOG_WARNING("target not halted");
+               return ERROR_TARGET_NOT_HALTED;
+       }
+
+       /* sanitize arguments */
+       if (((size != 4) && (size != 2) && (size != 1)) || (count == 0) || !(buffer))
+               return ERROR_INVALID_ARGUMENTS;
+       
+       if (((size == 4) && (address & 0x3u)) || ((size == 2) && (address & 0x1u)))
+               return ERROR_TARGET_UNALIGNED_ACCESS;
+       
+       /* send memory read request */
+       if ((ret = lm32_write_tx(target, LM32_MONITOR_CMD_READ_MEM)) != ERROR_OK)
+               return ret;
+       
+       /* send base address */
+       if ((ret = lm32_send_u32(target, address)) != ERROR_OK)
+               return ret;
+
+       /* send length */
+       if ((ret = lm32_send_u32(target, count*size)) != ERROR_OK)
+               return ret;
+
+       /* receive data */
+       for (u = 0; u < count*size; u++)
+       {
+               if ((ret = lm32_read_rx(target, buffer+u)) != ERROR_OK)
+                       return ret;
+       }
+
+       return ERROR_OK;
+}
+
+static int
+lm32_write_memory(struct target *target, uint32_t address, uint32_t size, uint32_t count, uint8_t *buffer)
+{
+       uint32_t u;
+       int ret;
+
+       LOG_DEBUG("address: 0x%8.8x, size: 0x%8.8x, count: 0x%8.8x", address, size, count);
+
+       if (target->state != TARGET_HALTED)
+       {
+               LOG_WARNING("target not halted");
+               return ERROR_TARGET_NOT_HALTED;
+       }
+
+       /* sanitize arguments */
+       if (((size != 4) && (size != 2) && (size != 1)) || (count == 0) || !(buffer))
+               return ERROR_INVALID_ARGUMENTS;
+
+       if (((size == 4) && (address & 0x3u)) || ((size == 2) && (address & 0x1u)))
+               return ERROR_TARGET_UNALIGNED_ACCESS;
+
+       /* send memory write request */
+       if ((ret = lm32_write_tx(target, LM32_MONITOR_CMD_WRITE_MEM)) != ERROR_OK)
+               return ret;
+       
+       /* send base address */
+       if ((ret = lm32_send_u32(target, address)) != ERROR_OK)
+               return ret;
+
+       /* send length */
+       if ((ret = lm32_send_u32(target, count*size)) != ERROR_OK)
+               return ret;
+
+       /* send data */
+       for (u = 0; u < count*size; u++)
+       {
+               if ((ret = lm32_write_tx(target, buffer[u])) != ERROR_OK)
+                       return ret;
+       }
+
+
+
+       return ERROR_OK;
+}
+
+static int
+lm32_invalidate_regs(struct target *target)
+{
+       struct lm32 *lm32 = target->arch_info;
+       int i;
+
+       for (i = 0; i < LM32_NUM_REGS; i++)
+       {
+               lm32->reg_cache->reg_list[i].valid = 0;
+               lm32->reg_cache->reg_list[i].dirty = 0;
+       }
+
+       return ERROR_OK;
+}
+
+static int
+lm32_debug_entry(struct target *target)
+{
+       struct lm32 *lm32 = target->arch_info;
+
+       int i;
+       uint8_t *buf;
+
+       LOG_DEBUG("-");
+
+       /* read registers base address */
+       if (lm32_write_tx(target, LM32_MONITOR_CMD_REG_ADDR) != ERROR_OK)
+       {
+               LOG_ERROR("error writing monitor command");
+               return ERROR_TARGET_FAILURE;
+       }
+
+       if (lm32_recv_u32(target, &(lm32->reg_base)) != ERROR_OK)
+       {
+               LOG_ERROR("error reading base address");
+               return ERROR_TARGET_FAILURE;
+       }
+
+       LOG_DEBUG("registers base address: %08x", lm32->reg_base);
+
+       /* allocate buffer */
+       buf = calloc(LM32_NUM_REGS, 4);
+
+       /* read registers content */
+       if (lm32_read_memory(target, lm32->reg_base, 4, LM32_NUM_REGS, buf)
+               != ERROR_OK)
+       {
+               LOG_ERROR("error reading memory");
+               free(buf);
+               return ERROR_TARGET_FAILURE;
+       }
+
+       for (i = 0; i < LM32_NUM_REGS; i++)
+       {
+               /* XXX endianess */
+               uint32_t data = buf[i*4] << 24 | buf[i*4+1] << 16 | buf[i*4+2] << 8 | buf[i*4+3];
+               buf_set_u32(lm32->reg_cache->reg_list[i].value, 0, 32, data);
+
+               LOG_DEBUG("reg %i -> %02x", i, buf_get_u32(buf+(i*4), 0, 32));
+
+               lm32->reg_cache->reg_list[i].dirty = 0;
+               lm32->reg_cache->reg_list[i].valid = 1;
+       }
+
+       free(buf);
+
+       LOG_DEBUG("pc: %08x", buf_get_u32(lm32->reg_cache->reg_list[LM32_PC].value, 0, 32));
+
+       return ERROR_OK;
+}
+
+static int
+lm32_restore_context(struct target *target)
+{
+       struct lm32 *lm32 = target->arch_info;
+
+       int i;
+       int dirty;
+       uint8_t *buf;
+
+       LOG_DEBUG("-");
+
+       if (target->state != TARGET_HALTED)
+       {
+               LOG_WARNING("target not halted");
+               return ERROR_TARGET_NOT_HALTED;
+       }
+
+       /* allocate buffer */
+       buf = calloc(LM32_NUM_REGS, 4);
+
+       dirty = 0;
+       for (i = 0; i < LM32_NUM_REGS; i++)
+       {
+               /* XXX endianess */
+               uint8_t* tmp_buf = lm32->reg_cache->reg_list[i].value;
+               uint32_t data = tmp_buf[0] << 24 | tmp_buf[1] << 16 | tmp_buf[2] << 8 | tmp_buf[3];
+               buf_set_u32(buf+(i*4), 0, 32, data);
+               if (lm32->reg_cache->reg_list[i].dirty)
+               {
+                       dirty = 1;
+               }
+       }
+
+       /* write registers content back */
+       if (dirty)
+       {
+               if (lm32_write_memory(target, lm32->reg_base, 4, LM32_NUM_REGS, buf) != ERROR_OK)
+               {
+                       LOG_ERROR("error writing monitor command");
+                       free(buf);
+                       return ERROR_TARGET_FAILURE;
+               }
+       }
+
+       free(buf);
+
+       return ERROR_OK;
+}
+
+/*
+ * target functions
+ */
+
+static int
+lm32_arch_state(struct target *target)
+{
+       return ERROR_OK;
+}
+
+static int
+lm32_poll(struct target *target)
+{
+       int ret = ERROR_OK;
+       uint32_t status;
+       uint8_t data;
+
+       if (target->state == TARGET_RUNNING)
+       {
+               enum target_state previous_state = target->state;
+
+               if (lm32_get_status(target, &status) != ERROR_OK)
+               {
+                       LOG_ERROR("error reading jtag status");
+                       return ERROR_TARGET_FAILURE;
+               }
+
+               if (!(status & LM32_STAT_RX_READY))
+                       /* rx not ready */
+                       return ERROR_OK;
+
+               ret = lm32_read_rx(target, &data);
+               if (ret == ERROR_OK && data == 'T')
+               {
+                       target->state = TARGET_HALTED;
+                       lm32_debug_entry(target);
+               } else {
+                       LOG_DEBUG(
+                               "error while polling jtag uart rx register, reset cpu "
+                               "(ret=%i, data=%02x)", ret, data
+                       );
+                       target->state = TARGET_UNKNOWN;
+               }
+
+               /* if target was running, signal that we halted
+                * otherwise we reentered from debug execution */
+               if (previous_state == TARGET_RUNNING)
+                       target_call_event_callbacks(target, TARGET_EVENT_HALTED);
+       }
+
+       return ret;
+}
+
+static int
+lm32_halt(struct target *target)
+{
+       int ret;
+
+       LOG_DEBUG("target->state: %s", target_state_name(target));
+       
+       if (target->state == TARGET_HALTED)
+       {
+               LOG_DEBUG("target was already halted");
+               return ERROR_OK;
+       }
+       else if (target->state == TARGET_UNKNOWN)
+       {
+               LOG_ERROR("target was in unknown state when halt was requested");
+               return ERROR_TARGET_INVALID;
+       }
+       else if (target->state == TARGET_RESET)
+       {
+               LOG_ERROR("target was in reset state when halt was requested");
+               return ERROR_TARGET_INVALID;
+       }
+
+       if ((ret = lm32_send_command(target, LM32_CMD_BREAK, 1)) != ERROR_OK)
+       {
+               LOG_ERROR("could not send BREAK command");
+               return ret;
+       }
+
+       lm32_invalidate_regs(target);
+
+       target->debug_reason = DBG_REASON_DBGRQ;
+
+       return ERROR_OK;
+}
+
+static int
+lm32_assert_reset(struct target *target)
+{
+       LOG_WARNING("not implemented");
+
+       return ERROR_OK;
+}
+
+static int
+lm32_deassert_reset(struct target *target)
+{
+       LOG_WARNING("not implemented");
+
+       return ERROR_OK;
+}
+
+static int
+lm32_resume(struct target *target, int current, uint32_t address, int handle_breakpoints, int debug_execution)
+{
+       struct lm32 *lm32 = target->arch_info;
+
+       int ret;
+
+       if (target->state != TARGET_HALTED)
+       {
+               LOG_WARNING("target not halted");
+               return ERROR_TARGET_NOT_HALTED;
+       }
+
+#if 0
+       if (!debug_execution)
+       {
+               target_free_all_working_areas(target);
+       }
+#endif
+
+       if (!current)
+       {
+               buf_set_u32(lm32->reg_cache->reg_list[LM32_PC].value, 0, 32, address);
+               lm32->reg_cache->reg_list[LM32_PC].dirty = 1;
+               lm32->reg_cache->reg_list[LM32_PC].valid = 1;
+       }
+       
+       if (handle_breakpoints)
+       {
+               /* XXX */
+       }
+
+       if ((ret = lm32_restore_context(target)) != ERROR_OK)
+       {
+               LOG_ERROR("error restoring context");
+               return ret;
+       }
+
+       if ((ret = lm32_write_tx(target, LM32_MONITOR_CMD_CONTINUE)) != ERROR_OK)
+       {
+               LOG_ERROR("error writing jtag uart tx register");
+               return ret;
+       }
+
+       target_call_event_callbacks(target, TARGET_EVENT_RESUMED);
+       target->state = TARGET_RUNNING;
+
+       return ERROR_OK;
+}
+
+static int
+lm32_step(struct target *target, int current, uint32_t address, int handle_breakpoints)
+{
+       LOG_WARNING("not implemented");
+
+       return ERROR_OK;
+}
+
+static int
+lm32_soft_reset_halt(struct target *target)
+{
+       struct lm32 *lm32 = target->arch_info;
+       int i;
+       uint8_t data;
+
+       LOG_DEBUG("-");
+
+       lm32_send_command(target, LM32_CMD_BREAK, 0);
+       lm32_read_rx(target, &data);
+       lm32_write_tx(target, LM32_MONITOR_CMD_WRITE_CSR);
+       lm32_write_tx(target, LM32_MONITOR_WRITE_CSR_DC);
+       /* Cn = disabled, RE = remap all exception, SS = single step disabled */
+       lm32_send_u32(target, 0x00000002);
+
+       /* clear registers */
+       for (i = 0; i < LM32_NUM_REGS; i++)
+       {
+               data = 0;
+               buf_set_u32(lm32->reg_cache->reg_list[i].value, 0, 32, 0);
+               lm32->reg_cache->reg_list[i].dirty = 1;
+       }
+       lm32_restore_context(target);
+
+       lm32_send_command(target, LM32_CMD_RESET, 1);
+
+       target->state = TARGET_HALTED;
+       target->debug_reason = DBG_REASON_DBGRQ;
+       lm32_invalidate_regs(target);
+
+       lm32_debug_entry(target);
+       
+       return ERROR_OK;
+}
+
+static int
+lm32_get_gdb_reg_list(struct target *target, struct reg **reg_list[], int *reg_list_size)
+{
+       struct lm32 *lm32 = target->arch_info;
+       int i;
+
+       *reg_list_size = LM32_NUM_REGS;
+       *reg_list = calloc(sizeof(struct reg*), LM32_NUM_REGS);
+
+       for (i = 0; i < LM32_NUM_REGS; i++)
+       {
+               (*reg_list)[i] = &(lm32->reg_cache->reg_list[i]);
+       }
+
+       return ERROR_OK;
+}
+
+static int
+lm32_set_breakpoint(struct target *target, struct breakpoint *breakpoint)
+{
+       struct lm32 *lm32 = target->arch_info;
+
+       int retval;
+
+       if (breakpoint->set)
+       {
+               LOG_WARNING("breakpoint already set");
+               return ERROR_OK;
+       }
+
+       switch (breakpoint->type)
+       {
+               case BKPT_HARD:
+               {
+                       int bp_num = 0;
+                       int csr = 0;
+
+                       /* find free hw bp */
+                       for (bp_num = 0; !((1 << bp_num) & lm32->bps_used); bp_num++);
+
+                       switch (bp_num)
+                       {
+                               case 0: csr = LM32_MONITOR_WRITE_CSR_BP0; break;
+                               case 1: csr = LM32_MONITOR_WRITE_CSR_BP1; break;
+                               case 2: csr = LM32_MONITOR_WRITE_CSR_BP2; break;
+                               case 3: csr = LM32_MONITOR_WRITE_CSR_BP3; break;
+                               default:
+                                       LOG_ERROR("BUG: bp# %i not supported", bp_num);
+                                       exit(-1);
+                                       break;
+                       }
+
+                       lm32_write_tx(target, LM32_MONITOR_CMD_WRITE_CSR);
+                       lm32_write_tx(target, csr);
+                       lm32_send_u32(target, breakpoint->address + 1);
+
+                       /* mark bp as used */
+                       lm32->bps_used |= (1 << bp_num);
+                       /* remember bp_num */
+                       breakpoint->set = bp_num + 1;
+                       LOG_DEBUG("bp_num %i bp_value 0x%x", bp_num, breakpoint->address);
+               } break;
+               case BKPT_SOFT:
+               {
+                       uint32_t verify = 0xffffffff;
+
+                       if((retval = lm32_read_memory(target, breakpoint->address, breakpoint->length, 1, breakpoint->orig_instr)) != ERROR_OK)
+                       {
+                               LOG_DEBUG("error reading memory");
+                               return retval;
+                       }
+
+                       if ((retval = target_write_u32(target, breakpoint->address, LM32_INST_BREAK)) != ERROR_OK)
+                       {
+                               LOG_DEBUG("error writing software breakpoint");
+                               return retval;
+                       }
+
+                       if ((retval = target_read_u32(target, breakpoint->address, &verify)) != ERROR_OK)
+                       {
+                               LOG_DEBUG("error reading back software breakpoint");
+                               return retval;
+                       }
+
+                       if (verify != LM32_INST_BREAK)
+                       {
+                               LOG_ERROR("Unable to set 32bit breakpoint at address %08x - check that memory is read/writable", breakpoint->address);
+                               return ERROR_OK;
+                       }
+
+                       breakpoint->set = 1;
+               } break;
+               default:
+               {
+                       LOG_ERROR("BUG: unknown breakpoint type");
+                       exit(-1);
+               } break;
+       }
+
+       return ERROR_OK;
+}
+
+static int
+lm32_unset_breakpoint(struct target *target, struct breakpoint *breakpoint)
+{
+       struct lm32 *lm32 = target->arch_info;
+       int retval;
+
+       if (!breakpoint->set)
+       {
+               LOG_WARNING("breakpoint not set");
+               return ERROR_OK;
+       }
+
+       switch (breakpoint->type)
+       {
+               case BKPT_HARD:
+               {
+                       int bp_num = breakpoint->set - 1;
+                       int csr = 0;
+
+                       switch (bp_num)
+                       {
+                               case 0: csr = LM32_MONITOR_WRITE_CSR_BP0; break;
+                               case 1: csr = LM32_MONITOR_WRITE_CSR_BP1; break;
+                               case 2: csr = LM32_MONITOR_WRITE_CSR_BP2; break;
+                               case 3: csr = LM32_MONITOR_WRITE_CSR_BP3; break;
+                               default:
+                                       LOG_ERROR("BUG: bp# %i not supported", bp_num);
+                                       exit(-1);
+                                       break;
+                       }
+
+                       /* clear used flag */
+                       lm32->bps_used &= ~(1 << bp_num);
+
+                       lm32_write_tx(target, LM32_MONITOR_CMD_WRITE_CSR);
+                       lm32_write_tx(target, csr);
+                       lm32_send_u32(target, 0);
+               } break;
+               case BKPT_SOFT:
+               {
+                       uint32_t current_instr;
+
+                       /* check that user program has not modified breakpoint instruction */
+                       if ((retval = lm32_read_memory(target, breakpoint->address, 4, 1, (uint8_t*)&current_instr)) != ERROR_OK)
+                       {
+                               return retval;
+                       }
+                       if (current_instr == LM32_INST_BREAK)
+                       {
+                               if ((retval = lm32_write_memory(target, breakpoint->address, 4, 1, breakpoint->orig_instr)) != ERROR_OK)
+                               {
+                                       return retval;
+                               }
+                       }
+               } break;
+               default:
+               {
+                       LOG_ERROR("BUG: unknown breakpoint type");
+                       exit(-1);
+               } break;
+       }
+
+       breakpoint->set = 0;
+
+       return ERROR_OK;
+}
+
+static int
+lm32_add_breakpoint(struct target *target, struct breakpoint *breakpoint)
+{
+       struct lm32 *lm32 = target->arch_info;
+
+       if (target->state != TARGET_HALTED)
+       {
+               LOG_WARNING("target not halted");
+               return ERROR_TARGET_NOT_HALTED;
+       }
+
+       if (breakpoint->length != 4)
+               return ERROR_INVALID_ARGUMENTS;
+
+       if (breakpoint->type == BKPT_HARD)
+       {
+               if (lm32->num_bps_avail < 1)
+               {
+                       LOG_INFO("no hardware breakpoint available");
+                       return ERROR_TARGET_RESOURCE_NOT_AVAILABLE;
+               }
+
+               lm32->num_bps_avail--;
+       }
+
+       lm32_set_breakpoint(target, breakpoint);
+
+       return ERROR_OK;
+}
+
+static int
+lm32_remove_breakpoint(struct target *target, struct breakpoint *breakpoint)
+{
+       struct lm32 *lm32 = target->arch_info;
+
+       if (target->state != TARGET_HALTED)
+       {
+               LOG_WARNING("target not halted");
+               return ERROR_TARGET_NOT_HALTED;
+       }
+
+       if (breakpoint->length != 4)
+               return ERROR_INVALID_ARGUMENTS;
+
+       if (breakpoint->set)
+       {
+               lm32_unset_breakpoint(target, breakpoint);
+       }
+
+       if (breakpoint->type == BKPT_HARD)
+               lm32->num_bps_avail++;
+
+       return ERROR_OK;
+}
+
+static int
+lm32_add_watchpoint(struct target *target, struct watchpoint *watchpoint)
+{
+       LOG_WARNING("not implemented");
+
+       return ERROR_OK;
+}
+
+static int
+lm32_remove_watchpoint(struct target *target, struct watchpoint *watchpoint)
+{
+       LOG_WARNING("not implemented");
+
+       return ERROR_OK;
+}
+
+static int
+lm32_get_reg(struct reg *reg)
+{
+       struct lm32_reg *arch_info = reg->arch_info;
+       struct target *target = arch_info->target;
+       struct lm32 *lm32 = target->arch_info;
+       int num = arch_info->num;
+       uint8_t buf[4];
+       uint32_t data;
+
+       LOG_DEBUG("name=%s", reg->name);
+
+       if (target->state != TARGET_HALTED)
+       {
+               return ERROR_TARGET_NOT_HALTED;
+       }
+
+       /* XXX endianess */
+       if (lm32_read_memory(target, lm32->reg_base + (4*num), 4, 1, buf) != ERROR_OK)
+       {
+               LOG_DEBUG("error reading memory");
+               return ERROR_FAIL;
+       }
+
+       data = buf[0] << 24 | buf[1] << 16 | buf[2] << 8 | buf[3];
+       buf_set_u32(lm32->reg_cache->reg_list[num].value, 0, 32, data);
+       
+       reg->dirty = 0;
+       reg->valid = 1;
+
+       return ERROR_OK;
+}
+
+static int
+lm32_set_reg(struct reg *reg, uint8_t *buf)
+{
+       struct lm32_reg *arch_info = reg->arch_info;
+       struct target *target = arch_info->target;
+       struct lm32 *lm32 = target->arch_info;
+       int num = arch_info->num;
+       uint32_t value = buf_get_u32(buf, 0, 32);
+
+       LOG_DEBUG("reg=%s value=%08x", reg->name, value);
+
+       if (target->state != TARGET_HALTED)
+       {
+               return ERROR_TARGET_NOT_HALTED;
+       }
+               
+       if (lm32_write_memory(target, lm32->reg_base + (4*num), 4, 1, buf) != ERROR_OK)
+       {
+               LOG_DEBUG("error reading memory");
+               return ERROR_FAIL;
+       }
+
+       reg->dirty = 0;
+
+       return ERROR_OK;
+}
+
+static const struct reg_arch_type lm32_reg_type =
+{
+       .get = lm32_get_reg,
+       .set = lm32_set_reg,
+};
+
+static void
+lm32_build_reg_cache(struct target *target)
+{
+       struct lm32 *lm32 = target->arch_info;
+       struct reg_cache **cache_p = register_get_last_cache_p(&target->reg_cache);
+       struct lm32_reg *arch_info = malloc(sizeof(lm32_reg_arch_info));
+       int i;
+       int num_regs = ARRAY_SIZE(lm32_reg_arch_info);
+
+       struct reg_cache *cache = malloc(sizeof(struct reg_cache));
+       struct reg *reg_list = malloc(num_regs * sizeof(struct reg));
+
+       cache->name = "LM32 registers";
+       cache->next = NULL;
+       cache->reg_list = reg_list;
+       cache->num_regs = num_regs;
+
+       for (i = 0; i < num_regs; i++)
+       {
+               reg_list[i].name = lm32_reg_list[i];
+               reg_list[i].value = calloc(4, 1);
+               reg_list[i].dirty = 0;
+               reg_list[i].valid = 0;
+               reg_list[i].size = 32;
+               reg_list[i].arch_info = &arch_info[i];
+               reg_list[i].type = &lm32_reg_type;
+               arch_info[i] = lm32_reg_arch_info[i];
+               arch_info[i].target = target;
+       }
+
+       *cache_p = cache;
+       lm32->reg_cache = cache;
+}
+
+static int
+lm32_init_arch_info(struct target *target, struct lm32 *lm32, struct jtag_tap *tap, const char *variant)
+{
+       target->arch_info = lm32;
+
+       if (!variant || !strlen(variant)) {
+               LOG_ERROR("variant not defined");
+               return ERROR_FAIL;
+       }
+
+       if (strcmp(variant, "xc6s") == 0)
+       {
+#define XC6S_USER1 0x2
+               lm32->ir_insn = XC6S_USER1;
+       }
+       else
+       {
+               LOG_ERROR("%s: unrecognized variant %s", tap->dotted_name, variant);
+               return ERROR_FAIL;
+       }
+
+       return ERROR_OK;
+}
+
+static int
+lm32_init_target(struct command_context *cmd_ctx, struct target *target)
+{
+       lm32_build_reg_cache(target);
+
+       return ERROR_OK;
+}
+
+static int
+lm32_examine(struct target *target)
+{
+       struct lm32 *lm32 = target->arch_info;
+
+       /* set instruction register */
+       LOG_DEBUG("setting IR to 0x%04x", lm32->ir_insn);
+       lm32_jtag_set_instr(target->tap, lm32->ir_insn, TAP_IDLE);
+
+       target_set_examined(target);
+
+       return ERROR_OK;
+}
+
+static int
+lm32_target_create(struct target *target, Jim_Interp *interp)
+{
+       struct lm32 *lm32 = calloc(1, sizeof(struct lm32));
+
+       if (!lm32)
+               return ERROR_FAIL;
+
+       return lm32_init_arch_info(target, lm32, target->tap,
+                       target->variant);
+}
+
+static int
+lm32_bulk_write_memory(struct target *target, uint32_t address, uint32_t count, uint8_t *buffer)
+{
+       return lm32_write_memory(target, address, 4, count, buffer);
+}
+
+static int
+lm32_checksum_memory(struct target *target, uint32_t address, uint32_t size, uint32_t *checksum)
+{
+       return ERROR_FAIL; /* use bulk read method */
+}
+
+/*
+ * command handlers
+ */
+COMMAND_HANDLER(lm32_handle_break_command)
+{
+       struct target *target = get_current_target(CMD_CTX);
+       return lm32_send_command(target, LM32_CMD_BREAK, 1);
+}
+
+COMMAND_HANDLER(lm32_handle_reset_command)
+{
+       struct target *target = get_current_target(CMD_CTX);
+       /* XXX */
+       target->state = TARGET_RUNNING;
+       return lm32_send_command(target, LM32_CMD_RESET, 1);
+}
+
+COMMAND_HANDLER(lm32_handle_status_command)
+{
+       struct target *target = get_current_target(CMD_CTX);
+       uint32_t status;
+
+       lm32_get_status(target, &status);
+
+       command_print(CMD_CTX, "rx_ready=%i tx_full=%i processing=%i",
+               (status & LM32_STAT_RX_READY) ? 1 : 0,
+               (status & LM32_STAT_TX_FULL) ? 1 : 0,
+               (status & LM32_STAT_PROCESSING) ? 1 : 0
+       );
+
+       return ERROR_OK;
+}
+
+COMMAND_HANDLER(lm32_handle_juart_read_command)
+{
+       struct target *target = get_current_target(CMD_CTX);
+       uint8_t data;
+       int ret;
+
+       if ((ret = lm32_read_rx(target, &data)) != ERROR_OK)
+       {
+               LOG_ERROR("error reading rx");
+               return ret;
+       }
+
+       command_print(CMD_CTX, "rx=%02x", data);
+
+       return ERROR_OK;
+}
+
+COMMAND_HANDLER(lm32_handle_juart_write_command)
+{
+       struct target *target = get_current_target(CMD_CTX);
+       uint8_t byte;
+
+       if (CMD_ARGC < 1)
+       {
+               LOG_ERROR("'lm32 juart write <byte>' command takes one operands");
+               return ERROR_OK;
+       }
+       
+       COMMAND_PARSE_NUMBER(u8, CMD_ARGV[1], byte);
+
+       return lm32_write_tx(target, byte);
+}
+
+static const struct command_registration lm32_juart_command_handlers[] =
+{
+       {
+               .name = "read",
+               .handler = lm32_handle_juart_read_command,
+               .mode = COMMAND_EXEC,
+               .help = "Read from the JTAG uart.",
+               .usage = "",
+       },
+       {
+               .name = "write",
+               .handler = lm32_handle_juart_write_command,
+               .mode = COMMAND_EXEC,
+               .help = "Write to the JTAG uart.",
+               .usage = "byte",
+       },
+       COMMAND_REGISTRATION_DONE
+};
+
+static const struct command_registration lm32_any_command_handlers[] =
+{
+       {
+               .name = "reset",
+               .handler = lm32_handle_reset_command,
+               .mode = COMMAND_ANY,
+               .help = "Set PC to reset vector.",
+               .usage = "",
+       },
+       {
+               .name = "break",
+               .handler = lm32_handle_break_command,
+               .mode = COMMAND_ANY,
+               .help = "Set PC to debug vector.",
+               .usage = "",
+       },
+       {
+               .name = "status",
+               .handler = lm32_handle_status_command,
+               .mode = COMMAND_EXEC,
+               .help = "Shos status flags.",
+               .usage = "",
+       },
+       {
+               .name = "juart",
+               .mode = COMMAND_ANY,
+               .help = "JTAG uart commands",
+               .chain = lm32_juart_command_handlers,
+       },
+       COMMAND_REGISTRATION_DONE
+};
+
+static const struct command_registration lm32_command_handlers[] =
+{
+       {
+               .name = "lm32",
+               .mode = COMMAND_ANY,
+               .help = "lm32 command group",
+               .chain = lm32_any_command_handlers,
+       },
+       COMMAND_REGISTRATION_DONE
+};
+
+struct target_type lm32_target =
+{
+       .name = "lm32",
+
+       .poll = lm32_poll,
+       .arch_state = lm32_arch_state,
+
+       .target_request_data = NULL,
+
+       .halt = lm32_halt,
+       .resume = lm32_resume,
+       .step = lm32_step,
+
+       .assert_reset = lm32_assert_reset,
+       .deassert_reset = lm32_deassert_reset,
+       .soft_reset_halt = lm32_soft_reset_halt,
+
+       .get_gdb_reg_list = lm32_get_gdb_reg_list,
+
+       .read_memory = lm32_read_memory,
+       .read_phys_memory = NULL,
+       .write_memory = lm32_write_memory,
+       .write_phys_memory = NULL,
+       .bulk_write_memory = lm32_bulk_write_memory,
+
+       .checksum_memory = lm32_checksum_memory,
+       .blank_check_memory = NULL,
+
+       .run_algorithm = NULL,
+
+       .add_breakpoint = lm32_add_breakpoint,
+       .remove_breakpoint = lm32_remove_breakpoint,
+       .add_watchpoint = lm32_add_watchpoint,
+       .remove_watchpoint = lm32_remove_watchpoint,
+
+       .commands = lm32_command_handlers,
+       .target_create = lm32_target_create,
+       .init_target = lm32_init_target,
+       .examine = lm32_examine,
+
+       .virt2phys = NULL,
+       .mmu = NULL,
+};
+
index c37432a..5d165b0 100644 (file)
@@ -71,6 +71,7 @@ extern struct target_type avr_target;
 extern struct target_type dsp563xx_target;
 extern struct target_type testee_target;
 extern struct target_type avr32_ap7k_target;
+extern struct target_type lm32_target;
 
 static struct target_type *target_types[] =
 {
@@ -92,6 +93,7 @@ static struct target_type *target_types[] =
        &dsp563xx_target,
        &testee_target,
        &avr32_ap7k_target,
+       &lm32_target,
        NULL,
 };