stm8 : new target 53/3953/11
authorAke Rehnman <ake.rehnman@gmail.com>
Mon, 6 Nov 2017 18:56:28 +0000 (19:56 +0100)
committerPaul Fertser <fercerpav@gmail.com>
Thu, 7 Dec 2017 07:53:13 +0000 (07:53 +0000)
New STM8 target based mostly on mips4k. Target communication
through STLINK/SWIM. No flash driver yet but it is still possible
to program flash through load_image command. The usual target debug
methods are implemented.

Change-Id: I7216f231d3ac7c70cae20f1cd8463c2ed864a329
Signed-off-by: Ake Rehnman <ake.rehnman@gmail.com>
Reviewed-on: http://openocd.zylin.com/3953
Tested-by: jenkins
Reviewed-by: Tomas Vanek <vanekt@fbl.cz>
Reviewed-by: Paul Fertser <fercerpav@gmail.com>
contrib/loaders/Makefile
contrib/loaders/erase_check/Makefile
contrib/loaders/erase_check/stm8_erase_check.inc [new file with mode: 0644]
contrib/loaders/erase_check/stm8_erase_check.s [new file with mode: 0644]
src/target/Makefile.am
src/target/stm8.c [new file with mode: 0644]
src/target/stm8.h [new file with mode: 0644]
src/target/target.c
tcl/target/stm8l.cfg [new file with mode: 0644]
tcl/target/stm8s.cfg [new file with mode: 0644]

index 31cccb5ffe16305ea2592ed37d22f8af43a10715..a9a27706d682bc49a12c88b8c9aa7d2e22f39ec3 100644 (file)
@@ -1,6 +1,6 @@
 .PHONY: arm clean-arm
 
-all: arm
+all: arm stm8
 
 common_dirs = \
        checksum \
@@ -32,3 +32,6 @@ clean: clean-arm
        for d in $(common_dirs); do \
                $(MAKE) -C $$d clean; \
        done
+
+stm8:
+       $(MAKE) -C erase_check stm8
index 01e62dead37b85bdc731563fd5ea11671ee99348..427fa0c079284480de9f45ef308132c2bd1191c1 100644 (file)
@@ -6,6 +6,12 @@ ARM_OBJCOPY ?= $(ARM_CROSS_COMPILE)objcopy
 
 ARM_AFLAGS = -EL
 
+STM8_CROSS_COMPILE ?= stm8-
+STM8_AS      ?= $(STM8_CROSS_COMPILE)as
+STM8_OBJCOPY ?= $(STM8_CROSS_COMPILE)objcopy
+
+STM8_AFLAGS =
+
 arm: armv4_5_erase_check.inc armv7m_erase_check.inc armv7m_0_erase_check.inc
 
 armv4_5_%.elf: armv4_5_%.s
@@ -26,5 +32,16 @@ armv7m_%.bin: armv7m_%.elf
 armv7m_%.inc: armv7m_%.bin
        $(BIN2C) < $< > $@
 
+stm8: stm8_erase_check.inc
+
+stm8_%.elf: stm8_%.s
+       $(STM8_AS) $(STM8_AFLAGS) $< -o $@
+
+stm8_%.bin: stm8_%.elf
+       $(STM8_OBJCOPY) -Obinary $< $@
+
+stm8_%.inc: stm8_%.bin
+       $(BIN2C) < $< > $@
+
 clean:
        -rm -f *.elf *.bin *.inc
diff --git a/contrib/loaders/erase_check/stm8_erase_check.inc b/contrib/loaders/erase_check/stm8_erase_check.inc
new file mode 100644 (file)
index 0000000..66b4ec7
--- /dev/null
@@ -0,0 +1,5 @@
+/* Autogenerated with ../../../src/helper/bin2char.sh */
+0x00,0x80,0x00,0x00,0x80,0x00,0x96,0xcf,0x00,0x22,0x1e,0x01,0x16,0x04,0xa6,0xff,
+0x90,0x5d,0x26,0x04,0x0d,0x03,0x27,0x17,0x90,0x5d,0x26,0x02,0x0a,0x03,0x90,0x5a,
+0x92,0xbc,0x00,0x00,0xa1,0xff,0x26,0x07,0x5c,0x26,0xe5,0x0c,0x00,0x20,0xe1,0x1f,
+0x01,0x17,0x04,0x8b,
diff --git a/contrib/loaders/erase_check/stm8_erase_check.s b/contrib/loaders/erase_check/stm8_erase_check.s
new file mode 100644 (file)
index 0000000..6269400
--- /dev/null
@@ -0,0 +1,69 @@
+/*
+    Copyright (C) 2017 Ake Rehnman
+    ake.rehnman(at)gmail.com
+
+    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 3 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/>.
+*/
+;;
+;; erase check memory code
+;;
+ .org 0x0
+;; start address
+ start_addr: .byte 0x00
+       .word 0x8000
+;; byte count
+ byte_cnt: .byte 0x00
+       .word 0x8000
+;
+; SP must point to start_addr on entry
+; first relocate start_addr to the location
+; we are running at
+start:
+       ldw X,SP
+       ldw .cont+2,X
+       ldw X,(start_addr+1,SP) ;start addr
+       ldw Y,(byte_cnt+1,SP)   ;count
+       ld A,#0xff
+;
+; if count == 0 return
+.L1:
+       tnzw Y
+       jrne .decrcnt   ;continue if low word != 0
+       tnz (byte_cnt,SP)       ;high byte
+       jreq .exit      ;goto exit
+;
+; decrement count (byte_cnt)
+.decrcnt:
+       tnzw Y  ;low word count
+       jrne .decr1
+       dec (byte_cnt,SP)       ;high byte
+.decr1:
+       decw Y; decr low word
+;
+; first check if [start_addr] is 0xff
+.cont:
+       ldf A, [start_addr.e]
+       cp A,#0xff
+       jrne .exit ;exit if not 0xff
+;
+; increment start_addr (addr)
+       incw X
+       jrne .L1
+       inc (start_addr,SP)     ;increment high byte
+       jra .L1
+;
+.exit:
+       ldw (start_addr+1,SP),X ;start addr
+       ldw (byte_cnt+1,SP),Y   ;count
+       break
index 597070c4b86b0b4e4814fea10dc2face80694a07..d2aab0a5e3590d78170dc75c5f425f8b6b6c762e 100644 (file)
@@ -19,6 +19,7 @@ noinst_LTLIBRARIES += %D%/libtarget.la
        $(AVR32_SRC) \
        $(MIPS32_SRC) \
        $(NDS32_SRC) \
+       $(STM8_SRC) \
        $(INTEL_IA32_SRC) \
        %D%/avrt.c \
        %D%/dsp563xx.c \
@@ -124,6 +125,9 @@ NDS32_SRC = \
        %D%/nds32_v3m.c \
        %D%/nds32_aice.c
 
+STM8_SRC = \
+       %D%/stm8.c
+
 INTEL_IA32_SRC = \
        %D%/quark_x10xx.c \
        %D%/quark_d20xx.c \
@@ -205,6 +209,7 @@ INTEL_IA32_SRC = \
        %D%/nds32_v3.h \
        %D%/nds32_v3m.h \
        %D%/nds32_aice.h \
+       %D%/stm8.h \
        %D%/lakemont.h \
        %D%/x86_32_common.h \
        %D%/arm_cti.h
diff --git a/src/target/stm8.c b/src/target/stm8.c
new file mode 100644 (file)
index 0000000..262497b
--- /dev/null
@@ -0,0 +1,2219 @@
+/*
+    OpenOCD STM8 target driver
+    Copyright (C) 2017  Ake Rehnman
+    ake.rehnman(at)gmail.com
+
+    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 3 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 <helper/log.h>
+#include "target.h"
+#include "target_type.h"
+#include "hello.h"
+#include "jtag/jtag.h"
+#include "jtag/hla/hla_transport.h"
+#include "jtag/hla/hla_interface.h"
+#include "jtag/hla/hla_layout.h"
+#include "register.h"
+#include "breakpoints.h"
+#include "algorithm.h"
+#include "stm8.h"
+
+static struct reg_cache *stm8_build_reg_cache(struct target *target);
+static int stm8_read_core_reg(struct target *target, unsigned int num);
+static int stm8_write_core_reg(struct target *target, unsigned int num);
+static int stm8_save_context(struct target *target);
+static void stm8_enable_breakpoints(struct target *target);
+static int stm8_unset_breakpoint(struct target *target,
+               struct breakpoint *breakpoint);
+static int stm8_set_breakpoint(struct target *target,
+               struct breakpoint *breakpoint);
+static void stm8_enable_watchpoints(struct target *target);
+static int stm8_unset_watchpoint(struct target *target,
+               struct watchpoint *watchpoint);
+
+static const struct {
+       unsigned id;
+       const char *name;
+       const uint8_t bits;
+       enum reg_type type;
+       const char *group;
+       const char *feature;
+       int flag;
+} stm8_regs[] = {
+       {  0,  "pc", 32, REG_TYPE_UINT32, "general", "org.gnu.gdb.stm8.core", 0 },
+       {  1,  "a", 8, REG_TYPE_UINT8, "general", "org.gnu.gdb.stm8.core", 0 },
+       {  2,  "x", 16, REG_TYPE_UINT16, "general", "org.gnu.gdb.stm8.core", 0 },
+       {  3,  "y", 16, REG_TYPE_UINT16, "general", "org.gnu.gdb.stm8.core", 0 },
+       {  4,  "sp", 16, REG_TYPE_UINT16, "general", "org.gnu.gdb.stm8.core", 0 },
+       {  5,  "cc", 8, REG_TYPE_UINT8, "general", "org.gnu.gdb.stm8.core", 0 },
+};
+
+#define STM8_NUM_REGS ARRAY_SIZE(stm8_regs)
+#define STM8_PC 0
+#define STM8_A 1
+#define STM8_X 2
+#define STM8_Y 3
+#define STM8_SP 4
+#define STM8_CC 5
+
+#define CC_I0 0x8
+#define CC_I1 0x20
+
+#define DM_REGS 0x7f00
+#define DM_REG_A 0x7f00
+#define DM_REG_PC 0x7f01
+#define DM_REG_X 0x7f04
+#define DM_REG_Y 0x7f06
+#define DM_REG_SP 0x7f08
+#define DM_REG_CC 0x7f0a
+
+#define DM_BKR1E 0x7f90
+#define DM_BKR2E 0x7f93
+#define DM_CR1 0x7f96
+#define DM_CR2 0x7f97
+#define DM_CSR1 0x7f98
+#define DM_CSR2 0x7f99
+
+#define STE 0x40
+#define STF 0x20
+#define RST 0x10
+#define BRW 0x08
+#define BK2F 0x04
+#define BK1F 0x02
+
+#define SWBRK 0x20
+#define SWBKF 0x10
+#define STALL 0x08
+#define FLUSH 0x01
+
+#define FLASH_CR1_STM8S 0x505A
+#define FLASH_CR2_STM8S 0x505B
+#define FLASH_NCR2_STM8S 0x505C
+#define FLASH_IAPSR_STM8S 0x505F
+#define FLASH_PUKR_STM8S 0x5062
+#define FLASH_DUKR_STM8S 0x5064
+
+#define FLASH_CR1_STM8L 0x5050
+#define FLASH_CR2_STM8L 0x5051
+#define FLASH_NCR2_STM8L 0
+#define FLASH_PUKR_STM8L 0x5052
+#define FLASH_DUKR_STM8L 0x5053
+#define FLASH_IAPSR_STM8L 0x5054
+
+/* FLASH_IAPSR */
+#define HVOFF 0x40
+#define DUL 0x08
+#define EOP 0x04
+#define PUL 0x02
+#define WR_PG_DIS 0x01
+
+/* FLASH_CR2 */
+#define OPT 0x80
+#define WPRG 0x40
+#define ERASE 0x20
+#define FPRG 0x10
+#define PRG 0x01
+
+/* SWIM_CSR */
+#define SAFE_MASK 0x80
+#define NO_ACCESS 0x40
+#define SWIM_DM 0x20
+#define HS 0x10
+#define OSCOFF 0x08
+#define SWIM_RST 0x04
+#define HSIT 0x02
+#define PRI 0x01
+
+#define SWIM_CSR 0x7f80
+
+#define STM8_BREAK 0x8B
+
+enum mem_type {
+       RAM,
+       FLASH,
+       EEPROM,
+       OPTION
+};
+
+struct stm8_algorithm {
+       int common_magic;
+};
+
+struct stm8_core_reg {
+       uint32_t num;
+       struct target *target;
+       struct stm8_common *stm8_common;
+};
+
+enum hw_break_type {
+       /* break on execute */
+       HWBRK_EXEC,
+       /* break on read */
+       HWBRK_RD,
+       /* break on write */
+       HWBRK_WR,
+       /* break on read, write and execute */
+       HWBRK_ACC
+};
+
+struct stm8_comparator {
+       bool used;
+       uint32_t bp_value;
+       uint32_t reg_address;
+       enum hw_break_type type;
+};
+
+static inline struct hl_interface_s *target_to_adapter(struct target *target)
+{
+       return target->tap->priv;
+}
+
+static int stm8_adapter_read_memory(struct target *target,
+               uint32_t addr, int size, int count, void *buf)
+{
+       int ret;
+       struct hl_interface_s *adapter = target_to_adapter(target);
+
+       ret = adapter->layout->api->read_mem(adapter->handle,
+               addr, size, count, buf);
+       if (ret != ERROR_OK)
+               return ret;
+       return ERROR_OK;
+}
+
+static int stm8_adapter_write_memory(struct target *target,
+               uint32_t addr, int size, int count, const void *buf)
+{
+       int ret;
+       struct hl_interface_s *adapter = target_to_adapter(target);
+
+       ret = adapter->layout->api->write_mem(adapter->handle,
+               addr, size, count, buf);
+       if (ret != ERROR_OK)
+               return ret;
+       return ERROR_OK;
+}
+
+static int stm8_write_u8(struct target *target,
+               uint32_t addr, uint8_t val)
+{
+       int ret;
+       uint8_t buf[1];
+       struct hl_interface_s *adapter = target_to_adapter(target);
+
+       buf[0] = val;
+       ret =  adapter->layout->api->write_mem(adapter->handle, addr, 1, 1, buf);
+       if (ret != ERROR_OK)
+               return ret;
+       return ERROR_OK;
+}
+
+static int stm8_read_u8(struct target *target,
+               uint32_t addr, uint8_t *val)
+{
+       int ret;
+       struct hl_interface_s *adapter = target_to_adapter(target);
+
+       ret =  adapter->layout->api->read_mem(adapter->handle, addr, 1, 1, val);
+       if (ret != ERROR_OK)
+               return ret;
+       return ERROR_OK;
+}
+
+static int stm8_set_speed(struct target *target, int speed)
+{
+       struct hl_interface_s *adapter = target_to_adapter(target);
+       adapter->layout->api->speed(adapter->handle, speed, 0);
+       return ERROR_OK;
+}
+
+/*
+       <enable == 0> Disables interrupts.
+       If interrupts are enabled they are masked and the cc register
+       is saved.
+
+       <enable == 1> Enables interrupts.
+       Enable interrupts is actually restoring I1 I0 state from previous
+       call with enable == 0. Note that if stepping and breaking on a sim
+       instruction will NOT work since the interrupt flags are restored on
+       debug_entry. We don't have any way for the debugger to exclusively
+       disable the interrupts
+*/
+static int stm8_enable_interrupts(struct target *target, int enable)
+{
+       struct stm8_common *stm8 = target_to_stm8(target);
+       uint8_t cc;
+
+       if (enable) {
+               if (!stm8->cc_valid)
+                       return ERROR_OK; /* cc was not stashed */
+               /* fetch current cc */
+               stm8_read_u8(target, DM_REG_CC, &cc);
+               /* clear I1 I0 */
+               cc &= ~(CC_I0 + CC_I1);
+               /* restore I1 & I0 from stash*/
+               cc |= (stm8->cc & (CC_I0+CC_I1));
+               /* update current cc */
+               stm8_write_u8(target, DM_REG_CC, cc);
+               stm8->cc_valid = false;
+       } else {
+               stm8_read_u8(target, DM_REG_CC, &cc);
+               if ((cc & CC_I0) && (cc & CC_I1))
+                       return ERROR_OK; /* interrupts already masked */
+               /* stash cc */
+               stm8->cc = cc;
+               stm8->cc_valid = true;
+               /* mask interrupts (disable) */
+               cc |= (CC_I0 + CC_I1);
+               stm8_write_u8(target, DM_REG_CC, cc);
+       }
+
+       return ERROR_OK;
+}
+
+static int stm8_set_hwbreak(struct target *target,
+               struct stm8_comparator comparator_list[])
+{
+       uint8_t buf[3];
+       int i, ret;
+
+       /* Refer to Table 4 in UM0470 */
+       uint8_t bc = 0x5;
+       uint8_t bir = 0;
+       uint8_t biw = 0;
+
+       uint32_t data;
+       uint32_t addr;
+
+       if (!comparator_list[0].used) {
+               comparator_list[0].type = HWBRK_EXEC;
+               comparator_list[0].bp_value = -1;
+       }
+
+       if (!comparator_list[1].used) {
+               comparator_list[1].type = HWBRK_EXEC;
+               comparator_list[1].bp_value = -1;
+       }
+
+       if ((comparator_list[0].type == HWBRK_EXEC)
+                       && (comparator_list[1].type == HWBRK_EXEC)) {
+               comparator_list[0].reg_address = 0;
+               comparator_list[1].reg_address = 1;
+       }
+
+       if ((comparator_list[0].type == HWBRK_EXEC)
+                       && (comparator_list[1].type != HWBRK_EXEC)) {
+               comparator_list[0].reg_address = 0;
+               comparator_list[1].reg_address = 1;
+               switch (comparator_list[1].type) {
+               case HWBRK_RD:
+                       bir = 1;
+                       break;
+               case HWBRK_WR:
+                       biw = 1;
+                       break;
+               default:
+                       bir = 1;
+                       biw = 1;
+                       break;
+               }
+       }
+
+       if ((comparator_list[1].type == HWBRK_EXEC)
+                       && (comparator_list[0].type != HWBRK_EXEC)) {
+               comparator_list[0].reg_address = 1;
+               comparator_list[1].reg_address = 0;
+               switch (comparator_list[0].type) {
+               case HWBRK_RD:
+                       bir = 1;
+                       break;
+               case HWBRK_WR:
+                       biw = 1;
+                       break;
+               default:
+                       bir = 1;
+                       biw = 1;
+                       break;
+               }
+       }
+
+       if ((comparator_list[0].type != HWBRK_EXEC)
+                       && (comparator_list[1].type != HWBRK_EXEC)) {
+               if ((comparator_list[0].type != comparator_list[1].type)) {
+                       LOG_ERROR("data hw breakpoints must be of same type");
+                       return ERROR_TARGET_RESOURCE_NOT_AVAILABLE;
+               }
+       }
+
+       for (i = 0; i < 2; i++) {
+               data = comparator_list[i].bp_value;
+               addr = comparator_list[i].reg_address;
+
+               buf[0] = data >> 16;
+               buf[1] = data >> 8;
+               buf[2] = data;
+
+               if (addr == 0) {
+                       ret = stm8_adapter_write_memory(target, DM_BKR1E, 1, 3, buf);
+                       LOG_DEBUG("DM_BKR1E=%" PRIx32, data);
+               } else if (addr == 1) {
+                       ret = stm8_adapter_write_memory(target, DM_BKR2E, 1, 3, buf);
+                       LOG_DEBUG("DM_BKR2E=%" PRIx32, data);
+               } else {
+                       LOG_DEBUG("addr=%" PRIu32, addr);
+                       return ERROR_FAIL;
+               }
+
+               if (ret != ERROR_OK)
+                       return ret;
+
+               ret = stm8_write_u8(target, DM_CR1,
+                       (bc << 3) + (bir << 2) + (biw << 1));
+               LOG_DEBUG("DM_CR1=%" PRIx8, buf[0]);
+               if (ret != ERROR_OK)
+                       return ret;
+
+       }
+       return ERROR_OK;
+}
+
+/* read DM control and status regs */
+static int stm8_read_dm_csrx(struct target *target, uint8_t *csr1,
+               uint8_t *csr2)
+{
+       int ret;
+       uint8_t buf[2];
+
+       ret =  stm8_adapter_read_memory(target, DM_CSR1, 1, sizeof(buf), buf);
+       if (ret != ERROR_OK)
+               return ret;
+       if (csr1)
+               *csr1 = buf[0];
+       if (csr2)
+               *csr2 = buf[1];
+       return ERROR_OK;
+}
+
+/* set or clear the single step flag in DM */
+static int stm8_config_step(struct target *target, int enable)
+{
+       int ret;
+       uint8_t csr1, csr2;
+
+       ret = stm8_read_dm_csrx(target, &csr1, &csr2);
+       if (ret != ERROR_OK)
+               return ret;
+       if (enable)
+               csr1 |= STE;
+       else
+               csr1 &= ~STE;
+
+       ret =  stm8_write_u8(target, DM_CSR1, csr1);
+       if (ret != ERROR_OK)
+               return ret;
+       return ERROR_OK;
+}
+
+/* set the stall flag in DM */
+static int stm8_debug_stall(struct target *target)
+{
+       int ret;
+       uint8_t csr1, csr2;
+
+       ret = stm8_read_dm_csrx(target, &csr1, &csr2);
+       if (ret != ERROR_OK)
+               return ret;
+       csr2 |= STALL;
+       ret =  stm8_write_u8(target, DM_CSR2, csr2);
+       if (ret != ERROR_OK)
+               return ret;
+       return ERROR_OK;
+}
+
+static int stm8_configure_break_unit(struct target *target)
+{
+       /* get pointers to arch-specific information */
+       struct stm8_common *stm8 = target_to_stm8(target);
+
+       if (stm8->bp_scanned)
+               return ERROR_OK;
+
+       stm8->num_hw_bpoints = 2;
+       stm8->num_hw_bpoints_avail = stm8->num_hw_bpoints;
+
+       stm8->hw_break_list = calloc(stm8->num_hw_bpoints,
+               sizeof(struct stm8_comparator));
+
+       stm8->hw_break_list[0].reg_address = 0;
+       stm8->hw_break_list[1].reg_address = 1;
+
+       LOG_DEBUG("hw breakpoints: numinst %i numdata %i", stm8->num_hw_bpoints,
+               stm8->num_hw_bpoints);
+
+       stm8->bp_scanned = true;
+
+       return ERROR_OK;
+}
+
+static int stm8_examine_debug_reason(struct target *target)
+{
+       int retval;
+       uint8_t csr1, csr2;
+
+       retval = stm8_read_dm_csrx(target, &csr1, &csr2);
+       LOG_DEBUG("csr1 = 0x%02X csr2 = 0x%02X", csr1, csr2);
+
+       if ((target->debug_reason != DBG_REASON_DBGRQ)
+               && (target->debug_reason != DBG_REASON_SINGLESTEP)) {
+
+               if (retval != ERROR_OK)
+                       return retval;
+
+               if (csr1 & RST)
+                       /* halted on reset */
+                       target->debug_reason = DBG_REASON_UNDEFINED;
+
+               if (csr1 & (BK1F+BK2F))
+                       /* we have halted on a  breakpoint (or wp)*/
+                       target->debug_reason = DBG_REASON_BREAKPOINT;
+
+               if (csr2 & SWBKF)
+                       /* we have halted on a  breakpoint */
+                       target->debug_reason = DBG_REASON_BREAKPOINT;
+
+       }
+
+       return ERROR_OK;
+}
+
+static int stm8_debug_entry(struct target *target)
+{
+       struct stm8_common *stm8 = target_to_stm8(target);
+
+       /* restore interrupts */
+       stm8_enable_interrupts(target, 1);
+
+       stm8_save_context(target);
+
+       /* make sure stepping disabled STE bit in CSR1 cleared */
+       stm8_config_step(target, 0);
+
+       /* attempt to find halt reason */
+       stm8_examine_debug_reason(target);
+
+       LOG_DEBUG("entered debug state at PC 0x%" PRIx32 ", target->state: %s",
+               buf_get_u32(stm8->core_cache->reg_list[STM8_PC].value, 0, 32),
+               target_state_name(target));
+
+       return ERROR_OK;
+}
+
+/* clear stall flag in DM and flush instruction pipe */
+static int stm8_exit_debug(struct target *target)
+{
+       int ret;
+       uint8_t csr1, csr2;
+
+       ret = stm8_read_dm_csrx(target, &csr1, &csr2);
+       if (ret != ERROR_OK)
+               return ret;
+       csr2 |= FLUSH;
+       ret =  stm8_write_u8(target, DM_CSR2, csr2);
+       if (ret != ERROR_OK)
+               return ret;
+
+       csr2 &= ~STALL;
+       csr2 |= SWBRK;
+       ret =  stm8_write_u8(target, DM_CSR2, csr2);
+       if (ret != ERROR_OK)
+               return ret;
+       return ERROR_OK;
+}
+
+static int stm8_read_regs(struct target *target, uint32_t regs[])
+{
+       int ret;
+       uint8_t buf[11];
+
+       ret =  stm8_adapter_read_memory(target, DM_REGS, 1, sizeof(buf), buf);
+       if (ret != ERROR_OK)
+               return ret;
+
+       regs[0] = be_to_h_u24(buf+DM_REG_PC-DM_REGS);
+       regs[1] = buf[DM_REG_A-DM_REGS];
+       regs[2] = be_to_h_u16(buf+DM_REG_X-DM_REGS);
+       regs[3] = be_to_h_u16(buf+DM_REG_Y-DM_REGS);
+       regs[4] = be_to_h_u16(buf+DM_REG_SP-DM_REGS);
+       regs[5] = buf[DM_REG_CC-DM_REGS];
+
+       return ERROR_OK;
+}
+
+static int stm8_write_regs(struct target *target, uint32_t regs[])
+{
+       int ret;
+       uint8_t buf[11];
+
+       h_u24_to_be(buf+DM_REG_PC-DM_REGS, regs[0]);
+       buf[DM_REG_A-DM_REGS] = regs[1];
+       h_u16_to_be(buf+DM_REG_X-DM_REGS, regs[2]);
+       h_u16_to_be(buf+DM_REG_Y-DM_REGS, regs[3]);
+       h_u16_to_be(buf+DM_REG_SP-DM_REGS, regs[4]);
+       buf[DM_REG_CC-DM_REGS] = regs[5];
+
+       ret =  stm8_adapter_write_memory(target, DM_REGS, 1, sizeof(buf), buf);
+       if (ret != ERROR_OK)
+               return ret;
+
+       return ERROR_OK;
+}
+
+static int stm8_get_core_reg(struct reg *reg)
+{
+       int retval;
+       struct stm8_core_reg *stm8_reg = reg->arch_info;
+       struct target *target = stm8_reg->target;
+       struct stm8_common *stm8_target = target_to_stm8(target);
+
+       if (target->state != TARGET_HALTED)
+               return ERROR_TARGET_NOT_HALTED;
+
+       retval = stm8_target->read_core_reg(target, stm8_reg->num);
+
+       return retval;
+}
+
+static int stm8_set_core_reg(struct reg *reg, uint8_t *buf)
+{
+       struct stm8_core_reg *stm8_reg = reg->arch_info;
+       struct target *target = stm8_reg->target;
+       uint32_t value = buf_get_u32(buf, 0, reg->size);
+
+       if (target->state != TARGET_HALTED)
+               return ERROR_TARGET_NOT_HALTED;
+
+       buf_set_u32(reg->value, 0, 32, value);
+       reg->dirty = true;
+       reg->valid = true;
+
+       return ERROR_OK;
+}
+
+static int stm8_save_context(struct target *target)
+{
+       unsigned int i;
+
+       /* get pointers to arch-specific information */
+       struct stm8_common *stm8 = target_to_stm8(target);
+
+       /* read core registers */
+       stm8_read_regs(target, stm8->core_regs);
+
+       for (i = 0; i < STM8_NUM_REGS; i++) {
+               if (!stm8->core_cache->reg_list[i].valid)
+                       stm8->read_core_reg(target, i);
+       }
+
+       return ERROR_OK;
+}
+
+static int stm8_restore_context(struct target *target)
+{
+       unsigned int i;
+
+       /* get pointers to arch-specific information */
+       struct stm8_common *stm8 = target_to_stm8(target);
+
+       for (i = 0; i < STM8_NUM_REGS; i++) {
+               if (stm8->core_cache->reg_list[i].dirty)
+                       stm8->write_core_reg(target, i);
+       }
+
+       /* write core regs */
+       stm8_write_regs(target, stm8->core_regs);
+
+       return ERROR_OK;
+}
+
+static int stm8_unlock_flash(struct target *target)
+{
+       uint8_t data[1];
+
+       struct stm8_common *stm8 = target_to_stm8(target);
+
+       /* check if flash is unlocked */
+       stm8_read_u8(target, stm8->flash_iapsr, data);
+       if (~data[0] & PUL) {
+               /* unlock flash */
+               stm8_write_u8(target, stm8->flash_pukr, 0x56);
+               stm8_write_u8(target, stm8->flash_pukr, 0xae);
+       }
+
+       stm8_read_u8(target, stm8->flash_iapsr, data);
+       if (~data[0] & PUL)
+               return ERROR_FAIL;
+       return ERROR_OK;
+}
+
+static int stm8_unlock_eeprom(struct target *target)
+{
+       uint8_t data[1];
+
+       struct stm8_common *stm8 = target_to_stm8(target);
+
+       /* check if eeprom is unlocked */
+       stm8_read_u8(target, stm8->flash_iapsr, data);
+       if (~data[0] & DUL) {
+               /* unlock eeprom */
+               stm8_write_u8(target, stm8->flash_dukr, 0xae);
+               stm8_write_u8(target, stm8->flash_dukr, 0x56);
+       }
+
+       stm8_read_u8(target, stm8->flash_iapsr, data);
+       if (~data[0] & DUL)
+               return ERROR_FAIL;
+       return ERROR_OK;
+}
+
+static int stm8_write_flash(struct target *target, enum mem_type type,
+               uint32_t address,
+               uint32_t size, uint32_t count, uint32_t blocksize_param,
+               const uint8_t *buffer)
+{
+       struct stm8_common *stm8 = target_to_stm8(target);
+
+       uint8_t iapsr;
+       uint8_t opt = 0;
+       unsigned int i;
+       uint32_t blocksize = 0;
+       uint32_t bytecnt;
+       int res;
+
+       switch (type) {
+               case (FLASH):
+                       stm8_unlock_flash(target);
+                       break;
+               case (EEPROM):
+                       stm8_unlock_eeprom(target);
+                       break;
+               case (OPTION):
+                       stm8_unlock_eeprom(target);
+                       opt = OPT;
+                       break;
+               default:
+                       LOG_ERROR("BUG: wrong mem_type %d", type);
+                       assert(0);
+       }
+
+       if (size == 2) {
+               /* we don't support short writes */
+               count = count * 2;
+               size = 1;
+       }
+
+       bytecnt = count * size;
+
+       while (bytecnt) {
+               if ((bytecnt >= blocksize_param) && ((address & (blocksize_param-1)) == 0)) {
+                       if (stm8->flash_cr2)
+                               stm8_write_u8(target, stm8->flash_cr2, PRG + opt);
+                       if (stm8->flash_ncr2)
+                               stm8_write_u8(target, stm8->flash_ncr2, ~(PRG + opt));
+                       blocksize = blocksize_param;
+               } else
+               if ((bytecnt >= 4) && ((address & 0x3) == 0)) {
+                       if (stm8->flash_cr2)
+                               stm8_write_u8(target, stm8->flash_cr2, WPRG + opt);
+                       if (stm8->flash_ncr2)
+                               stm8_write_u8(target, stm8->flash_ncr2, ~(WPRG + opt));
+                       blocksize = 4;
+               } else
+               if (blocksize != 1) {
+                       if (stm8->flash_cr2)
+                               stm8_write_u8(target, stm8->flash_cr2, opt);
+                       if (stm8->flash_ncr2)
+                               stm8_write_u8(target, stm8->flash_ncr2, ~opt);
+                       blocksize = 1;
+               }
+
+               res = stm8_adapter_write_memory(target, address, 1, blocksize, buffer);
+               if (res != ERROR_OK)
+                       return res;
+               address += blocksize;
+               buffer += blocksize;
+               bytecnt -= blocksize;
+
+               /* lets hang here until end of program (EOP) */
+               for (i = 0; i < 16; i++) {
+                       stm8_read_u8(target, stm8->flash_iapsr, &iapsr);
+                       if (iapsr & EOP)
+                               break;
+                       else
+                               usleep(1000);
+               }
+               if (i == 16)
+                       return ERROR_FAIL;
+       }
+
+       /* disable write access */
+       res = stm8_write_u8(target, stm8->flash_iapsr, 0x0);
+
+       if (res != ERROR_OK)
+               return ERROR_FAIL;
+
+       return ERROR_OK;
+}
+
+static int stm8_write_memory(struct target *target, target_addr_t address,
+               uint32_t size, uint32_t count,
+               const uint8_t *buffer)
+{
+       struct stm8_common *stm8 = target_to_stm8(target);
+
+       LOG_DEBUG("address: 0x%8.8" TARGET_PRIxADDR
+               ", size: 0x%8.8" PRIx32
+               ", count: 0x%8.8" PRIx32,
+               address, size, count);
+
+       if (target->state != TARGET_HALTED)
+               LOG_WARNING("target not halted");
+
+       int retval;
+
+       if ((address >= stm8->flashstart) && (address <= stm8->flashend))
+               retval = stm8_write_flash(target, FLASH, address, size, count,
+                               stm8->blocksize, buffer);
+       else if ((address >= stm8->eepromstart) && (address <= stm8->eepromend))
+               retval = stm8_write_flash(target, EEPROM, address, size, count,
+                               stm8->blocksize, buffer);
+       else if ((address >= stm8->optionstart) && (address <= stm8->optionend))
+               retval = stm8_write_flash(target, OPTION, address, size, count, 0, buffer);
+       else
+               retval = stm8_adapter_write_memory(target, address, size, count,
+                               buffer);
+
+       if (retval != ERROR_OK)
+               return ERROR_TARGET_FAILURE;
+
+       return retval;
+}
+
+static int stm8_read_memory(struct target *target, target_addr_t address,
+               uint32_t size, uint32_t count, uint8_t *buffer)
+{
+       LOG_DEBUG("address: 0x%8.8" TARGET_PRIxADDR
+               ", size: 0x%8.8" PRIx32
+               ", count: 0x%8.8" PRIx32,
+               address, size, count);
+
+       if (target->state != TARGET_HALTED)
+               LOG_WARNING("target not halted");
+
+       int retval;
+       retval = stm8_adapter_read_memory(target, address, size, count, buffer);
+
+       if (retval != ERROR_OK)
+               return ERROR_TARGET_FAILURE;
+
+       return retval;
+}
+
+static int stm8_init(struct command_context *cmd_ctx, struct target *target)
+{
+       stm8_build_reg_cache(target);
+
+       return ERROR_OK;
+}
+
+static int stm8_poll(struct target *target)
+{
+       int retval = ERROR_OK;
+       uint8_t csr1, csr2;
+
+#ifdef LOG_STM8
+       LOG_DEBUG("target->state=%d", target->state);
+#endif
+
+       /* read dm_csrx control regs */
+       retval = stm8_read_dm_csrx(target, &csr1, &csr2);
+       if (retval != ERROR_OK) {
+               LOG_DEBUG("stm8_read_dm_csrx failed retval=%d", retval);
+               /*
+                  We return ERROR_OK here even if we didn't get an answer.
+                  openocd will call target_wait_state until we get target state TARGET_HALTED
+               */
+               return ERROR_OK;
+       }
+
+       /* check for processor halted */
+       if (csr2 & STALL) {
+               if (target->state != TARGET_HALTED) {
+                       if (target->state == TARGET_UNKNOWN)
+                               LOG_DEBUG("DM_CSR2_STALL already set during server startup.");
+
+                       retval = stm8_debug_entry(target);
+                       if (retval != ERROR_OK) {
+                               LOG_DEBUG("stm8_debug_entry failed retval=%d", retval);
+                               return ERROR_TARGET_FAILURE;
+                       }
+
+                       if (target->state == TARGET_DEBUG_RUNNING) {
+                               target->state = TARGET_HALTED;
+                               target_call_event_callbacks(target, TARGET_EVENT_DEBUG_HALTED);
+                       } else {
+                               target->state = TARGET_HALTED;
+                               target_call_event_callbacks(target, TARGET_EVENT_HALTED);
+                       }
+               }
+       } else
+               target->state = TARGET_RUNNING;
+#ifdef LOG_STM8
+       LOG_DEBUG("csr1 = 0x%02X csr2 = 0x%02X", csr1, csr2);
+#endif
+       return ERROR_OK;
+}
+
+static int stm8_halt(struct target *target)
+{
+       LOG_DEBUG("target->state: %s", target_state_name(target));
+
+       if (target->state == TARGET_HALTED) {
+               LOG_DEBUG("target was already halted");
+               return ERROR_OK;
+       }
+
+       if (target->state == TARGET_UNKNOWN)
+               LOG_WARNING("target was in unknown state when halt was requested");
+
+       if (target->state == TARGET_RESET) {
+               /* we came here in a reset_halt or reset_init sequence
+                * debug entry was already prepared in stm8_assert_reset()
+                */
+               target->debug_reason = DBG_REASON_DBGRQ;
+
+               return ERROR_OK;
+       }
+
+
+       /* break processor */
+       stm8_debug_stall(target);
+
+       target->debug_reason = DBG_REASON_DBGRQ;
+
+       return ERROR_OK;
+}
+
+static int stm8_reset_assert(struct target *target)
+{
+       int res = ERROR_OK;
+       struct hl_interface_s *adapter = target_to_adapter(target);
+       struct stm8_common *stm8 = target_to_stm8(target);
+       bool use_srst_fallback = true;
+
+       enum reset_types jtag_reset_config = jtag_get_reset_config();
+
+       if (jtag_reset_config & RESET_HAS_SRST) {
+               jtag_add_reset(0, 1);
+               res = adapter->layout->api->assert_srst(adapter->handle, 0);
+
+               if (res == ERROR_OK)
+                       /* hardware srst supported */
+                       use_srst_fallback = false;
+               else if (res != ERROR_COMMAND_NOTFOUND)
+                       /* some other failure */
+                       return res;
+       }
+
+       if (use_srst_fallback) {
+               LOG_DEBUG("Hardware srst not supported, falling back to swim reset");
+               res = adapter->layout->api->reset(adapter->handle);
+               if (res != ERROR_OK)
+                       return res;
+       }
+
+       /* registers are now invalid */
+       register_cache_invalidate(stm8->core_cache);
+
+       target->state = TARGET_RESET;
+       target->debug_reason = DBG_REASON_NOTHALTED;
+
+       if (target->reset_halt) {
+               res = target_halt(target);
+               if (res != ERROR_OK)
+                       return res;
+       }
+
+       return ERROR_OK;
+}
+
+static int stm8_reset_deassert(struct target *target)
+{
+       int res;
+       struct hl_interface_s *adapter = target_to_adapter(target);
+
+       enum reset_types jtag_reset_config = jtag_get_reset_config();
+
+       if (jtag_reset_config & RESET_HAS_SRST) {
+               res = adapter->layout->api->assert_srst(adapter->handle, 1);
+               if ((res != ERROR_OK) && (res != ERROR_COMMAND_NOTFOUND))
+                       return res;
+       }
+
+       /* virtual deassert reset, we need it for the internal
+        * jtag state machine
+        */
+       jtag_add_reset(0, 0);
+
+       /* The cpu should now be stalled. If halt was requested
+          let poll detect the stall */
+       if (target->reset_halt)
+               return ERROR_OK;
+
+       /* Instead of going thrugh saving context, polling and
+          then resuming target again just clear stall and proceed. */
+       target->state = TARGET_RUNNING;
+       return stm8_exit_debug(target);
+}
+
+/* stm8_single_step_core() is only used for stepping over breakpoints
+   from stm8_resume() */
+static int stm8_single_step_core(struct target *target)
+{
+       struct stm8_common *stm8 = target_to_stm8(target);
+
+       /* configure single step mode */
+       stm8_config_step(target, 1);
+
+       /* disable interrupts while stepping */
+       if (!stm8->enable_step_irq)
+               stm8_enable_interrupts(target, 0);
+
+       /* exit debug mode */
+       stm8_exit_debug(target);
+
+       stm8_debug_entry(target);
+
+       return ERROR_OK;
+}
+
+static int stm8_resume(struct target *target, int current,
+               target_addr_t address, int handle_breakpoints,
+               int debug_execution)
+{
+       struct stm8_common *stm8 = target_to_stm8(target);
+       struct breakpoint *breakpoint = NULL;
+       uint32_t resume_pc;
+
+       LOG_DEBUG("%d " TARGET_ADDR_FMT " %d %d", current, address,
+                       handle_breakpoints, debug_execution);
+
+       if (target->state != TARGET_HALTED) {
+               LOG_WARNING("target not halted");
+               return ERROR_TARGET_NOT_HALTED;
+       }
+
+       if (!debug_execution) {
+               target_free_all_working_areas(target);
+               stm8_enable_breakpoints(target);
+               stm8_enable_watchpoints(target);
+               struct stm8_comparator *comparator_list = stm8->hw_break_list;
+               stm8_set_hwbreak(target, comparator_list);
+       }
+
+       /* current = 1: continue on current pc,
+          otherwise continue at <address> */
+       if (!current) {
+               buf_set_u32(stm8->core_cache->reg_list[STM8_PC].value,
+                       0, 32, address);
+               stm8->core_cache->reg_list[STM8_PC].dirty = true;
+               stm8->core_cache->reg_list[STM8_PC].valid = true;
+       }
+
+       if (!current)
+               resume_pc = address;
+       else
+               resume_pc = buf_get_u32(
+                       stm8->core_cache->reg_list[STM8_PC].value,
+                       0, 32);
+
+       stm8_restore_context(target);
+
+       /* the front-end may request us not to handle breakpoints */
+       if (handle_breakpoints) {
+               /* Single step past breakpoint at current address */
+               breakpoint = breakpoint_find(target, resume_pc);
+               if (breakpoint) {
+                       LOG_DEBUG("unset breakpoint at " TARGET_ADDR_FMT,
+                                       breakpoint->address);
+                       stm8_unset_breakpoint(target, breakpoint);
+                       stm8_single_step_core(target);
+                       stm8_set_breakpoint(target, breakpoint);
+               }
+       }
+
+       /* disable interrupts if we are debugging */
+       if (debug_execution)
+               stm8_enable_interrupts(target, 0);
+
+       /* exit debug mode */
+       stm8_exit_debug(target);
+       target->debug_reason = DBG_REASON_NOTHALTED;
+
+       /* registers are now invalid */
+       register_cache_invalidate(stm8->core_cache);
+
+       if (!debug_execution) {
+               target->state = TARGET_RUNNING;
+               target_call_event_callbacks(target, TARGET_EVENT_RESUMED);
+               LOG_DEBUG("target resumed at 0x%" PRIx32 "", resume_pc);
+       } else {
+               target->state = TARGET_DEBUG_RUNNING;
+               target_call_event_callbacks(target, TARGET_EVENT_DEBUG_RESUMED);
+               LOG_DEBUG("target debug resumed at 0x%" PRIx32 "", resume_pc);
+       }
+
+       return ERROR_OK;
+}
+
+static int stm8_init_flash_regs(bool enable_stm8l, struct stm8_common *stm8)
+{
+       stm8->enable_stm8l = enable_stm8l;
+
+       if (stm8->enable_stm8l) {
+               stm8->flash_cr2 = FLASH_CR2_STM8L;
+               stm8->flash_ncr2 = FLASH_NCR2_STM8L;
+               stm8->flash_iapsr = FLASH_IAPSR_STM8L;
+               stm8->flash_dukr = FLASH_DUKR_STM8L;
+               stm8->flash_pukr = FLASH_PUKR_STM8L;
+       } else {
+               stm8->flash_cr2 = FLASH_CR2_STM8S;
+               stm8->flash_ncr2 = FLASH_NCR2_STM8S;
+               stm8->flash_iapsr = FLASH_IAPSR_STM8S;
+               stm8->flash_dukr = FLASH_DUKR_STM8S;
+               stm8->flash_pukr = FLASH_PUKR_STM8S;
+       }
+       return ERROR_OK;
+}
+
+static int stm8_init_arch_info(struct target *target,
+               struct stm8_common *stm8, struct jtag_tap *tap)
+{
+       target->endianness = TARGET_BIG_ENDIAN;
+       target->arch_info = stm8;
+       stm8->common_magic = STM8_COMMON_MAGIC;
+       stm8->fast_data_area = NULL;
+       stm8->blocksize = 0x80;
+       stm8->flashstart = 0x8000;
+       stm8->flashend = 0xffff;
+       stm8->eepromstart = 0x4000;
+       stm8->eepromend = 0x43ff;
+       stm8->optionstart = 0x4800;
+       stm8->optionend = 0x487F;
+
+       /* has breakpoint/watchpoint unit been scanned */
+       stm8->bp_scanned = false;
+       stm8->hw_break_list = NULL;
+
+       stm8->read_core_reg = stm8_read_core_reg;
+       stm8->write_core_reg = stm8_write_core_reg;
+
+       stm8_init_flash_regs(0, stm8);
+
+       return ERROR_OK;
+}
+
+static int stm8_target_create(struct target *target,
+               Jim_Interp *interp)
+{
+
+       struct stm8_common *stm8 = calloc(1, sizeof(struct stm8_common));
+
+       stm8_init_arch_info(target, stm8, target->tap);
+       stm8_configure_break_unit(target);
+
+       return ERROR_OK;
+}
+
+static int stm8_read_core_reg(struct target *target, unsigned int num)
+{
+       uint32_t reg_value;
+
+       /* get pointers to arch-specific information */
+       struct stm8_common *stm8 = target_to_stm8(target);
+
+       if (num >= STM8_NUM_REGS)
+               return ERROR_COMMAND_SYNTAX_ERROR;
+
+       reg_value = stm8->core_regs[num];
+       LOG_DEBUG("read core reg %i value 0x%" PRIx32 "", num , reg_value);
+       buf_set_u32(stm8->core_cache->reg_list[num].value, 0, 32, reg_value);
+       stm8->core_cache->reg_list[num].valid = true;
+       stm8->core_cache->reg_list[num].dirty = false;
+
+       return ERROR_OK;
+}
+
+static int stm8_write_core_reg(struct target *target, unsigned int num)
+{
+       uint32_t reg_value;
+
+       /* get pointers to arch-specific information */
+       struct stm8_common *stm8 = target_to_stm8(target);
+
+       if (num >= STM8_NUM_REGS)
+               return ERROR_COMMAND_SYNTAX_ERROR;
+
+       reg_value = buf_get_u32(stm8->core_cache->reg_list[num].value, 0, 32);
+       stm8->core_regs[num] = reg_value;
+       LOG_DEBUG("write core reg %i value 0x%" PRIx32 "", num , reg_value);
+       stm8->core_cache->reg_list[num].valid = true;
+       stm8->core_cache->reg_list[num].dirty = false;
+
+       return ERROR_OK;
+}
+
+static int stm8_get_gdb_reg_list(struct target *target, struct reg **reg_list[],
+               int *reg_list_size, enum target_register_class reg_class)
+{
+       /* get pointers to arch-specific information */
+       struct stm8_common *stm8 = target_to_stm8(target);
+       unsigned int i;
+
+       *reg_list_size = STM8_NUM_REGS;
+       *reg_list = malloc(sizeof(struct reg *) * (*reg_list_size));
+
+       for (i = 0; i < STM8_NUM_REGS; i++)
+               (*reg_list)[i] = &stm8->core_cache->reg_list[i];
+
+       return ERROR_OK;
+}
+
+static const struct reg_arch_type stm8_reg_type = {
+       .get = stm8_get_core_reg,
+       .set = stm8_set_core_reg,
+};
+
+static struct reg_cache *stm8_build_reg_cache(struct target *target)
+{
+       /* get pointers to arch-specific information */
+       struct stm8_common *stm8 = target_to_stm8(target);
+
+       int num_regs = STM8_NUM_REGS;
+       struct reg_cache **cache_p = register_get_last_cache_p(&target->reg_cache);
+       struct reg_cache *cache = malloc(sizeof(struct reg_cache));
+       struct reg *reg_list = calloc(num_regs, sizeof(struct reg));
+       struct stm8_core_reg *arch_info = malloc(
+                       sizeof(struct stm8_core_reg) * num_regs);
+       struct reg_feature *feature;
+       int i;
+
+       /* Build the process context cache */
+       cache->name = "stm8 registers";
+       cache->next = NULL;
+       cache->reg_list = reg_list;
+       cache->num_regs = num_regs;
+       (*cache_p) = cache;
+       stm8->core_cache = cache;
+
+       for (i = 0; i < num_regs; i++) {
+               arch_info[i].num = stm8_regs[i].id;
+               arch_info[i].target = target;
+               arch_info[i].stm8_common = stm8;
+
+               reg_list[i].name = stm8_regs[i].name;
+               reg_list[i].size = stm8_regs[i].bits;
+
+               reg_list[i].value = calloc(1, 4);
+               reg_list[i].valid = false;
+               reg_list[i].type = &stm8_reg_type;
+               reg_list[i].arch_info = &arch_info[i];
+
+               reg_list[i].reg_data_type = calloc(1, sizeof(struct reg_data_type));
+               if (reg_list[i].reg_data_type)
+                       reg_list[i].reg_data_type->type = stm8_regs[i].type;
+               else {
+                       LOG_ERROR("unable to allocate reg type list");
+                       return NULL;
+               }
+
+               reg_list[i].dirty = false;
+               reg_list[i].group = stm8_regs[i].group;
+               reg_list[i].number = stm8_regs[i].id;
+               reg_list[i].exist = true;
+               reg_list[i].caller_save = true; /* gdb defaults to true */
+
+               feature = calloc(1, sizeof(struct reg_feature));
+               if (feature) {
+                       feature->name = stm8_regs[i].feature;
+                       reg_list[i].feature = feature;
+               } else
+                       LOG_ERROR("unable to allocate feature list");
+       }
+
+       return cache;
+}
+
+static void stm8_free_reg_cache(struct target *target)
+{
+       struct stm8_common *stm8 = target_to_stm8(target);
+       struct reg_cache *cache;
+       struct reg *reg;
+       unsigned int i;
+
+       cache = stm8->core_cache;
+
+       if (!cache)
+               return;
+
+       for (i = 0; i < cache->num_regs; i++) {
+               reg = &cache->reg_list[i];
+
+               free(reg->feature);
+               free(reg->reg_data_type);
+               free(reg->value);
+       }
+
+       free(cache->reg_list[0].arch_info);
+       free(cache->reg_list);
+       free(cache);
+
+       stm8->core_cache = NULL;
+}
+
+static void stm8_deinit(struct target *target)
+{
+       struct stm8_common *stm8 = target_to_stm8(target);
+
+       free(stm8->hw_break_list);
+
+       stm8_free_reg_cache(target);
+
+       free(stm8);
+}
+
+static int stm8_arch_state(struct target *target)
+{
+       struct stm8_common *stm8 = target_to_stm8(target);
+
+       LOG_USER("target halted due to %s, pc: 0x%8.8" PRIx32 "",
+               debug_reason_name(target),
+               buf_get_u32(stm8->core_cache->reg_list[STM8_PC].value, 0, 32));
+
+       return ERROR_OK;
+}
+
+static int stm8_step(struct target *target, int current,
+               target_addr_t address, int handle_breakpoints)
+{
+       LOG_DEBUG("%" PRIx32 " " TARGET_ADDR_FMT " %" PRIx32,
+               current, address, handle_breakpoints);
+
+       /* get pointers to arch-specific information */
+       struct stm8_common *stm8 = target_to_stm8(target);
+       struct breakpoint *breakpoint = NULL;
+
+       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) {
+               buf_set_u32(stm8->core_cache->reg_list[STM8_PC].value, 0, 32, address);
+               stm8->core_cache->reg_list[STM8_PC].dirty = true;
+               stm8->core_cache->reg_list[STM8_PC].valid = true;
+       }
+
+       /* the front-end may request us not to handle breakpoints */
+       if (handle_breakpoints) {
+               breakpoint = breakpoint_find(target,
+                               buf_get_u32(stm8->core_cache->reg_list[STM8_PC].value, 0, 32));
+               if (breakpoint)
+                       stm8_unset_breakpoint(target, breakpoint);
+       }
+
+       /* restore context */
+       stm8_restore_context(target);
+
+       /* configure single step mode */
+       stm8_config_step(target, 1);
+
+       target->debug_reason = DBG_REASON_SINGLESTEP;
+
+       target_call_event_callbacks(target, TARGET_EVENT_RESUMED);
+
+       /* disable interrupts while stepping */
+       if (!stm8->enable_step_irq)
+               stm8_enable_interrupts(target, 0);
+
+       /* exit debug mode */
+       stm8_exit_debug(target);
+
+       /* registers are now invalid */
+       register_cache_invalidate(stm8->core_cache);
+
+       LOG_DEBUG("target stepped ");
+       stm8_debug_entry(target);
+
+       if (breakpoint)
+               stm8_set_breakpoint(target, breakpoint);
+
+       target_call_event_callbacks(target, TARGET_EVENT_HALTED);
+
+       return ERROR_OK;
+}
+
+static void stm8_enable_breakpoints(struct target *target)
+{
+       struct breakpoint *breakpoint = target->breakpoints;
+
+       /* set any pending breakpoints */
+       while (breakpoint) {
+               if (breakpoint->set == 0)
+                       stm8_set_breakpoint(target, breakpoint);
+               breakpoint = breakpoint->next;
+       }
+}
+
+static int stm8_set_breakpoint(struct target *target,
+               struct breakpoint *breakpoint)
+{
+       struct stm8_common *stm8 = target_to_stm8(target);
+       struct stm8_comparator *comparator_list = stm8->hw_break_list;
+       int retval;
+
+       if (breakpoint->set) {
+               LOG_WARNING("breakpoint already set");
+               return ERROR_OK;
+       }
+
+       if (breakpoint->type == BKPT_HARD) {
+               int bp_num = 0;
+
+               while (comparator_list[bp_num].used && (bp_num < stm8->num_hw_bpoints))
+                       bp_num++;
+               if (bp_num >= stm8->num_hw_bpoints) {
+                       LOG_ERROR("Can not find free breakpoint register (bpid: %" PRIu32 ")",
+                                       breakpoint->unique_id);
+                       return ERROR_TARGET_RESOURCE_NOT_AVAILABLE;
+               }
+               breakpoint->set = bp_num + 1;
+               comparator_list[bp_num].used = true;
+               comparator_list[bp_num].bp_value = breakpoint->address;
+               comparator_list[bp_num].type = HWBRK_EXEC;
+
+               retval = stm8_set_hwbreak(target, comparator_list);
+               if (retval != ERROR_OK)
+                       return retval;
+
+               LOG_DEBUG("bpid: %" PRIu32 ", bp_num %i bp_value 0x%" PRIx32 "",
+                                 breakpoint->unique_id,
+                                 bp_num, comparator_list[bp_num].bp_value);
+       } else if (breakpoint->type == BKPT_SOFT) {
+               LOG_DEBUG("bpid: %" PRIu32, breakpoint->unique_id);
+               if (breakpoint->length == 1) {
+                       uint8_t verify = 0x55;
+
+                       retval = target_read_u8(target, breakpoint->address,
+                                       breakpoint->orig_instr);
+                       if (retval != ERROR_OK)
+                               return retval;
+                       retval = target_write_u8(target, breakpoint->address, STM8_BREAK);
+                       if (retval != ERROR_OK)
+                               return retval;
+
+                       retval = target_read_u8(target, breakpoint->address, &verify);
+                       if (retval != ERROR_OK)
+                               return retval;
+                       if (verify != STM8_BREAK) {
+                               LOG_ERROR("Unable to set breakpoint at address " TARGET_ADDR_FMT
+                                               " - check that memory is read/writable",
+                                               breakpoint->address);
+                               return ERROR_TARGET_RESOURCE_NOT_AVAILABLE;
+                       }
+               } else {
+                       return ERROR_TARGET_RESOURCE_NOT_AVAILABLE;
+               }
+               breakpoint->set = 1; /* Any nice value but 0 */
+       }
+
+       return ERROR_OK;
+}
+
+static int stm8_add_breakpoint(struct target *target,
+               struct breakpoint *breakpoint)
+{
+       struct stm8_common *stm8 = target_to_stm8(target);
+       int ret;
+
+       if (breakpoint->type == BKPT_HARD) {
+               if (stm8->num_hw_bpoints_avail < 1) {
+                       LOG_INFO("no hardware breakpoint available");
+                       return ERROR_TARGET_RESOURCE_NOT_AVAILABLE;
+               }
+
+               ret = stm8_set_breakpoint(target, breakpoint);
+               if (ret != ERROR_OK)
+                       return ret;
+
+               stm8->num_hw_bpoints_avail--;
+               return ERROR_OK;
+       }
+
+       ret = stm8_set_breakpoint(target, breakpoint);
+       if (ret != ERROR_OK)
+               return ret;
+
+       return ERROR_OK;
+}
+
+static int stm8_unset_breakpoint(struct target *target,
+               struct breakpoint *breakpoint)
+{
+       /* get pointers to arch-specific information */
+       struct stm8_common *stm8 = target_to_stm8(target);
+       struct stm8_comparator *comparator_list = stm8->hw_break_list;
+       int retval;
+
+       if (!breakpoint->set) {
+               LOG_WARNING("breakpoint not set");
+               return ERROR_OK;
+       }
+
+       if (breakpoint->type == BKPT_HARD) {
+               int bp_num = breakpoint->set - 1;
+               if ((bp_num < 0) || (bp_num >= stm8->num_hw_bpoints)) {
+                       LOG_DEBUG("Invalid comparator number in breakpoint (bpid: %" PRIu32 ")",
+                                         breakpoint->unique_id);
+                       return ERROR_OK;
+               }
+               LOG_DEBUG("bpid: %" PRIu32 " - releasing hw: %d",
+                               breakpoint->unique_id,
+                               bp_num);
+               comparator_list[bp_num].used = false;
+               retval = stm8_set_hwbreak(target, comparator_list);
+               if (retval != ERROR_OK)
+                       return retval;
+       } else {
+               /* restore original instruction (kept in target endianness) */
+               LOG_DEBUG("bpid: %" PRIu32, breakpoint->unique_id);
+               if (breakpoint->length == 1) {
+                       uint8_t current_instr;
+
+                       /* check that user program has not
+                         modified breakpoint instruction */
+                       retval = target_read_memory(target, breakpoint->address, 1, 1,
+                                       (uint8_t *)&current_instr);
+                       if (retval != ERROR_OK)
+                               return retval;
+
+                       if (current_instr == STM8_BREAK) {
+                               retval = target_write_memory(target, breakpoint->address, 1, 1,
+                                               breakpoint->orig_instr);
+                               if (retval != ERROR_OK)
+                                       return retval;
+                       }
+               } else
+                       return ERROR_FAIL;
+       }
+       breakpoint->set = 0;
+
+       return ERROR_OK;
+}
+
+static int stm8_remove_breakpoint(struct target *target,
+               struct breakpoint *breakpoint)
+{
+       /* get pointers to arch-specific information */
+       struct stm8_common *stm8 = target_to_stm8(target);
+
+       if (target->state != TARGET_HALTED) {
+               LOG_WARNING("target not halted");
+               return ERROR_TARGET_NOT_HALTED;
+       }
+
+       if (breakpoint->set)
+               stm8_unset_breakpoint(target, breakpoint);
+
+       if (breakpoint->type == BKPT_HARD)
+               stm8->num_hw_bpoints_avail++;
+
+       return ERROR_OK;
+}
+
+static int stm8_set_watchpoint(struct target *target,
+               struct watchpoint *watchpoint)
+{
+       struct stm8_common *stm8 = target_to_stm8(target);
+       struct stm8_comparator *comparator_list = stm8->hw_break_list;
+       int wp_num = 0;
+       int ret;
+
+       if (watchpoint->set) {
+               LOG_WARNING("watchpoint already set");
+               return ERROR_OK;
+       }
+
+       while (comparator_list[wp_num].used && (wp_num < stm8->num_hw_bpoints))
+               wp_num++;
+       if (wp_num >= stm8->num_hw_bpoints) {
+               LOG_ERROR("Can not find free hw breakpoint");
+               return ERROR_TARGET_RESOURCE_NOT_AVAILABLE;
+       }
+
+       if (watchpoint->length != 1) {
+                       LOG_ERROR("Only watchpoints of length 1 are supported");
+                       return ERROR_TARGET_UNALIGNED_ACCESS;
+       }
+
+       enum hw_break_type enable = 0;
+
+       switch (watchpoint->rw) {
+               case WPT_READ:
+                       enable = HWBRK_RD;
+                       break;
+               case WPT_WRITE:
+                       enable = HWBRK_WR;
+                       break;
+               case WPT_ACCESS:
+                       enable = HWBRK_ACC;
+                       break;
+               default:
+                       LOG_ERROR("BUG: watchpoint->rw neither read, write nor access");
+       }
+
+       comparator_list[wp_num].used = true;
+       comparator_list[wp_num].bp_value = watchpoint->address;
+       comparator_list[wp_num].type = enable;
+
+       ret = stm8_set_hwbreak(target, comparator_list);
+       if (ret != ERROR_OK) {
+               comparator_list[wp_num].used = false;
+               return ret;
+       }
+
+       watchpoint->set = wp_num + 1;
+
+       LOG_DEBUG("wp_num %i bp_value 0x%" PRIx32 "",
+                       wp_num,
+                       comparator_list[wp_num].bp_value);
+
+       return ERROR_OK;
+}
+
+static int stm8_add_watchpoint(struct target *target,
+               struct watchpoint *watchpoint)
+{
+       int ret;
+       struct stm8_common *stm8 = target_to_stm8(target);
+
+       if (stm8->num_hw_bpoints_avail < 1) {
+               LOG_INFO("no hardware watchpoints available");
+               return ERROR_TARGET_RESOURCE_NOT_AVAILABLE;
+       }
+
+       ret = stm8_set_watchpoint(target, watchpoint);
+       if (ret != ERROR_OK)
+               return ret;
+
+       stm8->num_hw_bpoints_avail--;
+       return ERROR_OK;
+}
+
+static void stm8_enable_watchpoints(struct target *target)
+{
+       struct watchpoint *watchpoint = target->watchpoints;
+
+       /* set any pending watchpoints */
+       while (watchpoint) {
+               if (watchpoint->set == 0)
+                       stm8_set_watchpoint(target, watchpoint);
+               watchpoint = watchpoint->next;
+       }
+}
+
+static int stm8_unset_watchpoint(struct target *target,
+               struct watchpoint *watchpoint)
+{
+       /* get pointers to arch-specific information */
+       struct stm8_common *stm8 = target_to_stm8(target);
+       struct stm8_comparator *comparator_list = stm8->hw_break_list;
+
+       if (!watchpoint->set) {
+               LOG_WARNING("watchpoint not set");
+               return ERROR_OK;
+       }
+
+       int wp_num = watchpoint->set - 1;
+       if ((wp_num < 0) || (wp_num >= stm8->num_hw_bpoints)) {
+               LOG_DEBUG("Invalid hw comparator number in watchpoint");
+               return ERROR_OK;
+       }
+       comparator_list[wp_num].used = false;
+       watchpoint->set = 0;
+
+       stm8_set_hwbreak(target, comparator_list);
+
+       return ERROR_OK;
+}
+
+static int stm8_remove_watchpoint(struct target *target,
+               struct watchpoint *watchpoint)
+{
+       /* get pointers to arch-specific information */
+       struct stm8_common *stm8 = target_to_stm8(target);
+
+       if (target->state != TARGET_HALTED) {
+               LOG_WARNING("target not halted");
+               return ERROR_TARGET_NOT_HALTED;
+       }
+
+       if (watchpoint->set)
+               stm8_unset_watchpoint(target, watchpoint);
+
+       stm8->num_hw_bpoints_avail++;
+
+       return ERROR_OK;
+}
+
+static int stm8_examine(struct target *target)
+{
+       int retval;
+       uint8_t csr1, csr2;
+       /* get pointers to arch-specific information */
+       struct stm8_common *stm8 = target_to_stm8(target);
+       struct hl_interface_s *adapter = target_to_adapter(target);
+
+       if (!target_was_examined(target)) {
+               if (!stm8->swim_configured) {
+                       /* set SWIM_CSR = 0xa0 (enable mem access & mask reset) */
+                       LOG_DEBUG("writing A0 to SWIM_CSR (SAFE_MASK + SWIM_DM)");
+                       retval = stm8_write_u8(target, SWIM_CSR, SAFE_MASK + SWIM_DM);
+                       if (retval != ERROR_OK)
+                               return retval;
+                       /* set high speed */
+                       LOG_DEBUG("writing B0 to SWIM_CSR (SAFE_MASK + SWIM_DM + HS)");
+                       retval = stm8_write_u8(target, SWIM_CSR, SAFE_MASK + SWIM_DM + HS);
+                       if (retval != ERROR_OK)
+                               return retval;
+                       retval = stm8_set_speed(target, 1);
+                       if (retval == ERROR_OK)
+                               stm8->swim_configured = true;
+                       /*
+                               Now is the time to deassert reset if connect_under_reset.
+                               Releasing reset line will cause the option bytes to load.
+                               The core will still be stalled.
+                       */
+                       if (adapter->param.connect_under_reset)
+                               stm8_reset_deassert(target);
+               } else {
+                       LOG_INFO("trying to reconnect");
+
+                       retval = adapter->layout->api->state(adapter->handle);
+                       if (retval != ERROR_OK) {
+                               LOG_ERROR("reconnect failed");
+                               return ERROR_FAIL;
+                       }
+
+                       /* read dm_csrx control regs */
+                       retval = stm8_read_dm_csrx(target, &csr1, &csr2);
+                       if (retval != ERROR_OK) {
+                               LOG_ERROR("state query failed");
+                               return ERROR_FAIL;
+                       }
+               }
+
+               target_set_examined(target);
+
+               return ERROR_OK;
+       }
+
+       return ERROR_OK;
+}
+
+/** Checks whether a memory region is erased. */
+static int stm8_blank_check_memory(struct target *target,
+               target_addr_t address, uint32_t count, uint32_t *blank, uint8_t erased_value)
+{
+       struct working_area *erase_check_algorithm;
+       struct reg_param reg_params[2];
+       struct mem_param mem_params[2];
+       struct stm8_algorithm stm8_info;
+
+       static const uint8_t stm8_erase_check_code[] = {
+#include "../../contrib/loaders/erase_check/stm8_erase_check.inc"
+       };
+
+       if (erased_value != 0xff) {
+               LOG_ERROR("Erase value 0x%02" PRIx8 " not yet supported for STM8",
+                       erased_value);
+               return ERROR_FAIL;
+       }
+
+       /* make sure we have a working area */
+       if (target_alloc_working_area(target, sizeof(stm8_erase_check_code),
+                       &erase_check_algorithm) != ERROR_OK)
+               return ERROR_TARGET_RESOURCE_NOT_AVAILABLE;
+
+       target_write_buffer(target, erase_check_algorithm->address,
+                       sizeof(stm8_erase_check_code), stm8_erase_check_code);
+
+       stm8_info.common_magic = STM8_COMMON_MAGIC;
+
+       init_mem_param(&mem_params[0], 0x0, 3, PARAM_OUT);
+       buf_set_u32(mem_params[0].value, 0, 24, address);
+
+       init_mem_param(&mem_params[1], 0x3, 3, PARAM_OUT);
+       buf_set_u32(mem_params[1].value, 0, 24, count);
+
+       init_reg_param(&reg_params[0], "a", 32, PARAM_IN_OUT);
+       buf_set_u32(reg_params[0].value, 0, 32, erased_value);
+
+       init_reg_param(&reg_params[1], "sp", 32, PARAM_OUT);
+       buf_set_u32(reg_params[1].value, 0, 32, erase_check_algorithm->address);
+
+       int retval = target_run_algorithm(target, 2, mem_params, 2, reg_params,
+                       erase_check_algorithm->address + 6,
+                       erase_check_algorithm->address + (sizeof(stm8_erase_check_code) - 1),
+                       10000, &stm8_info);
+
+       if (retval == ERROR_OK)
+               *blank = (*(reg_params[0].value) == 0xff);
+
+       destroy_mem_param(&mem_params[0]);
+       destroy_mem_param(&mem_params[1]);
+       destroy_reg_param(&reg_params[0]);
+
+       target_free_working_area(target, erase_check_algorithm);
+
+       return retval;
+}
+
+static int stm8_checksum_memory(struct target *target, target_addr_t address,
+               uint32_t count, uint32_t *checksum)
+{
+       /* let image_calculate_checksum() take care of business */
+       return ERROR_TARGET_RESOURCE_NOT_AVAILABLE;
+}
+
+/* run to exit point. return error if exit point was not reached. */
+static int stm8_run_and_wait(struct target *target, uint32_t entry_point,
+               int timeout_ms, uint32_t exit_point, struct stm8_common *stm8)
+{
+       uint32_t pc;
+       int retval;
+       /* This code relies on the target specific resume() and
+          poll()->debug_entry() sequence to write register values to the
+          processor and the read them back */
+       retval = target_resume(target, 0, entry_point, 0, 1);
+       if (retval != ERROR_OK)
+               return retval;
+
+       retval = target_wait_state(target, TARGET_HALTED, timeout_ms);
+       /* If the target fails to halt due to the breakpoint, force a halt */
+       if (retval != ERROR_OK || target->state != TARGET_HALTED) {
+               retval = target_halt(target);
+               if (retval != ERROR_OK)
+                       return retval;
+               retval = target_wait_state(target, TARGET_HALTED, 500);
+               if (retval != ERROR_OK)
+                       return retval;
+               return ERROR_TARGET_TIMEOUT;
+       }
+
+       pc = buf_get_u32(stm8->core_cache->reg_list[STM8_PC].value, 0, 32);
+       if (exit_point && (pc != exit_point)) {
+               LOG_DEBUG("failed algorithm halted at 0x%" PRIx32 " ", pc);
+               return ERROR_TARGET_TIMEOUT;
+       }
+
+       return ERROR_OK;
+}
+
+static int stm8_run_algorithm(struct target *target, int num_mem_params,
+               struct mem_param *mem_params, int num_reg_params,
+               struct reg_param *reg_params, target_addr_t entry_point,
+               target_addr_t exit_point, int timeout_ms, void *arch_info)
+{
+       struct stm8_common *stm8 = target_to_stm8(target);
+
+       uint32_t context[STM8_NUM_REGS];
+       int retval = ERROR_OK;
+
+       LOG_DEBUG("Running algorithm");
+
+       /* NOTE: stm8_run_algorithm requires that each
+          algorithm uses a software breakpoint
+          at the exit point */
+
+       if (stm8->common_magic != STM8_COMMON_MAGIC) {
+               LOG_ERROR("current target isn't a STM8 target");
+               return ERROR_TARGET_INVALID;
+       }
+
+       if (target->state != TARGET_HALTED) {
+               LOG_WARNING("target not halted");
+               return ERROR_TARGET_NOT_HALTED;
+       }
+
+       /* refresh core register cache */
+       for (unsigned int i = 0; i < STM8_NUM_REGS; i++) {
+               if (!stm8->core_cache->reg_list[i].valid)
+                       stm8->read_core_reg(target, i);
+               context[i] = buf_get_u32(stm8->core_cache->reg_list[i].value, 0, 32);
+       }
+
+       for (int i = 0; i < num_mem_params; i++) {
+               retval = target_write_buffer(target, mem_params[i].address,
+                               mem_params[i].size, mem_params[i].value);
+               if (retval != ERROR_OK)
+                       return retval;
+       }
+
+       for (int i = 0; i < num_reg_params; i++) {
+               struct reg *reg = register_get_by_name(stm8->core_cache,
+                               reg_params[i].reg_name, 0);
+
+               if (!reg) {
+                       LOG_ERROR("BUG: register '%s' not found", reg_params[i].reg_name);
+                       return ERROR_COMMAND_SYNTAX_ERROR;
+               }
+
+               if (reg_params[i].size != 32) {
+                       LOG_ERROR("BUG: register '%s' size doesn't match reg_params[i].size",
+                                       reg_params[i].reg_name);
+                       return ERROR_COMMAND_SYNTAX_ERROR;
+               }
+
+               stm8_set_core_reg(reg, reg_params[i].value);
+       }
+
+       retval = stm8_run_and_wait(target, entry_point,
+                       timeout_ms, exit_point, stm8);
+
+       if (retval != ERROR_OK)
+               return retval;
+
+       for (int i = 0; i < num_mem_params; i++) {
+               if (mem_params[i].direction != PARAM_OUT) {
+                       retval = target_read_buffer(target, mem_params[i].address,
+                                       mem_params[i].size, mem_params[i].value);
+                       if (retval != ERROR_OK)
+                               return retval;
+               }
+       }
+
+       for (int i = 0; i < num_reg_params; i++) {
+               if (reg_params[i].direction != PARAM_OUT) {
+                       struct reg *reg = register_get_by_name(stm8->core_cache,
+                                       reg_params[i].reg_name, 0);
+                       if (!reg) {
+                               LOG_ERROR("BUG: register '%s' not found",
+                                               reg_params[i].reg_name);
+                               return ERROR_COMMAND_SYNTAX_ERROR;
+                       }
+
+                       if (reg_params[i].size != 32) {
+                               LOG_ERROR("BUG: register '%s' size doesn't match reg_params[i].size",
+                                               reg_params[i].reg_name);
+                               return ERROR_COMMAND_SYNTAX_ERROR;
+                       }
+
+                       buf_set_u32(reg_params[i].value,
+                                       0, 32, buf_get_u32(reg->value, 0, 32));
+               }
+       }
+
+       /* restore everything we saved before */
+       for (unsigned int i = 0; i < STM8_NUM_REGS; i++) {
+               uint32_t regvalue;
+               regvalue = buf_get_u32(stm8->core_cache->reg_list[i].value, 0, 32);
+               if (regvalue != context[i]) {
+                       LOG_DEBUG("restoring register %s with value 0x%8.8" PRIx32,
+                               stm8->core_cache->reg_list[i].name, context[i]);
+                       buf_set_u32(stm8->core_cache->reg_list[i].value,
+                                       0, 32, context[i]);
+                       stm8->core_cache->reg_list[i].valid = true;
+                       stm8->core_cache->reg_list[i].dirty = true;
+               }
+       }
+
+       return ERROR_OK;
+}
+
+int stm8_jim_configure(struct target *target, Jim_GetOptInfo *goi)
+{
+       struct stm8_common *stm8 = target_to_stm8(target);
+       jim_wide w;
+       int e;
+       const char *arg;
+
+       arg = Jim_GetString(goi->argv[0], NULL);
+       if (!strcmp(arg, "-blocksize")) {
+               e = Jim_GetOpt_String(goi, &arg, NULL);
+               if (e != JIM_OK)
+                       return e;
+
+               if (goi->argc == 0) {
+                       Jim_WrongNumArgs(goi->interp, goi->argc, goi->argv,
+                                       "-blocksize ?bytes? ...");
+                       return JIM_ERR;
+               }
+
+               e = Jim_GetOpt_Wide(goi, &w);
+               if (e != JIM_OK)
+                       return e;
+
+               stm8->blocksize = w;
+               LOG_DEBUG("blocksize=%8.8x", stm8->blocksize);
+               return JIM_OK;
+       }
+       if (!strcmp(arg, "-flashstart")) {
+               e = Jim_GetOpt_String(goi, &arg, NULL);
+               if (e != JIM_OK)
+                       return e;
+
+               if (goi->argc == 0) {
+                       Jim_WrongNumArgs(goi->interp, goi->argc, goi->argv,
+                                       "-flashstart ?address? ...");
+                       return JIM_ERR;
+               }
+
+               e = Jim_GetOpt_Wide(goi, &w);
+               if (e != JIM_OK)
+                       return e;
+
+               stm8->flashstart = w;
+               LOG_DEBUG("flashstart=%8.8x", stm8->flashstart);
+               return JIM_OK;
+       }
+       if (!strcmp(arg, "-flashend")) {
+               e = Jim_GetOpt_String(goi, &arg, NULL);
+               if (e != JIM_OK)
+                       return e;
+
+               if (goi->argc == 0) {
+                       Jim_WrongNumArgs(goi->interp, goi->argc, goi->argv,
+                                       "-flashend ?address? ...");
+                       return JIM_ERR;
+               }
+
+               e = Jim_GetOpt_Wide(goi, &w);
+               if (e != JIM_OK)
+                       return e;
+
+               stm8->flashend = w;
+               LOG_DEBUG("flashend=%8.8x", stm8->flashend);
+               return JIM_OK;
+       }
+       if (!strcmp(arg, "-eepromstart")) {
+               e = Jim_GetOpt_String(goi, &arg, NULL);
+               if (e != JIM_OK)
+                       return e;
+
+               if (goi->argc == 0) {
+                       Jim_WrongNumArgs(goi->interp, goi->argc, goi->argv,
+                                       "-eepromstart ?address? ...");
+                       return JIM_ERR;
+               }
+
+               e = Jim_GetOpt_Wide(goi, &w);
+               if (e != JIM_OK)
+                       return e;
+
+               stm8->eepromstart = w;
+               LOG_DEBUG("eepromstart=%8.8x", stm8->eepromstart);
+               return JIM_OK;
+       }
+       if (!strcmp(arg, "-eepromend")) {
+               e = Jim_GetOpt_String(goi, &arg, NULL);
+               if (e != JIM_OK)
+                       return e;
+
+               if (goi->argc == 0) {
+                       Jim_WrongNumArgs(goi->interp, goi->argc, goi->argv,
+                                       "-eepromend ?address? ...");
+                       return JIM_ERR;
+               }
+
+               e = Jim_GetOpt_Wide(goi, &w);
+               if (e != JIM_OK)
+                       return e;
+
+               stm8->eepromend = w;
+               LOG_DEBUG("eepromend=%8.8x", stm8->eepromend);
+               return JIM_OK;
+       }
+       if (!strcmp(arg, "-optionstart")) {
+               e = Jim_GetOpt_String(goi, &arg, NULL);
+               if (e != JIM_OK)
+                       return e;
+
+               if (goi->argc == 0) {
+                       Jim_WrongNumArgs(goi->interp, goi->argc, goi->argv,
+                                       "-optionstart ?address? ...");
+                       return JIM_ERR;
+               }
+
+               e = Jim_GetOpt_Wide(goi, &w);
+               if (e != JIM_OK)
+                       return e;
+
+               stm8->optionstart = w;
+               LOG_DEBUG("optionstart=%8.8x", stm8->optionstart);
+               return JIM_OK;
+       }
+       if (!strcmp(arg, "-optionend")) {
+               e = Jim_GetOpt_String(goi, &arg, NULL);
+               if (e != JIM_OK)
+                       return e;
+
+               if (goi->argc == 0) {
+                       Jim_WrongNumArgs(goi->interp, goi->argc, goi->argv,
+                                       "-optionend ?address? ...");
+                       return JIM_ERR;
+               }
+
+               e = Jim_GetOpt_Wide(goi, &w);
+               if (e != JIM_OK)
+                       return e;
+
+               stm8->optionend = w;
+               LOG_DEBUG("optionend=%8.8x", stm8->optionend);
+               return JIM_OK;
+       }
+       if (!strcmp(arg, "-enable_step_irq")) {
+               e = Jim_GetOpt_String(goi, &arg, NULL);
+               if (e != JIM_OK)
+                       return e;
+
+               stm8->enable_step_irq = true;
+               LOG_DEBUG("enable_step_irq=%8.8x", stm8->enable_step_irq);
+               return JIM_OK;
+       }
+       if (!strcmp(arg, "-enable_stm8l")) {
+               e = Jim_GetOpt_String(goi, &arg, NULL);
+               if (e != JIM_OK)
+                       return e;
+
+               stm8->enable_stm8l = true;
+               LOG_DEBUG("enable_stm8l=%8.8x", stm8->enable_stm8l);
+               stm8_init_flash_regs(stm8->enable_stm8l, stm8);
+               return JIM_OK;
+       }
+       return JIM_CONTINUE;
+}
+
+COMMAND_HANDLER(stm8_handle_enable_step_irq_command)
+{
+       const char *msg;
+       struct target *target = get_current_target(CMD_CTX);
+       struct stm8_common *stm8 = target_to_stm8(target);
+       bool enable = stm8->enable_step_irq;
+
+       if (CMD_ARGC > 0) {
+               COMMAND_PARSE_ENABLE(CMD_ARGV[0], enable);
+               stm8->enable_step_irq = enable;
+       }
+       msg = stm8->enable_step_irq ? "enabled" : "disabled";
+       command_print(CMD_CTX, "enable_step_irq = %s", msg);
+       return ERROR_OK;
+}
+
+COMMAND_HANDLER(stm8_handle_enable_stm8l_command)
+{
+       const char *msg;
+       struct target *target = get_current_target(CMD_CTX);
+       struct stm8_common *stm8 = target_to_stm8(target);
+       bool enable = stm8->enable_stm8l;
+
+       if (CMD_ARGC > 0) {
+               COMMAND_PARSE_ENABLE(CMD_ARGV[0], enable);
+               stm8->enable_stm8l = enable;
+       }
+       msg = stm8->enable_stm8l ? "enabled" : "disabled";
+       command_print(CMD_CTX, "enable_stm8l = %s", msg);
+       stm8_init_flash_regs(stm8->enable_stm8l, stm8);
+       return ERROR_OK;
+}
+
+static const struct command_registration stm8_exec_command_handlers[] = {
+       {
+               .name = "enable_step_irq",
+               .handler = stm8_handle_enable_step_irq_command,
+               .mode = COMMAND_ANY,
+               .help = "Enable/disable irq handling during step",
+               .usage = "[1/0]",
+       },
+       {
+               .name = "enable_stm8l",
+               .handler = stm8_handle_enable_stm8l_command,
+               .mode = COMMAND_ANY,
+               .help = "Enable/disable STM8L flash programming",
+               .usage = "[1/0]",
+       },
+       COMMAND_REGISTRATION_DONE
+};
+
+const struct command_registration stm8_command_handlers[] = {
+       {
+               .name = "stm8",
+               .mode = COMMAND_ANY,
+               .help = "stm8 command group",
+               .usage = "",
+               .chain = stm8_exec_command_handlers,
+       },
+       COMMAND_REGISTRATION_DONE
+};
+
+struct target_type stm8_target = {
+       .name = "stm8",
+
+       .poll = stm8_poll,
+       .arch_state = stm8_arch_state,
+
+       .halt = stm8_halt,
+       .resume = stm8_resume,
+       .step = stm8_step,
+
+       .assert_reset = stm8_reset_assert,
+       .deassert_reset = stm8_reset_deassert,
+
+       .get_gdb_reg_list = stm8_get_gdb_reg_list,
+
+       .read_memory = stm8_read_memory,
+       .write_memory = stm8_write_memory,
+       .checksum_memory = stm8_checksum_memory,
+       .blank_check_memory = stm8_blank_check_memory,
+
+       .run_algorithm = stm8_run_algorithm,
+
+       .add_breakpoint = stm8_add_breakpoint,
+       .remove_breakpoint = stm8_remove_breakpoint,
+       .add_watchpoint = stm8_add_watchpoint,
+       .remove_watchpoint = stm8_remove_watchpoint,
+
+       .commands = stm8_command_handlers,
+       .target_create = stm8_target_create,
+       .init_target = stm8_init,
+       .examine = stm8_examine,
+
+       .deinit_target = stm8_deinit,
+       .target_jim_configure = stm8_jim_configure,
+};
diff --git a/src/target/stm8.h b/src/target/stm8.h
new file mode 100644 (file)
index 0000000..39fac3e
--- /dev/null
@@ -0,0 +1,75 @@
+/*
+    OpenOCD STM8 target driver
+    Copyright (C) 2017  Ake Rehnman
+    ake.rehnman(at)gmail.com
+
+    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 3 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/>.
+*/
+
+#ifndef OPENOCD_TARGET_STM8_H
+#define OPENOCD_TARGET_STM8_H
+
+struct target;
+
+#define STM8_COMMON_MAGIC      0x53544D38
+#define STM8_NUM_CORE_REGS 6
+
+struct stm8_common {
+       uint32_t common_magic;
+       void *arch_info;
+       struct reg_cache *core_cache;
+       uint32_t core_regs[STM8_NUM_CORE_REGS];
+
+       /* working area for fastdata access */
+       struct working_area *fast_data_area;
+
+       bool swim_configured;
+       bool bp_scanned;
+       uint8_t num_hw_bpoints;
+       uint8_t num_hw_bpoints_avail;
+       struct stm8_comparator *hw_break_list;
+       uint32_t blocksize;
+       uint32_t flashstart;
+       uint32_t flashend;
+       uint32_t eepromstart;
+       uint32_t eepromend;
+       uint32_t optionstart;
+       uint32_t optionend;
+       bool enable_step_irq;
+
+       bool enable_stm8l;
+       uint32_t flash_cr2;
+       uint32_t flash_ncr2;
+       uint32_t flash_iapsr;
+       uint32_t flash_dukr;
+       uint32_t flash_pukr;
+
+       /* cc value used for interrupt flags restore */
+       uint32_t cc;
+       bool cc_valid;
+
+       /* register cache to processor synchronization */
+       int (*read_core_reg)(struct target *target, unsigned int num);
+       int (*write_core_reg)(struct target *target, unsigned int num);
+};
+
+static inline struct stm8_common *
+target_to_stm8(struct target *target)
+{
+       return target->arch_info;
+}
+
+const struct command_registration stm8_command_handlers[];
+
+#endif /* OPENOCD_TARGET_STM8_H */
index 36318d88e423887c7d1a2249b5906b80b77b7117..dcb6725065ee766d57bbbd62e5c4e440f6c49ef8 100644 (file)
@@ -105,6 +105,7 @@ extern struct target_type nds32_v3m_target;
 extern struct target_type or1k_target;
 extern struct target_type quark_x10xx_target;
 extern struct target_type quark_d20xx_target;
+extern struct target_type stm8_target;
 
 static struct target_type *target_types[] = {
        &arm7tdmi_target,
@@ -136,6 +137,7 @@ static struct target_type *target_types[] = {
        &or1k_target,
        &quark_x10xx_target,
        &quark_d20xx_target,
+       &stm8_target,
 #if BUILD_TARGET64
        &aarch64_target,
 #endif
diff --git a/tcl/target/stm8l.cfg b/tcl/target/stm8l.cfg
new file mode 100644 (file)
index 0000000..5cc99e1
--- /dev/null
@@ -0,0 +1,87 @@
+# script for stm8l family
+
+#
+# stm8 devices support SWIM transports only.
+#
+
+transport select stlink_swim
+
+if { [info exists CHIPNAME] } {
+   set _CHIPNAME $CHIPNAME
+} else {
+   set _CHIPNAME stm8l
+}
+
+# Work-area is a space in RAM used for flash programming
+# By default use 1kB
+if { [info exists WORKAREASIZE] } {
+   set _WORKAREASIZE $WORKAREASIZE
+} else {
+   set _WORKAREASIZE 0x400
+}
+
+if { [info exists FLASHSTART] } {
+   set _FLASHSTART $FLASHSTART
+} else {
+   set _FLASHSTART 0x8000
+}
+
+if { [info exists FLASHEND] } {
+   set _FLASHEND $FLASHEND
+} else {
+   set _FLASHEND 0xffff
+}
+
+if { [info exists EEPROMSTART] } {
+   set _EEPROMSTART $EEPROMSTART
+} else {
+   set _EEPROMSTART 0x4000
+}
+
+if { [info exists EEPROMEND] } {
+   set _EEPROMEND $EEPROMEND
+} else {
+   set _EEPROMEND 0x43ff
+}
+
+if { [info exists OPTIONSTART] } {
+   set _OPTIONSTART $OPTIONSTART
+} else {
+   set _OPTIONSTART 0x4800
+}
+
+if { [info exists OPTIONEND] } {
+   set _OPTIONEND $OPTIONEND
+} else {
+   set _OPTIONEND 0x487f
+}
+
+if { [info exists BLOCKSIZE] } {
+   set _BLOCKSIZE $BLOCKSIZE
+} else {
+   set _BLOCKSIZE 0x80
+}
+
+hla newtap $_CHIPNAME cpu -irlen 4 -ircapture 0x1 -irmask 0xf -expected-id 0
+
+set _TARGETNAME $_CHIPNAME.cpu
+
+target create $_TARGETNAME stm8 -chain-position $_CHIPNAME.cpu
+
+$_TARGETNAME configure -work-area-phys 0x0 -work-area-size $_WORKAREASIZE -work-area-backup 1
+$_TARGETNAME configure -flashstart $_FLASHSTART -flashend $_FLASHEND -eepromstart $_EEPROMSTART -eepromend $_EEPROMEND
+$_TARGETNAME configure -optionstart $_OPTIONSTART -optionend $_OPTIONEND -blocksize $_BLOCKSIZE
+
+# Uncomment this line to enable interrupts while instruction step
+#$_TARGETNAME configure -enable_step_irq
+
+# Set stm8l type
+$_TARGETNAME configure -enable_stm8l
+
+# The khz rate does not apply here, only slow <0> and fast <1>
+adapter_khz 1
+
+reset_config srst_only
+
+#uncomment this line to connect under reset
+#reset_config srst_nogate connect_assert_srst
diff --git a/tcl/target/stm8s.cfg b/tcl/target/stm8s.cfg
new file mode 100644 (file)
index 0000000..d55e61b
--- /dev/null
@@ -0,0 +1,84 @@
+# script for stm8s family
+
+#
+# stm8 devices support SWIM transports only.
+#
+
+transport select stlink_swim
+
+if { [info exists CHIPNAME] } {
+   set _CHIPNAME $CHIPNAME
+} else {
+   set _CHIPNAME stm8s
+}
+
+# Work-area is a space in RAM used for flash programming
+# By default use 1kB
+if { [info exists WORKAREASIZE] } {
+   set _WORKAREASIZE $WORKAREASIZE
+} else {
+   set _WORKAREASIZE 0x400
+}
+
+if { [info exists FLASHSTART] } {
+   set _FLASHSTART $FLASHSTART
+} else {
+   set _FLASHSTART 0x8000
+}
+
+if { [info exists FLASHEND] } {
+   set _FLASHEND $FLASHEND
+} else {
+   set _FLASHEND 0xffff
+}
+
+if { [info exists EEPROMSTART] } {
+   set _EEPROMSTART $EEPROMSTART
+} else {
+   set _EEPROMSTART 0x4000
+}
+
+if { [info exists EEPROMEND] } {
+   set _EEPROMEND $EEPROMEND
+} else {
+   set _EEPROMEND 0x43ff
+}
+
+if { [info exists OPTIONSTART] } {
+   set _OPTIONSTART $OPTIONSTART
+} else {
+   set _OPTIONSTART 0x4800
+}
+
+if { [info exists OPTIONEND] } {
+   set _OPTIONEND $OPTIONEND
+} else {
+   set _OPTIONEND 0x487f
+}
+
+if { [info exists BLOCKSIZE] } {
+   set _BLOCKSIZE $BLOCKSIZE
+} else {
+   set _BLOCKSIZE 0x80
+}
+
+hla newtap $_CHIPNAME cpu -irlen 4 -ircapture 0x1 -irmask 0xf -expected-id 0
+
+set _TARGETNAME $_CHIPNAME.cpu
+
+target create $_TARGETNAME stm8 -chain-position $_CHIPNAME.cpu
+
+$_TARGETNAME configure -work-area-phys 0x0 -work-area-size $_WORKAREASIZE -work-area-backup 1
+$_TARGETNAME configure -flashstart $_FLASHSTART -flashend $_FLASHEND -eepromstart $_EEPROMSTART -eepromend $_EEPROMEND
+$_TARGETNAME configure -optionstart $_OPTIONSTART -optionend $_OPTIONEND -blocksize $_BLOCKSIZE
+
+# Uncomment this line to enable interrupts while instruction step
+#$_TARGETNAME configure -enable_step_irq
+
+# The khz rate does not apply here, only slow <0> and fast <1>
+adapter_khz 1
+
+reset_config srst_only
+
+# uncomment this line to connect under reset
+#reset_config srst_nogate connect_assert_srst

Linking to existing account procedure

If you already have an account and want to add another login method you MUST first sign in with your existing account and then change URL to read https://review.openocd.org/login/?link to get to this page again but this time it'll work for linking. Thank you.

SSH host keys fingerprints

1024 SHA256:YKx8b7u5ZWdcbp7/4AeXNaqElP49m6QrwfXaqQGJAOk gerrit-code-review@openocd.zylin.com (DSA)
384 SHA256:jHIbSQa4REvwCFG4cq5LBlBLxmxSqelQPem/EXIrxjk gerrit-code-review@openocd.org (ECDSA)
521 SHA256:UAOPYkU9Fjtcao0Ul/Rrlnj/OsQvt+pgdYSZ4jOYdgs gerrit-code-review@openocd.org (ECDSA)
256 SHA256:A13M5QlnozFOvTllybRZH6vm7iSt0XLxbA48yfc2yfY gerrit-code-review@openocd.org (ECDSA)
256 SHA256:spYMBqEYoAOtK7yZBrcwE8ZpYt6b68Cfh9yEVetvbXg gerrit-code-review@openocd.org (ED25519)
+--[ED25519 256]--+
|=..              |
|+o..   .         |
|*.o   . .        |
|+B . . .         |
|Bo. = o S        |
|Oo.+ + =         |
|oB=.* = . o      |
| =+=.+   + E     |
|. .=o   . o      |
+----[SHA256]-----+
2048 SHA256:0Onrb7/PHjpo6iVZ7xQX2riKN83FJ3KGU0TvI0TaFG4 gerrit-code-review@openocd.zylin.com (RSA)