diff --git a/.travis.yml b/.travis.yml index a99f743e0..35bf8c435 100644 --- a/.travis.yml +++ b/.travis.yml @@ -4,11 +4,16 @@ dist: trusty matrix: include: - os: linux - env: BUILD=x86_64-linux-gnu + env: + - BUILD=x86_64-linux-gnu + - EXECUTABLE=openocd compiler: gcc - os: linux - env: BUILD=i686-linux-gnu CFLAGS=-m32 + env: + - BUILD=i686-linux-gnu + - CFLAGS=-m32 + - EXECUTABLE=openocd addons: apt: packages: @@ -16,17 +21,33 @@ matrix: compiler: gcc - os: linux - env: BUILD=x86_64-linux-gnu + env: + - BUILD=x86_64-linux-gnu + - EXECUTABLE=openocd compiler: clang - os: linux - env: BUILD=i686-linux-gnu CFLAGS=-m32 + env: + - BUILD=i686-linux-gnu + - CFLAGS=-m32 + - EXECUTABLE=openocd compiler: clang addons: apt: packages: - gcc-multilib + - os: linux + env: + - BUILD=i686-w64-mingw + - CONFIGURE_ARGS="--build=i686-unknown-linux-gnu --host=i686-w64-mingw32" + - EXECUTABLE=openocd.exe + compiler: i686-w64-mingw32-gcc + addons: + apt: + packages: + - binutils-mingw-w64-i686 gcc-mingw-w64-i686 g++-mingw-w64-i686 + script: - - ./bootstrap && ./configure --enable-remote-bitbang --enable-jtag_vpi && make - - file src/openocd + - ./bootstrap && ./configure --enable-remote-bitbang --enable-jtag_vpi $CONFIGURE_ARGS && make + - file src/$EXECUTABLE diff --git a/configure.ac b/configure.ac index c680bda7a..562ec5a7c 100644 --- a/configure.ac +++ b/configure.ac @@ -571,7 +571,7 @@ AS_IF([test "x$build_buspirate" = "xyes"], [ AS_IF([test "x$use_internal_jimtcl" = "xyes"], [ AS_IF([test -f "$srcdir/jimtcl/configure.ac"], [ - AX_CONFIG_SUBDIR_OPTION([jimtcl], [--disable-install-jim --with-ext="eventloop array clock regexp stdlib tclcompat" --without-ext="default"]) + AX_CONFIG_SUBDIR_OPTION([jimtcl], [--disable-install-jim]) ], [ AC_MSG_ERROR([jimtcl not found, run git submodule init and git submodule update.]) ]) @@ -628,7 +628,7 @@ PKG_CHECK_MODULES([LIBFTDI], [libftdi1], [use_libftdi=yes], [ PKG_CHECK_MODULES([LIBFTDI], [libftdi], [use_libftdi=yes], [use_libftdi=no]) ]) -PKG_CHECK_MODULES([LIBJAYLINK], [libjaylink >= 0.1], +PKG_CHECK_MODULES([LIBJAYLINK], [libjaylink >= 0.2], [use_libjaylink=yes], [use_libjaylink=no]) m4_define([PROCESS_ADAPTERS], [ @@ -655,7 +655,7 @@ PROCESS_ADAPTERS([USB0_ADAPTERS], ["x$use_libusb0" = "xyes"], [libusb-0.1]) PROCESS_ADAPTERS([HIDAPI_ADAPTERS], ["x$use_hidapi" = "xyes"], [hidapi]) PROCESS_ADAPTERS([HIDAPI_USB1_ADAPTERS], ["x$use_hidapi" = "xyes" -a "x$use_libusb1" = "xyes"], [hidapi and libusb-1.x]) PROCESS_ADAPTERS([LIBFTDI_ADAPTERS], ["x$use_libftdi" = "xyes"], [libftdi]) -PROCESS_ADAPTERS([LIBJAYLINK_ADAPTERS], ["x$use_libusb1" = "xyes" -a "x$use_internal_libjaylink" = "xyes" -o "x$use_libjaylink" = "xyes"], [libusb-1.x or libjaylink-0.1]) +PROCESS_ADAPTERS([LIBJAYLINK_ADAPTERS], ["x$use_internal_libjaylink" = "xyes" -o "x$use_libjaylink" = "xyes"], [libjaylink-0.1]) AS_IF([test "x$build_openjtag" = "xyes"], [ AS_IF([test "x$use_libusb1" != "xyes" -a "x$use_libusb0" != "xyes"], [ diff --git a/contrib/loaders/Makefile b/contrib/loaders/Makefile index 31cccb5ff..a9a27706d 100644 --- a/contrib/loaders/Makefile +++ b/contrib/loaders/Makefile @@ -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 diff --git a/contrib/loaders/erase_check/Makefile b/contrib/loaders/erase_check/Makefile index 01e62dead..427fa0c07 100644 --- a/contrib/loaders/erase_check/Makefile +++ b/contrib/loaders/erase_check/Makefile @@ -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 index 000000000..66b4ec7f5 --- /dev/null +++ b/contrib/loaders/erase_check/stm8_erase_check.inc @@ -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 index 000000000..62694006b --- /dev/null +++ b/contrib/loaders/erase_check/stm8_erase_check.s @@ -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 . +*/ +;; +;; 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 diff --git a/contrib/loaders/flash/efm32.S b/contrib/loaders/flash/efm32.S index 25d63010a..c5de55c27 100644 --- a/contrib/loaders/flash/efm32.S +++ b/contrib/loaders/flash/efm32.S @@ -44,11 +44,7 @@ #define EFM32_MSC_ADDRB_OFFSET 0x010 #define EFM32_MSC_WDATA_OFFSET 0x018 #define EFM32_MSC_STATUS_OFFSET 0x01c -#define EFM32_MSC_LOCK_OFFSET 0x03c - /* unlock MSC */ - ldr r6, =#0x1b71 - str r6, [r0, #EFM32_MSC_LOCK_OFFSET] /* set WREN to 1 */ movs r6, #1 str r6, [r0, #EFM32_MSC_WRITECTRL_OFFSET] diff --git a/contrib/loaders/flash/stm32h7x.S b/contrib/loaders/flash/stm32h7x.S new file mode 100644 index 000000000..0f5ea996f --- /dev/null +++ b/contrib/loaders/flash/stm32h7x.S @@ -0,0 +1,121 @@ +/*************************************************************************** + * Copyright (C) 2017 by STMicroelectronics * + * * + * 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. * + ***************************************************************************/ + + .text + .syntax unified + .cpu cortex-m7 + .thumb + .thumb_func + +/* + * To assemble: + * arm-none-eabi-gcc -c stm32h7x.S + * + * To disassemble: + * arm-none-eabi-objdump -d stm32h7x.o + * + * To generate binary file: + * arm-none-eabi-objcopy -O binary stm32h7x.o stm32h7_flash_write_code.bin + * + * To generate include file: + * xxd -i stm32h7_flash_write_code.bin + */ + +/* + * Code limitations: + * The workarea must have size multiple of 4 bytes, since R/W + * operations are all at 32 bits. + * The workarea must be big enough to contain 32 bytes of data, + * thus the minimum size is (rp, wp, data) = 4 + 4 + 32 = 40 bytes. + * To benefit from concurrent host write-to-buffer and target + * write-to-flash, the workarea must be way bigger than the minimum. + */ + +/* + * Params : + * r0 = workarea start, status (out) + * r1 = workarea end + * r2 = target address + * r3 = count (256 bit words) + * r4 = flash reg base + * + * Clobbered: + * r5 - rp + * r6 - wp, status, tmp + * r7 - loop index, tmp + */ + +#define STM32_FLASH_CR_OFFSET 0x0C /* offset of CR register in FLASH struct */ +#define STM32_FLASH_SR_OFFSET 0x10 /* offset of SR register in FLASH struct */ +#define STM32_CR_PROG 0x00000032 /* PSIZE64 | PG */ +#define STM32_SR_BUSY_MASK 0x00000001 /* BSY */ +#define STM32_SR_ERROR_MASK 0x03ee0000 /* DBECCERR | SNECCERR | RDSERR | RDPERR | OPERR + | INCERR | STRBERR | PGSERR | WRPERR */ + +code: + ldr r5, [r0, #4] /* read rp */ + +wait_fifo: + ldr r6, [r0, #0] /* read wp */ + cbz r6, exit /* abort if wp == 0, status = 0 */ + subs r6, r6, r5 /* number of bytes available for read in r6 */ + ittt mi /* if wrapped around */ + addmi r6, r1 /* add size of buffer */ + submi r6, r0 + submi r6, #8 + cmp r6, #32 /* wait until 32 bytes are available */ + bcc wait_fifo + + mov r6, #STM32_CR_PROG + str r6, [r4, #STM32_FLASH_CR_OFFSET] + + mov r7, #8 /* program by 8 words = 32 bytes */ +write_flash: + ldr r6, [r5], #0x04 /* read one word from src, increment ptr */ + str r6, [r2], #0x04 /* write one word to dst, increment ptr */ + dsb + cmp r5, r1 /* if rp >= end of buffer ... */ + it cs + addcs r5, r0, #8 /* ... then wrap at buffer start */ + subs r7, r7, #1 /* decrement loop index */ + bne write_flash /* loop if not done */ + +busy: + ldr r6, [r4, #STM32_FLASH_SR_OFFSET] + tst r6, #STM32_SR_BUSY_MASK + bne busy /* operation in progress, wait ... */ + + ldr r7, stm32_sr_error_mask + tst r6, r7 + bne error /* fail... */ + + str r5, [r0, #4] /* store rp */ + subs r3, r3, #1 /* decrement count */ + bne wait_fifo /* loop if not done */ + b exit + +error: + movs r7, #0 + str r7, [r0, #4] /* set rp = 0 on error */ + +exit: + mov r0, r6 /* return status in r0 */ + bkpt #0x00 + +stm32_sr_error_mask: + .word STM32_SR_ERROR_MASK diff --git a/doc/openocd.texi b/doc/openocd.texi index 89ee5eb45..ebd03c4cb 100644 --- a/doc/openocd.texi +++ b/doc/openocd.texi @@ -2948,8 +2948,8 @@ Specifies the serial number of the adapter. Specifies the adapter layout to use. @end deffn -@deffn {Config Command} {hla_vid_pid} vid pid -The vendor ID and product ID of the device. +@deffn {Config Command} {hla_vid_pid} [vid pid]+ +Pairs of vendor IDs and product IDs of the device. @end deffn @deffn {Command} {hla_command} command @@ -4503,12 +4503,6 @@ and (if the target is using it) after SRST has been released on the scan chain. @item @b{reset-end} @* Issued as the final step in @command{reset} processing. -@ignore -@item @b{reset-halt-post} -@* Currently not used -@item @b{reset-halt-pre} -@* Currently not used -@end ignore @item @b{reset-init} @* Used by @b{reset init} command for board-specific initialization. This event fires after @emph{reset-deassert-post}. @@ -4525,12 +4519,6 @@ before @command{reset_init} is called. This is the most robust place to use @command{jtag_rclk} or @command{adapter_khz} to switch to a low JTAG clock rate, when reset disables PLLs needed to use a fast clock. -@ignore -@item @b{reset-wait-pos} -@* Currently not used -@item @b{reset-wait-pre} -@* Currently not used -@end ignore @item @b{resume-start} @* Before any target is resumed @item @b{resume-end} @@ -5767,17 +5755,19 @@ Show information about flash driver. @end deffn -@deffn {Flash Driver} nrf51 +@deffn {Flash Driver} nrf5 All members of the nRF51 microcontroller families from Nordic Semiconductor include internal flash and use ARM Cortex-M0 core. +Also, the nRF52832 microcontroller from Nordic Semiconductor, which include +internal flash and use an ARM Cortex-M4F core. @example -flash bank $_FLASHNAME nrf51 0 0x00000000 0 0 $_TARGETNAME +flash bank $_FLASHNAME nrf5 0 0x00000000 0 0 $_TARGETNAME @end example -Some nrf51-specific commands are defined: +Some nrf5-specific commands are defined: -@deffn Command {nrf51 mass_erase} +@deffn Command {nrf5 mass_erase} Erases the contents of the code memory and user information configuration registers as well. It must be noted that this command works only for chips that do not have factory pre-programmed region 0 @@ -5886,9 +5876,6 @@ All members of the Stellaris LM3Sxxx, LM4x and Tiva C microcontroller families from Texas Instruments include internal flash. The driver automatically recognizes a number of these chips using the chip identification register, and autoconfigures itself. -@footnote{Currently there is a @command{stellaris mass_erase} command. -That seems pointless since the same effect can be had using the -standard @command{flash erase_address} command.} @example flash bank $_FLASHNAME stellaris 0 0 0 0 $_TARGETNAME @@ -5934,11 +5921,7 @@ as per the following example. flash bank $_FLASHNAME stm32f1x 0x08080000 0 0 0 $_TARGETNAME @end example -Some stm32f1x-specific commands -@footnote{Currently there is a @command{stm32f1x mass_erase} command. -That seems pointless since the same effect can be had using the -standard @command{flash erase_address} command.} -are defined: +Some stm32f1x-specific commands are defined: @deffn Command {stm32f1x lock} num Locks the entire stm32 device. @@ -5950,6 +5933,11 @@ Unlocks the entire stm32 device. The @var{num} parameter is a value shown by @command{flash banks}. @end deffn +@deffn Command {stm32f1x mass_erase} num +Mass erases the entire stm32f1x device. +The @var{num} parameter is a value shown by @command{flash banks}. +@end deffn + @deffn Command {stm32f1x options_read} num Read and display the stm32 option bytes written by the @command{stm32f1x options_write} command. @@ -5968,6 +5956,10 @@ include internal flash and use ARM Cortex-M3/M4/M7 cores. The driver automatically recognizes a number of these chips using the chip identification register, and autoconfigures itself. +@example +flash bank $_FLASHNAME stm32f2x 0 0 0 0 $_TARGETNAME +@end example + Note that some devices have been found that have a flash size register that contains an invalid value, to workaround this issue you can override the probed value used by the flash driver. @@ -5988,6 +5980,11 @@ Unlocks the entire stm32 device. The @var{num} parameter is a value shown by @command{flash banks}. @end deffn +@deffn Command {stm32f2x mass_erase} num +Mass erases the entire stm32f2x device. +The @var{num} parameter is a value shown by @command{flash banks}. +@end deffn + @deffn Command {stm32f2x options_read} num Reads and displays user options and (where implemented) boot_addr0, boot_addr1, optcr2. The @var{num} parameter is a value shown by @command{flash banks}. @@ -6007,12 +6004,52 @@ The @var{num} parameter is a value shown by @command{flash banks}, @var{optcr2} @end deffn @end deffn +@deffn {Flash Driver} stm32h7x +All members of the STM32H7 microcontroller families from ST Microelectronics +include internal flash and use ARM Cortex-M7 core. +The driver automatically recognizes a number of these chips using +the chip identification register, and autoconfigures itself. + +@example +flash bank $_FLASHNAME stm32h7x 0 0 0 0 $_TARGETNAME +@end example + +Note that some devices have been found that have a flash size register that contains +an invalid value, to workaround this issue you can override the probed value used by +the flash driver. + +@example +flash bank $_FLASHNAME stm32h7x 0 0x20000 0 0 $_TARGETNAME +@end example + +Some stm32h7x-specific commands are defined: + +@deffn Command {stm32h7x lock} num +Locks the entire stm32 device. +The @var{num} parameter is a value shown by @command{flash banks}. +@end deffn + +@deffn Command {stm32h7x unlock} num +Unlocks the entire stm32 device. +The @var{num} parameter is a value shown by @command{flash banks}. +@end deffn + +@deffn Command {stm32h7x mass_erase} num +Mass erases the entire stm32h7x device. +The @var{num} parameter is a value shown by @command{flash banks}. +@end deffn +@end deffn + @deffn {Flash Driver} stm32lx All members of the STM32L microcontroller families from ST Microelectronics include internal flash and use ARM Cortex-M3 and Cortex-M0+ cores. The driver automatically recognizes a number of these chips using the chip identification register, and autoconfigures itself. +@example +flash bank $_FLASHNAME stm32lx 0 0 0 0 $_TARGETNAME +@end example + Note that some devices have been found that have a flash size register that contains an invalid value, to workaround this issue you can override the probed value used by the flash driver. If you use 0 as the bank base address, it tells the @@ -6025,6 +6062,16 @@ flash bank $_FLASHNAME stm32lx 0x08000000 0x20000 0 0 $_TARGETNAME Some stm32lx-specific commands are defined: +@deffn Command {stm32lx lock} num +Locks the entire stm32 device. +The @var{num} parameter is a value shown by @command{flash banks}. +@end deffn + +@deffn Command {stm32lx unlock} num +Unlocks the entire stm32 device. +The @var{num} parameter is a value shown by @command{flash banks}. +@end deffn + @deffn Command {stm32lx mass_erase} num Mass erases the entire stm32lx device (all flash banks and EEPROM data). This is the only way to unlock a protected flash (unless RDP @@ -6033,6 +6080,42 @@ The @var{num} parameter is a value shown by @command{flash banks}. @end deffn @end deffn +@deffn {Flash Driver} stm32l4x +All members of the STM32L4 microcontroller families from ST Microelectronics +include internal flash and use ARM Cortex-M4 cores. +The driver automatically recognizes a number of these chips using +the chip identification register, and autoconfigures itself. + +@example +flash bank $_FLASHNAME stm32l4x 0 0 0 0 $_TARGETNAME +@end example + +Note that some devices have been found that have a flash size register that contains +an invalid value, to workaround this issue you can override the probed value used by +the flash driver. + +@example +flash bank $_FLASHNAME stm32l4x 0x08000000 0x40000 0 0 $_TARGETNAME +@end example + +Some stm32l4x-specific commands are defined: + +@deffn Command {stm32l4x lock} num +Locks the entire stm32 device. +The @var{num} parameter is a value shown by @command{flash banks}. +@end deffn + +@deffn Command {stm32l4x unlock} num +Unlocks the entire stm32 device. +The @var{num} parameter is a value shown by @command{flash banks}. +@end deffn + +@deffn Command {stm32l4x mass_erase} num +Mass erases the entire stm32l4x device. +The @var{num} parameter is a value shown by @command{flash banks}. +@end deffn +@end deffn + @deffn {Flash Driver} str7x All members of the STR7 microcontroller family from ST Microelectronics include internal flash and use ARM7TDMI cores. @@ -7602,6 +7685,20 @@ requests by using a special SVC instruction that is trapped at the Supervisor Call vector by OpenOCD. @end deffn +@deffn Command {arm semihosting_cmdline} [@option{enable}|@option{disable}] +@cindex ARM semihosting +Set the command line to be passed to the debuggee. + +@example +arm semihosting_cmdline argv0 argv1 argv2 ... +@end example + +This option lets one set the command line arguments to be passed to +the program. The first argument (argv0) is the program name in a +standard C environment (argv[0]). Depending on the program (not much +programs look at argv[0]), argv0 is ignored and can be any string. +@end deffn + @deffn Command {arm semihosting_fileio} [@option{enable}|@option{disable}] @cindex ARM semihosting Display status of semihosting fileio, after optionally changing that diff --git a/src/flash/mflash.c b/src/flash/mflash.c index b69995542..4c95d216c 100644 --- a/src/flash/mflash.c +++ b/src/flash/mflash.c @@ -259,11 +259,11 @@ static int mg_dsk_wait(mg_io_type_wait wait_local, uint32_t time_var) case mg_io_wait_rdy: if (status & mg_io_rbit_status_ready) return ERROR_OK; - + /* fallthrough */ case mg_io_wait_drq: if (status & mg_io_rbit_status_data_req) return ERROR_OK; - + /* fallthrough */ default: break; } diff --git a/src/flash/nand/mx3.c b/src/flash/nand/mx3.c index b61e47535..5fdc92305 100644 --- a/src/flash/nand/mx3.c +++ b/src/flash/nand/mx3.c @@ -281,6 +281,7 @@ static int imx31_command(struct nand_device *nand, uint8_t command) * offset == one half of page size */ in_sram_address = MX3_NF_MAIN_BUFFER0 + (nand->page_size >> 1); + break; default: in_sram_address = MX3_NF_MAIN_BUFFER0; } diff --git a/src/flash/nor/Makefile.am b/src/flash/nor/Makefile.am index c647cbb60..8a57f4f7b 100644 --- a/src/flash/nor/Makefile.am +++ b/src/flash/nor/Makefile.am @@ -37,7 +37,7 @@ NOR_DRIVERS = \ %D%/mrvlqspi.c \ %D%/niietcm4.c \ %D%/non_cfi.c \ - %D%/nrf51.c \ + %D%/nrf5.c \ %D%/numicro.c \ %D%/ocl.c \ %D%/pic32mx.c \ @@ -50,6 +50,7 @@ NOR_DRIVERS = \ %D%/stm32f2x.c \ %D%/stm32lx.c \ %D%/stm32l4x.c \ + %D%/stm32h7x.c \ %D%/str7x.c \ %D%/str9x.c \ %D%/str9xpec.c \ diff --git a/src/flash/nor/drivers.c b/src/flash/nor/drivers.c index 56b451c46..3f0c3c7e6 100644 --- a/src/flash/nor/drivers.c +++ b/src/flash/nor/drivers.c @@ -49,6 +49,7 @@ extern struct flash_driver lpcspifi_flash; extern struct flash_driver mdr_flash; extern struct flash_driver mrvlqspi_flash; extern struct flash_driver niietcm4_flash; +extern struct flash_driver nrf5_flash; extern struct flash_driver nrf51_flash; extern struct flash_driver numicro_flash; extern struct flash_driver ocl_flash; @@ -60,6 +61,7 @@ extern struct flash_driver stm32f1x_flash; extern struct flash_driver stm32f2x_flash; extern struct flash_driver stm32lx_flash; extern struct flash_driver stm32l4x_flash; +extern struct flash_driver stm32h7x_flash; extern struct flash_driver stmsmi_flash; extern struct flash_driver str7x_flash; extern struct flash_driver str9x_flash; @@ -103,6 +105,7 @@ static struct flash_driver *flash_drivers[] = { &mdr_flash, &mrvlqspi_flash, &niietcm4_flash, + &nrf5_flash, &nrf51_flash, &numicro_flash, &ocl_flash, @@ -114,6 +117,7 @@ static struct flash_driver *flash_drivers[] = { &stm32f2x_flash, &stm32lx_flash, &stm32l4x_flash, + &stm32h7x_flash, &stmsmi_flash, &str7x_flash, &str9x_flash, diff --git a/src/flash/nor/efm32.c b/src/flash/nor/efm32.c index 117cd8a1b..b8453e1dd 100644 --- a/src/flash/nor/efm32.c +++ b/src/flash/nor/efm32.c @@ -49,6 +49,8 @@ #define EZR_FAMILY_ID_WONDER_GECKO 120 #define EZR_FAMILY_ID_LEOPARD_GECKO 121 #define EZR_FAMILY_ID_HAPPY_GECKO 122 +#define EFR_FAMILY_ID_MIGHTY_GECKO 16 +#define EFR_FAMILY_ID_BLUE_GECKO 20 #define EFM32_FLASH_ERASE_TMO 100 #define EFM32_FLASH_WDATAREADY_TMO 100 @@ -72,27 +74,31 @@ #define EFM32_MSC_DI_PROD_REV (EFM32_MSC_DEV_INFO+0x1ff) #define EFM32_MSC_REGBASE 0x400c0000 -#define EFM32_MSC_WRITECTRL (EFM32_MSC_REGBASE+0x008) +#define EFR32_MSC_REGBASE 0x400e0000 +#define EFM32_MSC_REG_WRITECTRL 0x008 #define EFM32_MSC_WRITECTRL_WREN_MASK 0x1 -#define EFM32_MSC_WRITECMD (EFM32_MSC_REGBASE+0x00c) +#define EFM32_MSC_REG_WRITECMD 0x00c #define EFM32_MSC_WRITECMD_LADDRIM_MASK 0x1 #define EFM32_MSC_WRITECMD_ERASEPAGE_MASK 0x2 #define EFM32_MSC_WRITECMD_WRITEONCE_MASK 0x8 -#define EFM32_MSC_ADDRB (EFM32_MSC_REGBASE+0x010) -#define EFM32_MSC_WDATA (EFM32_MSC_REGBASE+0x018) -#define EFM32_MSC_STATUS (EFM32_MSC_REGBASE+0x01c) +#define EFM32_MSC_REG_ADDRB 0x010 +#define EFM32_MSC_REG_WDATA 0x018 +#define EFM32_MSC_REG_STATUS 0x01c #define EFM32_MSC_STATUS_BUSY_MASK 0x1 #define EFM32_MSC_STATUS_LOCKED_MASK 0x2 #define EFM32_MSC_STATUS_INVADDR_MASK 0x4 #define EFM32_MSC_STATUS_WDATAREADY_MASK 0x8 #define EFM32_MSC_STATUS_WORDTIMEOUT_MASK 0x10 #define EFM32_MSC_STATUS_ERASEABORTED_MASK 0x20 -#define EFM32_MSC_LOCK (EFM32_MSC_REGBASE+0x03c) +#define EFM32_MSC_REG_LOCK 0x03c +#define EFR32_MSC_REG_LOCK 0x040 #define EFM32_MSC_LOCK_LOCKKEY 0x1b71 struct efm32x_flash_bank { int probed; uint32_t lb_page[LOCKBITS_PAGE_SZ/4]; + uint32_t reg_base; + uint32_t reg_lock; }; struct efm32_info { @@ -132,11 +138,30 @@ static int efm32x_get_prod_rev(struct flash_bank *bank, uint8_t *prev) return target_read_u8(bank->target, EFM32_MSC_DI_PROD_REV, prev); } +static int efm32x_read_reg_u32(struct flash_bank *bank, target_addr_t offset, + uint32_t *value) +{ + struct efm32x_flash_bank *efm32x_info = bank->driver_priv; + uint32_t base = efm32x_info->reg_base; + + return target_read_u32(bank->target, base + offset, value); +} + +static int efm32x_write_reg_u32(struct flash_bank *bank, target_addr_t offset, + uint32_t value) +{ + struct efm32x_flash_bank *efm32x_info = bank->driver_priv; + uint32_t base = efm32x_info->reg_base; + + return target_write_u32(bank->target, base + offset, value); +} + static int efm32x_read_info(struct flash_bank *bank, struct efm32_info *efm32_info) { int ret; uint32_t cpuid = 0; + struct efm32x_flash_bank *efm32x_info = bank->driver_priv; memset(efm32_info, 0, sizeof(struct efm32_info)); @@ -175,6 +200,15 @@ static int efm32x_read_info(struct flash_bank *bank, if (ERROR_OK != ret) return ret; + if (EFR_FAMILY_ID_BLUE_GECKO == efm32_info->part_family || + EFR_FAMILY_ID_MIGHTY_GECKO == efm32_info->part_family) { + efm32x_info->reg_base = EFR32_MSC_REGBASE; + efm32x_info->reg_lock = EFR32_MSC_REG_LOCK; + } else { + efm32x_info->reg_base = EFM32_MSC_REGBASE; + efm32x_info->reg_lock = EFM32_MSC_REG_LOCK; + } + if (EFM_FAMILY_ID_GECKO == efm32_info->part_family || EFM_FAMILY_ID_TINY_GECKO == efm32_info->part_family) efm32_info->page_size = 512; @@ -208,7 +242,9 @@ static int efm32x_read_info(struct flash_bank *bank, } } else if (EFM_FAMILY_ID_WONDER_GECKO == efm32_info->part_family || EZR_FAMILY_ID_WONDER_GECKO == efm32_info->part_family || - EZR_FAMILY_ID_LEOPARD_GECKO == efm32_info->part_family) { + EZR_FAMILY_ID_LEOPARD_GECKO == efm32_info->part_family || + EFR_FAMILY_ID_BLUE_GECKO == efm32_info->part_family || + EFR_FAMILY_ID_MIGHTY_GECKO == efm32_info->part_family) { uint8_t pg_size = 0; ret = target_read_u8(bank->target, EFM32_MSC_DI_PAGE_SIZE, &pg_size); @@ -241,6 +277,10 @@ static int efm32x_decode_info(struct efm32_info *info, char *buf, int buf_size) case EZR_FAMILY_ID_HAPPY_GECKO: printed = snprintf(buf, buf_size, "EZR32 "); break; + case EFR_FAMILY_ID_MIGHTY_GECKO: + case EFR_FAMILY_ID_BLUE_GECKO: + printed = snprintf(buf, buf_size, "EFR32 "); + break; default: printed = snprintf(buf, buf_size, "EFM32 "); } @@ -276,6 +316,12 @@ static int efm32x_decode_info(struct efm32_info *info, char *buf, int buf_size) case EZR_FAMILY_ID_HAPPY_GECKO: printed = snprintf(buf, buf_size, "Happy Gecko"); break; + case EFR_FAMILY_ID_BLUE_GECKO: + printed = snprintf(buf, buf_size, "Blue Gecko"); + break; + case EFR_FAMILY_ID_MIGHTY_GECKO: + printed = snprintf(buf, buf_size, "Mighty Gecko"); + break; } buf += printed; @@ -319,7 +365,7 @@ static int efm32x_set_reg_bits(struct flash_bank *bank, uint32_t reg, int ret = 0; uint32_t reg_val = 0; - ret = target_read_u32(bank->target, reg, ®_val); + ret = efm32x_read_reg_u32(bank, reg, ®_val); if (ERROR_OK != ret) return ret; @@ -328,18 +374,19 @@ static int efm32x_set_reg_bits(struct flash_bank *bank, uint32_t reg, else reg_val &= ~bitmask; - return target_write_u32(bank->target, reg, reg_val); + return efm32x_write_reg_u32(bank, reg, reg_val); } static int efm32x_set_wren(struct flash_bank *bank, int write_enable) { - return efm32x_set_reg_bits(bank, EFM32_MSC_WRITECTRL, + return efm32x_set_reg_bits(bank, EFM32_MSC_REG_WRITECTRL, EFM32_MSC_WRITECTRL_WREN_MASK, write_enable); } static int efm32x_msc_lock(struct flash_bank *bank, int lock) { - return target_write_u32(bank->target, EFM32_MSC_LOCK, + struct efm32x_flash_bank *efm32x_info = bank->driver_priv; + return efm32x_write_reg_u32(bank, efm32x_info->reg_lock, (lock ? 0 : EFM32_MSC_LOCK_LOCKKEY)); } @@ -350,7 +397,7 @@ static int efm32x_wait_status(struct flash_bank *bank, int timeout, uint32_t status = 0; while (1) { - ret = target_read_u32(bank->target, EFM32_MSC_STATUS, &status); + ret = efm32x_read_reg_u32(bank, EFM32_MSC_REG_STATUS, &status); if (ERROR_OK != ret) break; @@ -389,16 +436,16 @@ static int efm32x_erase_page(struct flash_bank *bank, uint32_t addr) LOG_DEBUG("erasing flash page at 0x%08" PRIx32, addr); - ret = target_write_u32(bank->target, EFM32_MSC_ADDRB, addr); + ret = efm32x_write_reg_u32(bank, EFM32_MSC_REG_ADDRB, addr); if (ERROR_OK != ret) return ret; - ret = efm32x_set_reg_bits(bank, EFM32_MSC_WRITECMD, + ret = efm32x_set_reg_bits(bank, EFM32_MSC_REG_WRITECMD, EFM32_MSC_WRITECMD_LADDRIM_MASK, 1); if (ERROR_OK != ret) return ret; - ret = target_read_u32(bank->target, EFM32_MSC_STATUS, &status); + ret = efm32x_read_reg_u32(bank, EFM32_MSC_REG_STATUS, &status); if (ERROR_OK != ret) return ret; @@ -412,7 +459,7 @@ static int efm32x_erase_page(struct flash_bank *bank, uint32_t addr) return ERROR_FAIL; } - ret = efm32x_set_reg_bits(bank, EFM32_MSC_WRITECMD, + ret = efm32x_set_reg_bits(bank, EFM32_MSC_REG_WRITECMD, EFM32_MSC_WRITECMD_ERASEPAGE_MASK, 1); if (ERROR_OK != ret) return ret; @@ -589,6 +636,7 @@ static int efm32x_write_block(struct flash_bank *bank, const uint8_t *buf, uint32_t address = bank->base + offset; struct reg_param reg_params[5]; struct armv7m_algorithm armv7m_info; + struct efm32x_flash_bank *efm32x_info = bank->driver_priv; int ret = ERROR_OK; /* see contrib/loaders/flash/efm32.S for src */ @@ -598,10 +646,7 @@ static int efm32x_write_block(struct flash_bank *bank, const uint8_t *buf, /* #define EFM32_MSC_ADDRB_OFFSET 0x010 */ /* #define EFM32_MSC_WDATA_OFFSET 0x018 */ /* #define EFM32_MSC_STATUS_OFFSET 0x01c */ - /* #define EFM32_MSC_LOCK_OFFSET 0x03c */ - 0x15, 0x4e, /* ldr r6, =#0x1b71 */ - 0xc6, 0x63, /* str r6, [r0, #EFM32_MSC_LOCK_OFFSET] */ 0x01, 0x26, /* movs r6, #1 */ 0x86, 0x60, /* str r6, [r0, #EFM32_MSC_WRITECTRL_OFFSET] */ @@ -660,11 +705,9 @@ static int efm32x_write_block(struct flash_bank *bank, const uint8_t *buf, /* exit: */ 0x30, 0x46, /* mov r0, r6 */ 0x00, 0xbe, /* bkpt #0 */ - - /* LOCKKEY */ - 0x71, 0x1b, 0x00, 0x00 }; + /* flash write code */ if (target_alloc_working_area(target, sizeof(efm32x_flash_write_code), &write_algorithm) != ERROR_OK) { @@ -697,7 +740,7 @@ static int efm32x_write_block(struct flash_bank *bank, const uint8_t *buf, init_reg_param(®_params[3], "r3", 32, PARAM_OUT); /* buffer end */ init_reg_param(®_params[4], "r4", 32, PARAM_IN_OUT); /* target address */ - buf_set_u32(reg_params[0].value, 0, 32, EFM32_MSC_REGBASE); + buf_set_u32(reg_params[0].value, 0, 32, efm32x_info->reg_base); buf_set_u32(reg_params[1].value, 0, 32, count); buf_set_u32(reg_params[2].value, 0, 32, source->address); buf_set_u32(reg_params[3].value, 0, 32, source->address + source->size); @@ -762,16 +805,16 @@ static int efm32x_write_word(struct flash_bank *bank, uint32_t addr, /* if not called, GDB errors will be reported during large writes */ keep_alive(); - ret = target_write_u32(bank->target, EFM32_MSC_ADDRB, addr); + ret = efm32x_write_reg_u32(bank, EFM32_MSC_REG_ADDRB, addr); if (ERROR_OK != ret) return ret; - ret = efm32x_set_reg_bits(bank, EFM32_MSC_WRITECMD, + ret = efm32x_set_reg_bits(bank, EFM32_MSC_REG_WRITECMD, EFM32_MSC_WRITECMD_LADDRIM_MASK, 1); if (ERROR_OK != ret) return ret; - ret = target_read_u32(bank->target, EFM32_MSC_STATUS, &status); + ret = efm32x_read_reg_u32(bank, EFM32_MSC_REG_STATUS, &status); if (ERROR_OK != ret) return ret; @@ -792,13 +835,13 @@ static int efm32x_write_word(struct flash_bank *bank, uint32_t addr, return ret; } - ret = target_write_u32(bank->target, EFM32_MSC_WDATA, val); + ret = efm32x_write_reg_u32(bank, EFM32_MSC_REG_WDATA, val); if (ERROR_OK != ret) { LOG_ERROR("WDATA write failed"); return ret; } - ret = target_write_u32(bank->target, EFM32_MSC_WRITECMD, + ret = efm32x_write_reg_u32(bank, EFM32_MSC_REG_WRITECMD, EFM32_MSC_WRITECMD_WRITEONCE_MASK); if (ERROR_OK != ret) { LOG_ERROR("WRITECMD write failed"); diff --git a/src/flash/nor/fespi.c b/src/flash/nor/fespi.c index 9b53d2d89..cedcb3b4a 100644 --- a/src/flash/nor/fespi.c +++ b/src/flash/nor/fespi.c @@ -1,7 +1,7 @@ /*************************************************************************** * Copyright (C) 2010 by Antonio Borneo * * Modified by Megan Wachs from the original stmsmi.c * - * * + * * * 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 * @@ -18,7 +18,7 @@ /* The Freedom E SPI controller is a SPI bus controller * specifically designed for SPI Flash Memories on Freedom E platforms. - * + * * Two working modes are available: * - SW mode: the SPI is controlled by SW. Any custom commands can be sent * on the bus. Writes are only possible in this mode. @@ -127,7 +127,7 @@ int __a; \ uint32_t __v; \ \ - __a = target_read_u32(target, ctrl_base + (a), &__v); \ + __a = target_read_u32(target, ctrl_base + (a), &__v); \ if (__a != ERROR_OK) { \ LOG_ERROR("FESPI_READ_REG error"); \ return __a; \ @@ -139,7 +139,7 @@ { \ int __r; \ \ - __r = target_write_u32(target, ctrl_base + (a), (v)); \ + __r = target_write_u32(target, ctrl_base + (a), (v)); \ if (__r != ERROR_OK) { \ LOG_ERROR("FESPI_WRITE_REG error"); \ return __r; \ @@ -163,7 +163,7 @@ struct fespi_target { uint32_t ctrl_base; }; -//TODO !!! What is the right naming convention here? +/* TODO !!! What is the right naming convention here? */ static const struct fespi_target target_devices[] = { /* name, tap_idcode, ctrl_base */ { "Freedom E300 SPI Flash", 0x10e31913 , 0x10014000 }, @@ -189,16 +189,17 @@ FLASH_BANK_COMMAND_HANDLER(fespi_flash_bank_command) fespi_info->probed = 0; fespi_info->ctrl_base = 0; if (CMD_ARGC >= 7) { - int temp; - COMMAND_PARSE_NUMBER(int, CMD_ARGV[6], temp); - fespi_info->ctrl_base = (uint32_t) temp; - LOG_DEBUG("ASSUMING FESPI device at ctrl_base = 0x%x", fespi_info->ctrl_base); + int temp; + COMMAND_PARSE_NUMBER(int, CMD_ARGV[6], temp); + fespi_info->ctrl_base = (uint32_t) temp; + LOG_DEBUG("ASSUMING FESPI device at ctrl_base = 0x%x", fespi_info->ctrl_base); } return ERROR_OK; } -static int fespi_set_dir (struct flash_bank * bank, bool dir) { +static int fespi_set_dir(struct flash_bank *bank, bool dir) +{ struct target *target = bank->target; struct fespi_flash_bank *fespi_info = bank->driver_priv; uint32_t ctrl_base = fespi_info->ctrl_base; @@ -211,7 +212,8 @@ static int fespi_set_dir (struct flash_bank * bank, bool dir) { } -static int fespi_txwm_wait(struct flash_bank *bank) { +static int fespi_txwm_wait(struct flash_bank *bank) +{ struct target *target = bank->target; struct fespi_flash_bank *fespi_info = bank->driver_priv; uint32_t ctrl_base = fespi_info->ctrl_base; @@ -219,9 +221,8 @@ static int fespi_txwm_wait(struct flash_bank *bank) { int64_t start = timeval_ms(); while (1) { - if (FESPI_READ_REG(FESPI_REG_IP) & FESPI_IP_TXWM) { + if (FESPI_READ_REG(FESPI_REG_IP) & FESPI_IP_TXWM) break; - } int64_t now = timeval_ms(); if (now - start > 1000) { LOG_ERROR("ip.txwm didn't get set."); @@ -233,7 +234,8 @@ static int fespi_txwm_wait(struct flash_bank *bank) { } -static int fespi_tx(struct flash_bank *bank, uint8_t in){ +static int fespi_tx(struct flash_bank *bank, uint8_t in) +{ struct target *target = bank->target; struct fespi_flash_bank *fespi_info = bank->driver_priv; uint32_t ctrl_base = fespi_info->ctrl_base; @@ -241,9 +243,8 @@ static int fespi_tx(struct flash_bank *bank, uint8_t in){ int64_t start = timeval_ms(); while (1) { - if ((int32_t) FESPI_READ_REG(FESPI_REG_TXFIFO) >= 0) { + if ((int32_t) FESPI_READ_REG(FESPI_REG_TXFIFO) >= 0) break; - } int64_t now = timeval_ms(); if (now - start > 1000) { LOG_ERROR("txfifo stayed negative."); @@ -276,14 +277,14 @@ static int fespi_rx(struct flash_bank *bank, uint8_t *out) } } - if (out) { + if (out) *out = value & 0xff; - } + return ERROR_OK; } -//TODO!!! Why don't we need to call this after writing? -static int fespi_wip (struct flash_bank * bank, int timeout) +/* TODO!!! Why don't we need to call this after writing? */ +static int fespi_wip(struct flash_bank *bank, int timeout) { struct target *target = bank->target; struct fespi_flash_bank *fespi_info = bank->driver_priv; @@ -326,26 +327,34 @@ static int fespi_erase_sector(struct flash_bank *bank, int sector) int retval; retval = fespi_tx(bank, SPIFLASH_WRITE_ENABLE); - if (retval != ERROR_OK) {return retval;} + if (retval != ERROR_OK) + return retval; retval = fespi_txwm_wait(bank); - if (retval != ERROR_OK) {return retval;} - + if (retval != ERROR_OK) + return retval; + FESPI_WRITE_REG(FESPI_REG_CSMODE, FESPI_CSMODE_HOLD); retval = fespi_tx(bank, fespi_info->dev->erase_cmd); - if (retval != ERROR_OK) {return retval;} + if (retval != ERROR_OK) + return retval; sector = bank->sectors[sector].offset; retval = fespi_tx(bank, sector >> 16); - if (retval != ERROR_OK) {return retval;} + if (retval != ERROR_OK) + return retval; retval = fespi_tx(bank, sector >> 8); - if (retval != ERROR_OK) {return retval;} + if (retval != ERROR_OK) + return retval; retval = fespi_tx(bank, sector); - if (retval != ERROR_OK) {return retval;} + if (retval != ERROR_OK) + return retval; retval = fespi_txwm_wait(bank); - if (retval != ERROR_OK) {return retval;} + if (retval != ERROR_OK) + return retval; FESPI_WRITE_REG(FESPI_REG_CSMODE, FESPI_CSMODE_AUTO); retval = fespi_wip(bank, FESPI_MAX_TIMEOUT); - if (retval != ERROR_OK){return retval;} + if (retval != ERROR_OK) + return retval; return ERROR_OK; } @@ -384,9 +393,9 @@ static int fespi_erase(struct flash_bank *bank, int first, int last) FESPI_WRITE_REG(FESPI_REG_TXCTRL, FESPI_TXWM(1)); retval = fespi_txwm_wait(bank); - if (retval != ERROR_OK){ - LOG_ERROR("WM Didn't go high before attempting."); - return retval; + if (retval != ERROR_OK) { + LOG_ERROR("WM Didn't go high before attempting."); + return retval; } /* Disable Hardware accesses*/ @@ -394,7 +403,7 @@ static int fespi_erase(struct flash_bank *bank, int first, int last) /* poll WIP */ retval = fespi_wip(bank, FESPI_PROBE_TIMEOUT); - if (retval != ERROR_OK) + if (retval != ERROR_OK) return retval; for (sector = first; sector <= last; sector++) { @@ -427,7 +436,7 @@ static int slow_fespi_write_buffer(struct flash_bank *bank, uint32_t ctrl_base = fespi_info->ctrl_base; uint32_t ii; - //TODO!!! assert that len < page size + /* TODO!!! assert that len < page size */ fespi_tx(bank, SPIFLASH_WRITE_ENABLE); fespi_txwm_wait(bank); @@ -440,9 +449,8 @@ static int slow_fespi_write_buffer(struct flash_bank *bank, fespi_tx(bank, offset >> 8); fespi_tx(bank, offset); - for (ii = 0; ii < len; ii++) { + for (ii = 0; ii < len; ii++) fespi_tx(bank, buffer[ii]); - } fespi_txwm_wait(bank); @@ -457,8 +465,9 @@ static int slow_fespi_write_buffer(struct flash_bank *bank, * Here's the source for the algorithm. * You can turn it into the array below using: sed -n '/ALGO_START$/,/ALGO_END/ p' fespi.c | \ - riscv32-unknown-elf-gcc -x assembler-with-cpp - -nostdlib -nostartfiles -o tmp.o && \ - riscv32-unknown-elf-objcopy -O binary tmp.o algorithm.bin && \ + riscv64-unknown-elf-gcc -march=rv32i -mabi=ilp32 -x \ + assembler-with-cpp - -nostdlib -nostartfiles -o tmp.o && \ + riscv64-unknown-elf-objcopy -O binary tmp.o algorithm.bin && \ xxd -i algorithm.bin // ALGO_START @@ -487,67 +496,67 @@ static int slow_fespi_write_buffer(struct flash_bank *bank, .global _start _start: command_table: - j main // 0 - ebreak // 4 - j tx // 8 - j txwm_wait // 12 - j write_reg // 16 + j main // 0 + ebreak // 4 + j tx // 8 + j txwm_wait // 12 + j write_reg // 16 j wip_wait // 20 - j set_dir // 24 + j set_dir // 24 // Execute the program. main: - lbu t0, 0(a1) - addi a1, a1, 1 - la t1, command_table - add t0, t0, t1 - jr t0 + lbu t0, 0(a1) + addi a1, a1, 1 + la t1, command_table + add t0, t0, t1 + jr t0 // Read 1 byte the contains the number of bytes to transmit. Then read those // bytes from the program and transmit them one by one. tx: - lbu t1, 0(a1) // read number of bytes to transmit - addi a1, a1, 1 + lbu t1, 0(a1) // read number of bytes to transmit + addi a1, a1, 1 1: lw t0, FESPI_REG_TXFIFO(a0) // wait for FIFO clear - bltz t0, 1b - lbu t0, 0(a1) // Load byte to write - sw t0, FESPI_REG_TXFIFO(a0) - addi a1, a1, 1 - addi t1, t1, -1 - bgtz t1, 1b - j main + bltz t0, 1b + lbu t0, 0(a1) // Load byte to write + sw t0, FESPI_REG_TXFIFO(a0) + addi a1, a1, 1 + addi t1, t1, -1 + bgtz t1, 1b + j main // Wait until TXWM is set. txwm_wait: 1: lw t0, FESPI_REG_IP(a0) - andi t0, t0, FESPI_IP_TXWM - beqz t0, 1b - j main + andi t0, t0, FESPI_IP_TXWM + beqz t0, 1b + j main // Read 1 byte that contains the offset of the register to write, and 1 byte // that contains the data to write. write_reg: - lbu t0, 0(a1) // read register to write - add t0, t0, a0 - lbu t1, 1(a1) // read value to write - addi a1, a1, 2 - sw t1, 0(t0) - j main + lbu t0, 0(a1) // read register to write + add t0, t0, a0 + lbu t1, 1(a1) // read value to write + addi a1, a1, 2 + sw t1, 0(t0) + j main wip_wait: li a2, SPIFLASH_READ_STATUS - jal txrx_byte + jal txrx_byte // discard first result 1: li a2, 0 - jal txrx_byte + jal txrx_byte andi t0, a2, SPIFLASH_BSY_BIT bnez t0, 1b j main txrx_byte: // transmit the byte in a2, receive a bit into a2 lw t0, FESPI_REG_TXFIFO(a0) // wait for FIFO clear - bltz t0, txrx_byte - sw a2, FESPI_REG_TXFIFO(a0) + bltz t0, txrx_byte + sw a2, FESPI_REG_TXFIFO(a0) 1: lw a2, FESPI_REG_RXFIFO(a0) bltz a2, 1b ret @@ -556,8 +565,8 @@ set_dir: lw t0, FESPI_REG_FMT(a0) li t1, ~(FESPI_FMT_DIR(0xFFFFFFFF)) and t0, t0, t1 - lbu t1, 0(a1) // read value to OR in - addi a1, a1, 1 + lbu t1, 0(a1) // read value to OR in + addi a1, a1, 1 or t0, t0, t1 sw t0, FESPI_REG_FMT(a0) j main @@ -565,24 +574,24 @@ set_dir: // ALGO_END */ static const uint8_t algorithm_bin[] = { - 0x6f, 0x00, 0xc0, 0x01, 0x73, 0x00, 0x10, 0x00, 0x6f, 0x00, 0xc0, 0x02, - 0x6f, 0x00, 0x00, 0x05, 0x6f, 0x00, 0xc0, 0x05, 0x6f, 0x00, 0x00, 0x07, - 0x6f, 0x00, 0x00, 0x0a, 0x83, 0xc2, 0x05, 0x00, 0x93, 0x85, 0x15, 0x00, - 0x17, 0x03, 0x00, 0x00, 0x13, 0x03, 0xc3, 0xfd, 0xb3, 0x82, 0x62, 0x00, - 0x67, 0x80, 0x02, 0x00, 0x03, 0xc3, 0x05, 0x00, 0x93, 0x85, 0x15, 0x00, - 0x83, 0x22, 0x85, 0x04, 0xe3, 0xce, 0x02, 0xfe, 0x83, 0xc2, 0x05, 0x00, - 0x23, 0x24, 0x55, 0x04, 0x93, 0x85, 0x15, 0x00, 0x13, 0x03, 0xf3, 0xff, - 0xe3, 0x44, 0x60, 0xfe, 0x6f, 0xf0, 0x5f, 0xfc, 0x83, 0x22, 0x45, 0x07, - 0x93, 0xf2, 0x12, 0x00, 0xe3, 0x8c, 0x02, 0xfe, 0x6f, 0xf0, 0x5f, 0xfb, - 0x83, 0xc2, 0x05, 0x00, 0xb3, 0x82, 0xa2, 0x00, 0x03, 0xc3, 0x15, 0x00, - 0x93, 0x85, 0x25, 0x00, 0x23, 0xa0, 0x62, 0x00, 0x6f, 0xf0, 0xdf, 0xf9, - 0x13, 0x06, 0x50, 0x00, 0xef, 0x00, 0x80, 0x01, 0x13, 0x06, 0x00, 0x00, - 0xef, 0x00, 0x00, 0x01, 0x93, 0x72, 0x16, 0x00, 0xe3, 0x9a, 0x02, 0xfe, - 0x6f, 0xf0, 0x1f, 0xf8, 0x83, 0x22, 0x85, 0x04, 0xe3, 0xce, 0x02, 0xfe, - 0x23, 0x24, 0xc5, 0x04, 0x03, 0x26, 0xc5, 0x04, 0xe3, 0x4e, 0x06, 0xfe, - 0x67, 0x80, 0x00, 0x00, 0x83, 0x22, 0x05, 0x04, 0x13, 0x03, 0x70, 0xff, - 0xb3, 0xf2, 0x62, 0x00, 0x03, 0xc3, 0x05, 0x00, 0x93, 0x85, 0x15, 0x00, - 0xb3, 0xe2, 0x62, 0x00, 0x23, 0x20, 0x55, 0x04, 0x6f, 0xf0, 0x9f, 0xf4 + 0x6f, 0x00, 0xc0, 0x01, 0x73, 0x00, 0x10, 0x00, 0x6f, 0x00, 0xc0, 0x02, + 0x6f, 0x00, 0x00, 0x05, 0x6f, 0x00, 0xc0, 0x05, 0x6f, 0x00, 0x00, 0x07, + 0x6f, 0x00, 0x00, 0x0a, 0x83, 0xc2, 0x05, 0x00, 0x93, 0x85, 0x15, 0x00, + 0x17, 0x03, 0x00, 0x00, 0x13, 0x03, 0xc3, 0xfd, 0xb3, 0x82, 0x62, 0x00, + 0x67, 0x80, 0x02, 0x00, 0x03, 0xc3, 0x05, 0x00, 0x93, 0x85, 0x15, 0x00, + 0x83, 0x22, 0x85, 0x04, 0xe3, 0xce, 0x02, 0xfe, 0x83, 0xc2, 0x05, 0x00, + 0x23, 0x24, 0x55, 0x04, 0x93, 0x85, 0x15, 0x00, 0x13, 0x03, 0xf3, 0xff, + 0xe3, 0x44, 0x60, 0xfe, 0x6f, 0xf0, 0x5f, 0xfc, 0x83, 0x22, 0x45, 0x07, + 0x93, 0xf2, 0x12, 0x00, 0xe3, 0x8c, 0x02, 0xfe, 0x6f, 0xf0, 0x5f, 0xfb, + 0x83, 0xc2, 0x05, 0x00, 0xb3, 0x82, 0xa2, 0x00, 0x03, 0xc3, 0x15, 0x00, + 0x93, 0x85, 0x25, 0x00, 0x23, 0xa0, 0x62, 0x00, 0x6f, 0xf0, 0xdf, 0xf9, + 0x13, 0x06, 0x50, 0x00, 0xef, 0x00, 0x80, 0x01, 0x13, 0x06, 0x00, 0x00, + 0xef, 0x00, 0x00, 0x01, 0x93, 0x72, 0x16, 0x00, 0xe3, 0x9a, 0x02, 0xfe, + 0x6f, 0xf0, 0x1f, 0xf8, 0x83, 0x22, 0x85, 0x04, 0xe3, 0xce, 0x02, 0xfe, + 0x23, 0x24, 0xc5, 0x04, 0x03, 0x26, 0xc5, 0x04, 0xe3, 0x4e, 0x06, 0xfe, + 0x67, 0x80, 0x00, 0x00, 0x83, 0x22, 0x05, 0x04, 0x13, 0x03, 0x70, 0xff, + 0xb3, 0xf2, 0x62, 0x00, 0x03, 0xc3, 0x05, 0x00, 0x93, 0x85, 0x15, 0x00, + 0xb3, 0xe2, 0x62, 0x00, 0x23, 0x20, 0x55, 0x04, 0x6f, 0xf0, 0x9f, 0xf4 }; #define STEP_EXIT 4 #define STEP_TX 8 @@ -598,7 +607,7 @@ struct algorithm_steps { uint8_t **steps; }; -struct algorithm_steps *as_new(unsigned size) +static struct algorithm_steps *as_new(unsigned size) { struct algorithm_steps *as = calloc(1, sizeof(struct algorithm_steps)); as->size = size; @@ -606,7 +615,7 @@ struct algorithm_steps *as_new(unsigned size) return as; } -struct algorithm_steps *as_delete(struct algorithm_steps *as) +static struct algorithm_steps *as_delete(struct algorithm_steps *as) { for (unsigned step = 0; step < as->used; step++) { free(as->steps[step]); @@ -616,7 +625,7 @@ struct algorithm_steps *as_delete(struct algorithm_steps *as) return NULL; } -int as_empty(struct algorithm_steps *as) +static int as_empty(struct algorithm_steps *as) { for (unsigned s = 0; s < as->used; s++) { if (as->steps[s][0] != STEP_NOP) @@ -625,8 +634,8 @@ int as_empty(struct algorithm_steps *as) return 1; } -// Return size of compiled program. -unsigned as_compile(struct algorithm_steps *as, uint8_t *target, +/* Return size of compiled program. */ +static unsigned as_compile(struct algorithm_steps *as, uint8_t *target, unsigned target_size) { unsigned offset = 0; @@ -684,16 +693,15 @@ unsigned as_compile(struct algorithm_steps *as, uint8_t *target, LOG_DEBUG("%d-byte program:", offset); for (unsigned i = 0; i < offset;) { char buf[80]; - for (unsigned x = 0; i < offset && x < 16; x++, i++) { + for (unsigned x = 0; i < offset && x < 16; x++, i++) sprintf(buf + x*3, "%02x ", target[i]); - } LOG_DEBUG("%s", buf); } return offset; } -void as_add_tx(struct algorithm_steps *as, unsigned count, const uint8_t *data) +static void as_add_tx(struct algorithm_steps *as, unsigned count, const uint8_t *data) { LOG_DEBUG("count=%d", count); while (count > 0) { @@ -709,14 +717,14 @@ void as_add_tx(struct algorithm_steps *as, unsigned count, const uint8_t *data) } } -void as_add_tx1(struct algorithm_steps *as, uint8_t byte) +static void as_add_tx1(struct algorithm_steps *as, uint8_t byte) { uint8_t data[1]; data[0] = byte; as_add_tx(as, 1, data); } -void as_add_write_reg(struct algorithm_steps *as, uint8_t offset, uint8_t data) +static void as_add_write_reg(struct algorithm_steps *as, uint8_t offset, uint8_t data) { assert(as->used < as->size); as->steps[as->used] = malloc(3); @@ -726,7 +734,7 @@ void as_add_write_reg(struct algorithm_steps *as, uint8_t offset, uint8_t data) as->used++; } -void as_add_txwm_wait(struct algorithm_steps *as) +static void as_add_txwm_wait(struct algorithm_steps *as) { assert(as->used < as->size); as->steps[as->used] = malloc(1); @@ -734,7 +742,7 @@ void as_add_txwm_wait(struct algorithm_steps *as) as->used++; } -void as_add_wip_wait(struct algorithm_steps *as) +static void as_add_wip_wait(struct algorithm_steps *as) { assert(as->used < as->size); as->steps[as->used] = malloc(1); @@ -742,7 +750,7 @@ void as_add_wip_wait(struct algorithm_steps *as) as->used++; } -void as_add_set_dir(struct algorithm_steps *as, bool dir) +static void as_add_set_dir(struct algorithm_steps *as, bool dir) { assert(as->used < as->size); as->steps[as->used] = malloc(2); @@ -771,7 +779,7 @@ static int steps_add_buffer_write(struct algorithm_steps *as, as_add_txwm_wait(as); as_add_write_reg(as, FESPI_REG_CSMODE, FESPI_CSMODE_AUTO); - // fespi_wip() + /* fespi_wip() */ as_add_set_dir(as, FESPI_DIR_RX); as_add_write_reg(as, FESPI_REG_CSMODE, FESPI_CSMODE_HOLD); as_add_wip_wait(as); @@ -788,19 +796,20 @@ static int steps_execute(struct algorithm_steps *as, struct target *target = bank->target; struct fespi_flash_bank *fespi_info = bank->driver_priv; uint32_t ctrl_base = fespi_info->ctrl_base; - uint8_t *data_buf = malloc(data_wa->size); int xlen = riscv_xlen(target); struct reg_param reg_params[2]; - init_reg_param(®_params[0], "x10", xlen, PARAM_OUT); - init_reg_param(®_params[1], "x11", xlen, PARAM_OUT); + init_reg_param(®_params[0], "a0", xlen, PARAM_OUT); + init_reg_param(®_params[1], "a1", xlen, PARAM_OUT); buf_set_u64(reg_params[0].value, 0, xlen, ctrl_base); buf_set_u64(reg_params[1].value, 0, xlen, data_wa->address); while (!as_empty(as)) { keep_alive(); + uint8_t *data_buf = malloc(data_wa->size); unsigned bytes = as_compile(as, data_buf, data_wa->size); int retval = target_write_buffer(target, data_wa->address, bytes, data_buf); + free(data_buf); if (retval != ERROR_OK) { LOG_ERROR("Failed to write data to 0x%" TARGET_PRIxADDR ": %d", data_wa->address, retval); @@ -908,11 +917,10 @@ static int fespi_write(struct flash_bank *bank, const uint8_t *buffer, cur_count = 4 - (offset & 3); if (cur_count > count) cur_count = count; - if (algorithm_wa) { + if (algorithm_wa) retval = steps_add_buffer_write(as, buffer, offset, cur_count); - } else { + else retval = slow_fespi_write_buffer(bank, buffer, offset, cur_count); - } if (retval != ERROR_OK) goto err; offset += cur_count; @@ -929,11 +937,10 @@ static int fespi_write(struct flash_bank *bank, const uint8_t *buffer, else cur_count = count & ~3; - if (algorithm_wa) { + if (algorithm_wa) retval = steps_add_buffer_write(as, buffer, offset, cur_count); - } else { + else retval = slow_fespi_write_buffer(bank, buffer, offset, cur_count); - } if (retval != ERROR_OK) goto err; @@ -945,18 +952,16 @@ static int fespi_write(struct flash_bank *bank, const uint8_t *buffer, /* buffer tail */ if (count > 0) { - if (algorithm_wa) { + if (algorithm_wa) retval = steps_add_buffer_write(as, buffer, offset, count); - } else { + else retval = slow_fespi_write_buffer(bank, buffer, offset, count); - } if (retval != ERROR_OK) goto err; } - if (algorithm_wa) { + if (algorithm_wa) retval = steps_execute(as, bank, algorithm_wa, data_wa); - } err: if (algorithm_wa) { @@ -964,6 +969,8 @@ err: target_free_working_area(target, algorithm_wa); } + as_delete(as); + /* Switch to HW mode before return to prompt */ FESPI_ENABLE_HW_MODE(); return retval; @@ -1041,27 +1048,27 @@ static int fespi_probe(struct flash_bank *bank) fespi_info->probed = 0; if (fespi_info->ctrl_base == 0) { - for (target_device = target_devices ; target_device->name ; ++target_device) - if (target_device->tap_idcode == target->tap->idcode) - break; + for (target_device = target_devices ; target_device->name ; ++target_device) + if (target_device->tap_idcode == target->tap->idcode) + break; - if (!target_device->name) { - LOG_ERROR("Device ID 0x%" PRIx32 " is not known as FESPI capable", - target->tap->idcode); - return ERROR_FAIL; - } + if (!target_device->name) { + LOG_ERROR("Device ID 0x%" PRIx32 " is not known as FESPI capable", + target->tap->idcode); + return ERROR_FAIL; + } - fespi_info->ctrl_base = target_device->ctrl_base; + fespi_info->ctrl_base = target_device->ctrl_base; - LOG_DEBUG("Valid FESPI on device %s at address 0x%" PRIx32, - target_device->name, bank->base); + LOG_DEBUG("Valid FESPI on device %s at address 0x%" PRIx32, + target_device->name, bank->base); } else { LOG_DEBUG("Assuming FESPI as specified at address 0x%x with ctrl at 0x%x", fespi_info->ctrl_base, bank->base); } - ctrl_base = fespi_info->ctrl_base; + ctrl_base = fespi_info->ctrl_base; /* read and decode flash ID; returns in SW mode */ FESPI_WRITE_REG(FESPI_REG_TXCTRL, FESPI_TXWM(1)); diff --git a/src/flash/nor/kinetis.c b/src/flash/nor/kinetis.c index 4ef438507..5c0ffbd6f 100644 --- a/src/flash/nor/kinetis.c +++ b/src/flash/nor/kinetis.c @@ -1959,7 +1959,7 @@ static int kinetis_probe_chip(struct kinetis_chip *k_chip) unsigned cpu_mhz = 120; unsigned idx; bool use_nvm_marking = false; - char flash_marking[8], nvm_marking[2]; + char flash_marking[11], nvm_marking[2]; char name[40]; k_chip->probed = false; @@ -2126,6 +2126,7 @@ static int kinetis_probe_chip(struct kinetis_chip *k_chip) case KINETIS_SDID_FAMILYID_K6X | KINETIS_SDID_SUBFAMID_KX1: /* errata 7534 - should be K63 */ case KINETIS_SDID_FAMILYID_K6X | KINETIS_SDID_SUBFAMID_KX2: /* errata 7534 - should be K64 */ subfamid += 2; /* errata 7534 fix */ + /* fallthrough */ case KINETIS_SDID_FAMILYID_K6X | KINETIS_SDID_SUBFAMID_KX3: /* K63FN1M0 */ case KINETIS_SDID_FAMILYID_K6X | KINETIS_SDID_SUBFAMID_KX4: diff --git a/src/flash/nor/nrf51.c b/src/flash/nor/nrf5.c similarity index 57% rename from src/flash/nor/nrf51.c rename to src/flash/nor/nrf5.c index 7b7acf479..11e57291c 100644 --- a/src/flash/nor/nrf51.c +++ b/src/flash/nor/nrf5.c @@ -28,97 +28,97 @@ #include enum { - NRF51_FLASH_BASE = 0x00000000, + NRF5_FLASH_BASE = 0x00000000, }; -enum nrf51_ficr_registers { - NRF51_FICR_BASE = 0x10000000, /* Factory Information Configuration Registers */ +enum nrf5_ficr_registers { + NRF5_FICR_BASE = 0x10000000, /* Factory Information Configuration Registers */ -#define NRF51_FICR_REG(offset) (NRF51_FICR_BASE + offset) +#define NRF5_FICR_REG(offset) (NRF5_FICR_BASE + offset) - NRF51_FICR_CODEPAGESIZE = NRF51_FICR_REG(0x010), - NRF51_FICR_CODESIZE = NRF51_FICR_REG(0x014), - NRF51_FICR_CLENR0 = NRF51_FICR_REG(0x028), - NRF51_FICR_PPFC = NRF51_FICR_REG(0x02C), - NRF51_FICR_NUMRAMBLOCK = NRF51_FICR_REG(0x034), - NRF51_FICR_SIZERAMBLOCK0 = NRF51_FICR_REG(0x038), - NRF51_FICR_SIZERAMBLOCK1 = NRF51_FICR_REG(0x03C), - NRF51_FICR_SIZERAMBLOCK2 = NRF51_FICR_REG(0x040), - NRF51_FICR_SIZERAMBLOCK3 = NRF51_FICR_REG(0x044), - NRF51_FICR_CONFIGID = NRF51_FICR_REG(0x05C), - NRF51_FICR_DEVICEID0 = NRF51_FICR_REG(0x060), - NRF51_FICR_DEVICEID1 = NRF51_FICR_REG(0x064), - NRF51_FICR_ER0 = NRF51_FICR_REG(0x080), - NRF51_FICR_ER1 = NRF51_FICR_REG(0x084), - NRF51_FICR_ER2 = NRF51_FICR_REG(0x088), - NRF51_FICR_ER3 = NRF51_FICR_REG(0x08C), - NRF51_FICR_IR0 = NRF51_FICR_REG(0x090), - NRF51_FICR_IR1 = NRF51_FICR_REG(0x094), - NRF51_FICR_IR2 = NRF51_FICR_REG(0x098), - NRF51_FICR_IR3 = NRF51_FICR_REG(0x09C), - NRF51_FICR_DEVICEADDRTYPE = NRF51_FICR_REG(0x0A0), - NRF51_FICR_DEVICEADDR0 = NRF51_FICR_REG(0x0A4), - NRF51_FICR_DEVICEADDR1 = NRF51_FICR_REG(0x0A8), - NRF51_FICR_OVERRIDEN = NRF51_FICR_REG(0x0AC), - NRF51_FICR_NRF_1MBIT0 = NRF51_FICR_REG(0x0B0), - NRF51_FICR_NRF_1MBIT1 = NRF51_FICR_REG(0x0B4), - NRF51_FICR_NRF_1MBIT2 = NRF51_FICR_REG(0x0B8), - NRF51_FICR_NRF_1MBIT3 = NRF51_FICR_REG(0x0BC), - NRF51_FICR_NRF_1MBIT4 = NRF51_FICR_REG(0x0C0), - NRF51_FICR_BLE_1MBIT0 = NRF51_FICR_REG(0x0EC), - NRF51_FICR_BLE_1MBIT1 = NRF51_FICR_REG(0x0F0), - NRF51_FICR_BLE_1MBIT2 = NRF51_FICR_REG(0x0F4), - NRF51_FICR_BLE_1MBIT3 = NRF51_FICR_REG(0x0F8), - NRF51_FICR_BLE_1MBIT4 = NRF51_FICR_REG(0x0FC), + NRF5_FICR_CODEPAGESIZE = NRF5_FICR_REG(0x010), + NRF5_FICR_CODESIZE = NRF5_FICR_REG(0x014), + NRF5_FICR_CLENR0 = NRF5_FICR_REG(0x028), + NRF5_FICR_PPFC = NRF5_FICR_REG(0x02C), + NRF5_FICR_NUMRAMBLOCK = NRF5_FICR_REG(0x034), + NRF5_FICR_SIZERAMBLOCK0 = NRF5_FICR_REG(0x038), + NRF5_FICR_SIZERAMBLOCK1 = NRF5_FICR_REG(0x03C), + NRF5_FICR_SIZERAMBLOCK2 = NRF5_FICR_REG(0x040), + NRF5_FICR_SIZERAMBLOCK3 = NRF5_FICR_REG(0x044), + NRF5_FICR_CONFIGID = NRF5_FICR_REG(0x05C), + NRF5_FICR_DEVICEID0 = NRF5_FICR_REG(0x060), + NRF5_FICR_DEVICEID1 = NRF5_FICR_REG(0x064), + NRF5_FICR_ER0 = NRF5_FICR_REG(0x080), + NRF5_FICR_ER1 = NRF5_FICR_REG(0x084), + NRF5_FICR_ER2 = NRF5_FICR_REG(0x088), + NRF5_FICR_ER3 = NRF5_FICR_REG(0x08C), + NRF5_FICR_IR0 = NRF5_FICR_REG(0x090), + NRF5_FICR_IR1 = NRF5_FICR_REG(0x094), + NRF5_FICR_IR2 = NRF5_FICR_REG(0x098), + NRF5_FICR_IR3 = NRF5_FICR_REG(0x09C), + NRF5_FICR_DEVICEADDRTYPE = NRF5_FICR_REG(0x0A0), + NRF5_FICR_DEVICEADDR0 = NRF5_FICR_REG(0x0A4), + NRF5_FICR_DEVICEADDR1 = NRF5_FICR_REG(0x0A8), + NRF5_FICR_OVERRIDEN = NRF5_FICR_REG(0x0AC), + NRF5_FICR_NRF_1MBIT0 = NRF5_FICR_REG(0x0B0), + NRF5_FICR_NRF_1MBIT1 = NRF5_FICR_REG(0x0B4), + NRF5_FICR_NRF_1MBIT2 = NRF5_FICR_REG(0x0B8), + NRF5_FICR_NRF_1MBIT3 = NRF5_FICR_REG(0x0BC), + NRF5_FICR_NRF_1MBIT4 = NRF5_FICR_REG(0x0C0), + NRF5_FICR_BLE_1MBIT0 = NRF5_FICR_REG(0x0EC), + NRF5_FICR_BLE_1MBIT1 = NRF5_FICR_REG(0x0F0), + NRF5_FICR_BLE_1MBIT2 = NRF5_FICR_REG(0x0F4), + NRF5_FICR_BLE_1MBIT3 = NRF5_FICR_REG(0x0F8), + NRF5_FICR_BLE_1MBIT4 = NRF5_FICR_REG(0x0FC), }; -enum nrf51_uicr_registers { - NRF51_UICR_BASE = 0x10001000, /* User Information +enum nrf5_uicr_registers { + NRF5_UICR_BASE = 0x10001000, /* User Information * Configuration Regsters */ - NRF51_UICR_SIZE = 0x100, + NRF5_UICR_SIZE = 0x100, -#define NRF51_UICR_REG(offset) (NRF51_UICR_BASE + offset) +#define NRF5_UICR_REG(offset) (NRF5_UICR_BASE + offset) - NRF51_UICR_CLENR0 = NRF51_UICR_REG(0x000), - NRF51_UICR_RBPCONF = NRF51_UICR_REG(0x004), - NRF51_UICR_XTALFREQ = NRF51_UICR_REG(0x008), - NRF51_UICR_FWID = NRF51_UICR_REG(0x010), + NRF5_UICR_CLENR0 = NRF5_UICR_REG(0x000), + NRF5_UICR_RBPCONF = NRF5_UICR_REG(0x004), + NRF5_UICR_XTALFREQ = NRF5_UICR_REG(0x008), + NRF5_UICR_FWID = NRF5_UICR_REG(0x010), }; -enum nrf51_nvmc_registers { - NRF51_NVMC_BASE = 0x4001E000, /* Non-Volatile Memory +enum nrf5_nvmc_registers { + NRF5_NVMC_BASE = 0x4001E000, /* Non-Volatile Memory * Controller Regsters */ -#define NRF51_NVMC_REG(offset) (NRF51_NVMC_BASE + offset) +#define NRF5_NVMC_REG(offset) (NRF5_NVMC_BASE + offset) - NRF51_NVMC_READY = NRF51_NVMC_REG(0x400), - NRF51_NVMC_CONFIG = NRF51_NVMC_REG(0x504), - NRF51_NVMC_ERASEPAGE = NRF51_NVMC_REG(0x508), - NRF51_NVMC_ERASEALL = NRF51_NVMC_REG(0x50C), - NRF51_NVMC_ERASEUICR = NRF51_NVMC_REG(0x514), + NRF5_NVMC_READY = NRF5_NVMC_REG(0x400), + NRF5_NVMC_CONFIG = NRF5_NVMC_REG(0x504), + NRF5_NVMC_ERASEPAGE = NRF5_NVMC_REG(0x508), + NRF5_NVMC_ERASEALL = NRF5_NVMC_REG(0x50C), + NRF5_NVMC_ERASEUICR = NRF5_NVMC_REG(0x514), }; -enum nrf51_nvmc_config_bits { - NRF51_NVMC_CONFIG_REN = 0x00, - NRF51_NVMC_CONFIG_WEN = 0x01, - NRF51_NVMC_CONFIG_EEN = 0x02, +enum nrf5_nvmc_config_bits { + NRF5_NVMC_CONFIG_REN = 0x00, + NRF5_NVMC_CONFIG_WEN = 0x01, + NRF5_NVMC_CONFIG_EEN = 0x02, }; -struct nrf51_info { +struct nrf5_info { uint32_t code_page_size; struct { bool probed; int (*write) (struct flash_bank *bank, - struct nrf51_info *chip, + struct nrf5_info *chip, const uint8_t *buffer, uint32_t offset, uint32_t count); } bank[2]; struct target *target; }; -struct nrf51_device_spec { +struct nrf5_device_spec { uint16_t hwid; const char *part; const char *variant; @@ -126,6 +126,15 @@ struct nrf51_device_spec { unsigned int flash_size_kb; }; +#define NRF5_DEVICE_DEF(id, pt, var, bcode, fsize) \ +{ \ +.hwid = (id), \ +.part = pt, \ +.variant = var, \ +.build_code = bcode, \ +.flash_size_kb = (fsize), \ +} + /* The known devices table below is derived from the "nRF51 Series * Compatibility Matrix" document, which can be found by searching for * ATTN-51 on the Nordic Semi website: @@ -138,294 +147,74 @@ struct nrf51_device_spec { * shown as Gx0, Bx0, etc. In these cases the HWID in the matrix is * for x==0, x!=0 means different (unspecified) HWIDs. */ -static const struct nrf51_device_spec nrf51_known_devices_table[] = { +static const struct nrf5_device_spec nrf5_known_devices_table[] = { /* nRF51822 Devices (IC rev 1). */ - { - .hwid = 0x001D, - .part = "51822", - .variant = "QFAA", - .build_code = "CA/C0", - .flash_size_kb = 256, - }, - { - .hwid = 0x0026, - .part = "51822", - .variant = "QFAB", - .build_code = "AA", - .flash_size_kb = 128, - }, - { - .hwid = 0x0027, - .part = "51822", - .variant = "QFAB", - .build_code = "A0", - .flash_size_kb = 128, - }, - { - .hwid = 0x0020, - .part = "51822", - .variant = "CEAA", - .build_code = "BA", - .flash_size_kb = 256, - }, - { - .hwid = 0x002F, - .part = "51822", - .variant = "CEAA", - .build_code = "B0", - .flash_size_kb = 256, - }, + NRF5_DEVICE_DEF(0x001D, "51822", "QFAA", "CA/C0", 256), + NRF5_DEVICE_DEF(0x0026, "51822", "QFAB", "AA", 128), + NRF5_DEVICE_DEF(0x0027, "51822", "QFAB", "A0", 128), + NRF5_DEVICE_DEF(0x0020, "51822", "CEAA", "BA", 256), + NRF5_DEVICE_DEF(0x002F, "51822", "CEAA", "B0", 256), /* nRF51822 Devices (IC rev 2). */ - { - .hwid = 0x002A, - .part = "51822", - .variant = "QFAA", - .build_code = "FA0", - .flash_size_kb = 256, - }, - { - .hwid = 0x0044, - .part = "51822", - .variant = "QFAA", - .build_code = "GC0", - .flash_size_kb = 256, - }, - { - .hwid = 0x003C, - .part = "51822", - .variant = "QFAA", - .build_code = "G0", - .flash_size_kb = 256, - }, - { - .hwid = 0x0057, - .part = "51822", - .variant = "QFAA", - .build_code = "G2", - .flash_size_kb = 256, - }, - { - .hwid = 0x0058, - .part = "51822", - .variant = "QFAA", - .build_code = "G3", - .flash_size_kb = 256, - }, - { - .hwid = 0x004C, - .part = "51822", - .variant = "QFAB", - .build_code = "B0", - .flash_size_kb = 128, - }, - { - .hwid = 0x0040, - .part = "51822", - .variant = "CEAA", - .build_code = "CA0", - .flash_size_kb = 256, - }, - { - .hwid = 0x0047, - .part = "51822", - .variant = "CEAA", - .build_code = "DA0", - .flash_size_kb = 256, - }, - { - .hwid = 0x004D, - .part = "51822", - .variant = "CEAA", - .build_code = "D00", - .flash_size_kb = 256, - }, + NRF5_DEVICE_DEF(0x002A, "51822", "QFAA", "FA0", 256), + NRF5_DEVICE_DEF(0x0044, "51822", "QFAA", "GC0", 256), + NRF5_DEVICE_DEF(0x003C, "51822", "QFAA", "G0", 256), + NRF5_DEVICE_DEF(0x0057, "51822", "QFAA", "G2", 256), + NRF5_DEVICE_DEF(0x0058, "51822", "QFAA", "G3", 256), + NRF5_DEVICE_DEF(0x004C, "51822", "QFAB", "B0", 128), + NRF5_DEVICE_DEF(0x0040, "51822", "CEAA", "CA0", 256), + NRF5_DEVICE_DEF(0x0047, "51822", "CEAA", "DA0", 256), + NRF5_DEVICE_DEF(0x004D, "51822", "CEAA", "D00", 256), /* nRF51822 Devices (IC rev 3). */ - { - .hwid = 0x0072, - .part = "51822", - .variant = "QFAA", - .build_code = "H0", - .flash_size_kb = 256, - }, - { - .hwid = 0x007B, - .part = "51822", - .variant = "QFAB", - .build_code = "C0", - .flash_size_kb = 128, - }, - { - .hwid = 0x0083, - .part = "51822", - .variant = "QFAC", - .build_code = "A0", - .flash_size_kb = 256, - }, - { - .hwid = 0x0084, - .part = "51822", - .variant = "QFAC", - .build_code = "A1", - .flash_size_kb = 256, - }, - { - .hwid = 0x007D, - .part = "51822", - .variant = "CDAB", - .build_code = "A0", - .flash_size_kb = 128, - }, - { - .hwid = 0x0079, - .part = "51822", - .variant = "CEAA", - .build_code = "E0", - .flash_size_kb = 256, - }, - { - .hwid = 0x0087, - .part = "51822", - .variant = "CFAC", - .build_code = "A0", - .flash_size_kb = 256, - }, - { - .hwid = 0x008F, - .part = "51822", - .variant = "QFAA", - .build_code = "H1", - .flash_size_kb = 256, - }, + NRF5_DEVICE_DEF(0x0072, "51822", "QFAA", "H0", 256), + NRF5_DEVICE_DEF(0x007B, "51822", "QFAB", "C0", 128), + NRF5_DEVICE_DEF(0x0083, "51822", "QFAC", "A0", 256), + NRF5_DEVICE_DEF(0x0084, "51822", "QFAC", "A1", 256), + NRF5_DEVICE_DEF(0x007D, "51822", "CDAB", "A0", 128), + NRF5_DEVICE_DEF(0x0079, "51822", "CEAA", "E0", 256), + NRF5_DEVICE_DEF(0x0087, "51822", "CFAC", "A0", 256), /* nRF51422 Devices (IC rev 1). */ - { - .hwid = 0x001E, - .part = "51422", - .variant = "QFAA", - .build_code = "CA", - .flash_size_kb = 256, - }, - { - .hwid = 0x0024, - .part = "51422", - .variant = "QFAA", - .build_code = "C0", - .flash_size_kb = 256, - }, - { - .hwid = 0x0031, - .part = "51422", - .variant = "CEAA", - .build_code = "A0A", - .flash_size_kb = 256, - }, + NRF5_DEVICE_DEF(0x001E, "51422", "QFAA", "CA", 256), + NRF5_DEVICE_DEF(0x0024, "51422", "QFAA", "C0", 256), + NRF5_DEVICE_DEF(0x0031, "51422", "CEAA", "A0A", 256), /* nRF51422 Devices (IC rev 2). */ - { - .hwid = 0x002D, - .part = "51422", - .variant = "QFAA", - .build_code = "DAA", - .flash_size_kb = 256, - }, - { - .hwid = 0x002E, - .part = "51422", - .variant = "QFAA", - .build_code = "E0", - .flash_size_kb = 256, - }, - { - .hwid = 0x0061, - .part = "51422", - .variant = "QFAB", - .build_code = "A00", - .flash_size_kb = 128, - }, - { - .hwid = 0x0050, - .part = "51422", - .variant = "CEAA", - .build_code = "B0", - .flash_size_kb = 256, - }, + NRF5_DEVICE_DEF(0x002D, "51422", "QFAA", "DAA", 256), + NRF5_DEVICE_DEF(0x002E, "51422", "QFAA", "E0", 256), + NRF5_DEVICE_DEF(0x0061, "51422", "QFAB", "A00", 128), + NRF5_DEVICE_DEF(0x0050, "51422", "CEAA", "B0", 256), /* nRF51422 Devices (IC rev 3). */ - { - .hwid = 0x0073, - .part = "51422", - .variant = "QFAA", - .build_code = "F0", - .flash_size_kb = 256, - }, - { - .hwid = 0x007C, - .part = "51422", - .variant = "QFAB", - .build_code = "B0", - .flash_size_kb = 128, - }, - { - .hwid = 0x0085, - .part = "51422", - .variant = "QFAC", - .build_code = "A0", - .flash_size_kb = 256, - }, - { - .hwid = 0x0086, - .part = "51422", - .variant = "QFAC", - .build_code = "A1", - .flash_size_kb = 256, - }, - { - .hwid = 0x007E, - .part = "51422", - .variant = "CDAB", - .build_code = "A0", - .flash_size_kb = 128, - }, - { - .hwid = 0x007A, - .part = "51422", - .variant = "CEAA", - .build_code = "C0", - .flash_size_kb = 256, - }, - { - .hwid = 0x0088, - .part = "51422", - .variant = "CFAC", - .build_code = "A0", - .flash_size_kb = 256, - }, + NRF5_DEVICE_DEF(0x0073, "51422", "QFAA", "F0", 256), + NRF5_DEVICE_DEF(0x007C, "51422", "QFAB", "B0", 128), + NRF5_DEVICE_DEF(0x0085, "51422", "QFAC", "A0", 256), + NRF5_DEVICE_DEF(0x0086, "51422", "QFAC", "A1", 256), + NRF5_DEVICE_DEF(0x007E, "51422", "CDAB", "A0", 128), + NRF5_DEVICE_DEF(0x007A, "51422", "CEAA", "C0", 256), + NRF5_DEVICE_DEF(0x0088, "51422", "CFAC", "A0", 256), + + /* nRF52832 Devices */ + NRF5_DEVICE_DEF(0x00C7, "52832", "QFAA", "B0", 512), /* Some early nRF51-DK (PCA10028) & nRF51-Dongle (PCA10031) boards with built-in jlink seem to use engineering samples not listed in the nRF51 Series Compatibility Matrix V1.0. */ - { - .hwid = 0x0071, - .part = "51822", - .variant = "QFAC", - .build_code = "AB", - .flash_size_kb = 256, - }, + NRF5_DEVICE_DEF(0x0071, "51822", "QFAC", "AB", 256), }; -static int nrf51_bank_is_probed(struct flash_bank *bank) +static int nrf5_bank_is_probed(struct flash_bank *bank) { - struct nrf51_info *chip = bank->driver_priv; + struct nrf5_info *chip = bank->driver_priv; assert(chip != NULL); return chip->bank[bank->bank_number].probed; } -static int nrf51_probe(struct flash_bank *bank); +static int nrf5_probe(struct flash_bank *bank); -static int nrf51_get_probed_chip_if_halted(struct flash_bank *bank, struct nrf51_info **chip) +static int nrf5_get_probed_chip_if_halted(struct flash_bank *bank, struct nrf5_info **chip) { if (bank->target->state != TARGET_HALTED) { LOG_ERROR("Target not halted"); @@ -434,23 +223,23 @@ static int nrf51_get_probed_chip_if_halted(struct flash_bank *bank, struct nrf51 *chip = bank->driver_priv; - int probed = nrf51_bank_is_probed(bank); + int probed = nrf5_bank_is_probed(bank); if (probed < 0) return probed; else if (!probed) - return nrf51_probe(bank); + return nrf5_probe(bank); else return ERROR_OK; } -static int nrf51_wait_for_nvmc(struct nrf51_info *chip) +static int nrf5_wait_for_nvmc(struct nrf5_info *chip) { uint32_t ready; int res; int timeout = 100; do { - res = target_read_u32(chip->target, NRF51_NVMC_READY, &ready); + res = target_read_u32(chip->target, NRF5_NVMC_READY, &ready); if (res != ERROR_OK) { LOG_ERROR("Couldn't read NVMC_READY register"); return res; @@ -466,12 +255,12 @@ static int nrf51_wait_for_nvmc(struct nrf51_info *chip) return ERROR_FLASH_BUSY; } -static int nrf51_nvmc_erase_enable(struct nrf51_info *chip) +static int nrf5_nvmc_erase_enable(struct nrf5_info *chip) { int res; res = target_write_u32(chip->target, - NRF51_NVMC_CONFIG, - NRF51_NVMC_CONFIG_EEN); + NRF5_NVMC_CONFIG, + NRF5_NVMC_CONFIG_EEN); if (res != ERROR_OK) { LOG_ERROR("Failed to enable erase operation"); @@ -482,19 +271,19 @@ static int nrf51_nvmc_erase_enable(struct nrf51_info *chip) According to NVMC examples in Nordic SDK busy status must be checked after writing to NVMC_CONFIG */ - res = nrf51_wait_for_nvmc(chip); + res = nrf5_wait_for_nvmc(chip); if (res != ERROR_OK) LOG_ERROR("Erase enable did not complete"); return res; } -static int nrf51_nvmc_write_enable(struct nrf51_info *chip) +static int nrf5_nvmc_write_enable(struct nrf5_info *chip) { int res; res = target_write_u32(chip->target, - NRF51_NVMC_CONFIG, - NRF51_NVMC_CONFIG_WEN); + NRF5_NVMC_CONFIG, + NRF5_NVMC_CONFIG_WEN); if (res != ERROR_OK) { LOG_ERROR("Failed to enable write operation"); @@ -505,19 +294,19 @@ static int nrf51_nvmc_write_enable(struct nrf51_info *chip) According to NVMC examples in Nordic SDK busy status must be checked after writing to NVMC_CONFIG */ - res = nrf51_wait_for_nvmc(chip); + res = nrf5_wait_for_nvmc(chip); if (res != ERROR_OK) LOG_ERROR("Write enable did not complete"); return res; } -static int nrf51_nvmc_read_only(struct nrf51_info *chip) +static int nrf5_nvmc_read_only(struct nrf5_info *chip) { int res; res = target_write_u32(chip->target, - NRF51_NVMC_CONFIG, - NRF51_NVMC_CONFIG_REN); + NRF5_NVMC_CONFIG, + NRF5_NVMC_CONFIG_REN); if (res != ERROR_OK) { LOG_ERROR("Failed to enable read-only operation"); @@ -527,19 +316,19 @@ static int nrf51_nvmc_read_only(struct nrf51_info *chip) According to NVMC examples in Nordic SDK busy status must be checked after writing to NVMC_CONFIG */ - res = nrf51_wait_for_nvmc(chip); + res = nrf5_wait_for_nvmc(chip); if (res != ERROR_OK) LOG_ERROR("Read only enable did not complete"); return res; } -static int nrf51_nvmc_generic_erase(struct nrf51_info *chip, +static int nrf5_nvmc_generic_erase(struct nrf5_info *chip, uint32_t erase_register, uint32_t erase_value) { int res; - res = nrf51_nvmc_erase_enable(chip); + res = nrf5_nvmc_erase_enable(chip); if (res != ERROR_OK) goto error; @@ -549,34 +338,34 @@ static int nrf51_nvmc_generic_erase(struct nrf51_info *chip, if (res != ERROR_OK) goto set_read_only; - res = nrf51_wait_for_nvmc(chip); + res = nrf5_wait_for_nvmc(chip); if (res != ERROR_OK) goto set_read_only; - return nrf51_nvmc_read_only(chip); + return nrf5_nvmc_read_only(chip); set_read_only: - nrf51_nvmc_read_only(chip); + nrf5_nvmc_read_only(chip); error: LOG_ERROR("Failed to erase reg: 0x%08"PRIx32" val: 0x%08"PRIx32, erase_register, erase_value); return ERROR_FAIL; } -static int nrf51_protect_check(struct flash_bank *bank) +static int nrf5_protect_check(struct flash_bank *bank) { int res; uint32_t clenr0; /* UICR cannot be write protected so just return early */ - if (bank->base == NRF51_UICR_BASE) + if (bank->base == NRF5_UICR_BASE) return ERROR_OK; - struct nrf51_info *chip = bank->driver_priv; + struct nrf5_info *chip = bank->driver_priv; assert(chip != NULL); - res = target_read_u32(chip->target, NRF51_FICR_CLENR0, + res = target_read_u32(chip->target, NRF5_FICR_CLENR0, &clenr0); if (res != ERROR_OK) { LOG_ERROR("Couldn't read code region 0 size[FICR]"); @@ -584,7 +373,7 @@ static int nrf51_protect_check(struct flash_bank *bank) } if (clenr0 == 0xFFFFFFFF) { - res = target_read_u32(chip->target, NRF51_UICR_CLENR0, + res = target_read_u32(chip->target, NRF5_UICR_CLENR0, &clenr0); if (res != ERROR_OK) { LOG_ERROR("Couldn't read code region 0 size[UICR]"); @@ -599,17 +388,17 @@ static int nrf51_protect_check(struct flash_bank *bank) return ERROR_OK; } -static int nrf51_protect(struct flash_bank *bank, int set, int first, int last) +static int nrf5_protect(struct flash_bank *bank, int set, int first, int last) { int res; uint32_t clenr0, ppfc; - struct nrf51_info *chip; + struct nrf5_info *chip; /* UICR cannot be write protected so just bail out early */ - if (bank->base == NRF51_UICR_BASE) + if (bank->base == NRF5_UICR_BASE) return ERROR_FAIL; - res = nrf51_get_probed_chip_if_halted(bank, &chip); + res = nrf5_get_probed_chip_if_halted(bank, &chip); if (res != ERROR_OK) return res; @@ -618,7 +407,7 @@ static int nrf51_protect(struct flash_bank *bank, int set, int first, int last) return ERROR_FAIL; } - res = target_read_u32(chip->target, NRF51_FICR_PPFC, + res = target_read_u32(chip->target, NRF5_FICR_PPFC, &ppfc); if (res != ERROR_OK) { LOG_ERROR("Couldn't read PPFC register"); @@ -630,7 +419,7 @@ static int nrf51_protect(struct flash_bank *bank, int set, int first, int last) return ERROR_FAIL; } - res = target_read_u32(chip->target, NRF51_UICR_CLENR0, + res = target_read_u32(chip->target, NRF5_UICR_CLENR0, &clenr0); if (res != ERROR_OK) { LOG_ERROR("Couldn't read code region 0 size[UICR]"); @@ -638,7 +427,7 @@ static int nrf51_protect(struct flash_bank *bank, int set, int first, int last) } if (clenr0 == 0xFFFFFFFF) { - res = target_write_u32(chip->target, NRF51_UICR_CLENR0, + res = target_write_u32(chip->target, NRF5_UICR_CLENR0, clenr0); if (res != ERROR_OK) { LOG_ERROR("Couldn't write code region 0 size[UICR]"); @@ -649,18 +438,18 @@ static int nrf51_protect(struct flash_bank *bank, int set, int first, int last) LOG_ERROR("You need to perform chip erase before changing the protection settings"); } - nrf51_protect_check(bank); + nrf5_protect_check(bank); return ERROR_OK; } -static int nrf51_probe(struct flash_bank *bank) +static int nrf5_probe(struct flash_bank *bank) { uint32_t hwid; int res; - struct nrf51_info *chip = bank->driver_priv; + struct nrf5_info *chip = bank->driver_priv; - res = target_read_u32(chip->target, NRF51_FICR_CONFIGID, &hwid); + res = target_read_u32(chip->target, NRF5_FICR_CONFIGID, &hwid); if (res != ERROR_OK) { LOG_ERROR("Couldn't read CONFIGID register"); return res; @@ -669,10 +458,10 @@ static int nrf51_probe(struct flash_bank *bank) hwid &= 0xFFFF; /* HWID is stored in the lower two * bytes of the CONFIGID register */ - const struct nrf51_device_spec *spec = NULL; - for (size_t i = 0; i < ARRAY_SIZE(nrf51_known_devices_table); i++) { - if (hwid == nrf51_known_devices_table[i].hwid) { - spec = &nrf51_known_devices_table[i]; + const struct nrf5_device_spec *spec = NULL; + for (size_t i = 0; i < ARRAY_SIZE(nrf5_known_devices_table); i++) { + if (hwid == nrf5_known_devices_table[i].hwid) { + spec = &nrf5_known_devices_table[i]; break; } } @@ -686,9 +475,9 @@ static int nrf51_probe(struct flash_bank *bank) LOG_WARNING("Unknown device (HWID 0x%08" PRIx32 ")", hwid); } - if (bank->base == NRF51_FLASH_BASE) { - /* The value stored in NRF51_FICR_CODEPAGESIZE is the number of bytes in one page of FLASH. */ - res = target_read_u32(chip->target, NRF51_FICR_CODEPAGESIZE, + if (bank->base == NRF5_FLASH_BASE) { + /* The value stored in NRF5_FICR_CODEPAGESIZE is the number of bytes in one page of FLASH. */ + res = target_read_u32(chip->target, NRF5_FICR_CODEPAGESIZE, &chip->code_page_size); if (res != ERROR_OK) { LOG_ERROR("Couldn't read code page size"); @@ -696,9 +485,9 @@ static int nrf51_probe(struct flash_bank *bank) } /* Note the register name is misleading, - * NRF51_FICR_CODESIZE is the number of pages in flash memory, not the number of bytes! */ + * NRF5_FICR_CODESIZE is the number of pages in flash memory, not the number of bytes! */ uint32_t num_sectors; - res = target_read_u32(chip->target, NRF51_FICR_CODESIZE, &num_sectors); + res = target_read_u32(chip->target, NRF5_FICR_CODESIZE, &num_sectors); if (res != ERROR_OK) { LOG_ERROR("Couldn't read code memory size"); return res; @@ -715,7 +504,7 @@ static int nrf51_probe(struct flash_bank *bank) if (!bank->sectors) return ERROR_FLASH_BANK_NOT_PROBED; - /* Fill out the sector information: all NRF51 sectors are the same size and + /* Fill out the sector information: all NRF5 sectors are the same size and * there is always a fixed number of them. */ for (int i = 0; i < bank->num_sectors; i++) { bank->sectors[i].size = chip->code_page_size; @@ -726,11 +515,11 @@ static int nrf51_probe(struct flash_bank *bank) bank->sectors[i].is_protected = -1; } - nrf51_protect_check(bank); + nrf5_protect_check(bank); chip->bank[0].probed = true; } else { - bank->size = NRF51_UICR_SIZE; + bank->size = NRF5_UICR_SIZE; bank->num_sectors = 1; bank->sectors = calloc(bank->num_sectors, sizeof((bank->sectors)[0])); @@ -750,21 +539,21 @@ static int nrf51_probe(struct flash_bank *bank) return ERROR_OK; } -static int nrf51_auto_probe(struct flash_bank *bank) +static int nrf5_auto_probe(struct flash_bank *bank) { - int probed = nrf51_bank_is_probed(bank); + int probed = nrf5_bank_is_probed(bank); if (probed < 0) return probed; else if (probed) return ERROR_OK; else - return nrf51_probe(bank); + return nrf5_probe(bank); } -static struct flash_sector *nrf51_find_sector_by_address(struct flash_bank *bank, uint32_t address) +static struct flash_sector *nrf5_find_sector_by_address(struct flash_bank *bank, uint32_t address) { - struct nrf51_info *chip = bank->driver_priv; + struct nrf5_info *chip = bank->driver_priv; for (int i = 0; i < bank->num_sectors; i++) if (bank->sectors[i].offset <= address && @@ -773,16 +562,16 @@ static struct flash_sector *nrf51_find_sector_by_address(struct flash_bank *bank return NULL; } -static int nrf51_erase_all(struct nrf51_info *chip) +static int nrf5_erase_all(struct nrf5_info *chip) { LOG_DEBUG("Erasing all non-volatile memory"); - return nrf51_nvmc_generic_erase(chip, - NRF51_NVMC_ERASEALL, + return nrf5_nvmc_generic_erase(chip, + NRF5_NVMC_ERASEALL, 0x00000001); } -static int nrf51_erase_page(struct flash_bank *bank, - struct nrf51_info *chip, +static int nrf5_erase_page(struct flash_bank *bank, + struct nrf5_info *chip, struct flash_sector *sector) { int res; @@ -793,9 +582,9 @@ static int nrf51_erase_page(struct flash_bank *bank, return ERROR_FAIL; } - if (bank->base == NRF51_UICR_BASE) { + if (bank->base == NRF5_UICR_BASE) { uint32_t ppfc; - res = target_read_u32(chip->target, NRF51_FICR_PPFC, + res = target_read_u32(chip->target, NRF5_FICR_PPFC, &ppfc); if (res != ERROR_OK) { LOG_ERROR("Couldn't read PPFC register"); @@ -813,14 +602,14 @@ static int nrf51_erase_page(struct flash_bank *bank, return ERROR_FAIL; } - res = nrf51_nvmc_generic_erase(chip, - NRF51_NVMC_ERASEUICR, + res = nrf5_nvmc_generic_erase(chip, + NRF5_NVMC_ERASEUICR, 0x00000001); } else { - res = nrf51_nvmc_generic_erase(chip, - NRF51_NVMC_ERASEPAGE, + res = nrf5_nvmc_generic_erase(chip, + NRF5_NVMC_ERASEPAGE, sector->offset); } @@ -830,7 +619,7 @@ static int nrf51_erase_page(struct flash_bank *bank, return res; } -static const uint8_t nrf51_flash_write_code[] = { +static const uint8_t nrf5_flash_write_code[] = { /* See contrib/loaders/flash/cortex-m0.S */ /* : */ 0x0d, 0x68, /* ldr r5, [r1, #0] */ @@ -855,13 +644,13 @@ static const uint8_t nrf51_flash_write_code[] = { /* Start a low level flash write for the specified region */ -static int nrf51_ll_flash_write(struct nrf51_info *chip, uint32_t offset, const uint8_t *buffer, uint32_t bytes) +static int nrf5_ll_flash_write(struct nrf5_info *chip, uint32_t offset, const uint8_t *buffer, uint32_t bytes) { struct target *target = chip->target; uint32_t buffer_size = 8192; struct working_area *write_algorithm; struct working_area *source; - uint32_t address = NRF51_FLASH_BASE + offset; + uint32_t address = NRF5_FLASH_BASE + offset; struct reg_param reg_params[4]; struct armv7m_algorithm armv7m_info; int retval = ERROR_OK; @@ -871,7 +660,7 @@ static int nrf51_ll_flash_write(struct nrf51_info *chip, uint32_t offset, const assert(bytes % 4 == 0); /* allocate working area with flash programming code */ - if (target_alloc_working_area(target, sizeof(nrf51_flash_write_code), + if (target_alloc_working_area(target, sizeof(nrf5_flash_write_code), &write_algorithm) != ERROR_OK) { LOG_WARNING("no working area available, falling back to slow memory writes"); @@ -880,7 +669,7 @@ static int nrf51_ll_flash_write(struct nrf51_info *chip, uint32_t offset, const if (retval != ERROR_OK) return retval; - retval = nrf51_wait_for_nvmc(chip); + retval = nrf5_wait_for_nvmc(chip); if (retval != ERROR_OK) return retval; @@ -893,11 +682,11 @@ static int nrf51_ll_flash_write(struct nrf51_info *chip, uint32_t offset, const LOG_WARNING("using fast async flash loader. This is currently supported"); LOG_WARNING("only with ST-Link and CMSIS-DAP. If you have issues, add"); - LOG_WARNING("\"set WORKAREASIZE 0\" before sourcing nrf51.cfg to disable it"); + LOG_WARNING("\"set WORKAREASIZE 0\" before sourcing nrf51.cfg/nrf52.cfg to disable it"); retval = target_write_buffer(target, write_algorithm->address, - sizeof(nrf51_flash_write_code), - nrf51_flash_write_code); + sizeof(nrf5_flash_write_code), + nrf5_flash_write_code); if (retval != ERROR_OK) return retval; @@ -948,10 +737,10 @@ static int nrf51_ll_flash_write(struct nrf51_info *chip, uint32_t offset, const /* Check and erase flash sectors in specified range then start a low level page write. start/end must be sector aligned. */ -static int nrf51_write_pages(struct flash_bank *bank, uint32_t start, uint32_t end, const uint8_t *buffer) +static int nrf5_write_pages(struct flash_bank *bank, uint32_t start, uint32_t end, const uint8_t *buffer) { int res = ERROR_FAIL; - struct nrf51_info *chip = bank->driver_priv; + struct nrf5_info *chip = bank->driver_priv; struct flash_sector *sector; uint32_t offset; @@ -960,7 +749,7 @@ static int nrf51_write_pages(struct flash_bank *bank, uint32_t start, uint32_t e /* Erase all sectors */ for (offset = start; offset < end; offset += chip->code_page_size) { - sector = nrf51_find_sector_by_address(bank, offset); + sector = nrf5_find_sector_by_address(bank, offset); if (!sector) { LOG_ERROR("Invalid sector @ 0x%08"PRIx32, offset); return ERROR_FLASH_SECTOR_INVALID; @@ -972,7 +761,7 @@ static int nrf51_write_pages(struct flash_bank *bank, uint32_t start, uint32_t e } if (sector->is_erased != 1) { /* 1 = erased, 0= not erased, -1 = unknown */ - res = nrf51_erase_page(bank, chip, sector); + res = nrf5_erase_page(bank, chip, sector); if (res != ERROR_OK) { LOG_ERROR("Failed to erase sector @ 0x%08"PRIx32, sector->offset); goto error; @@ -981,41 +770,41 @@ static int nrf51_write_pages(struct flash_bank *bank, uint32_t start, uint32_t e sector->is_erased = 0; } - res = nrf51_nvmc_write_enable(chip); + res = nrf5_nvmc_write_enable(chip); if (res != ERROR_OK) goto error; - res = nrf51_ll_flash_write(chip, start, buffer, (end - start)); + res = nrf5_ll_flash_write(chip, start, buffer, (end - start)); if (res != ERROR_OK) goto set_read_only; - return nrf51_nvmc_read_only(chip); + return nrf5_nvmc_read_only(chip); set_read_only: - nrf51_nvmc_read_only(chip); + nrf5_nvmc_read_only(chip); error: - LOG_ERROR("Failed to write to nrf51 flash"); + LOG_ERROR("Failed to write to nrf5 flash"); return res; } -static int nrf51_erase(struct flash_bank *bank, int first, int last) +static int nrf5_erase(struct flash_bank *bank, int first, int last) { int res; - struct nrf51_info *chip; + struct nrf5_info *chip; - res = nrf51_get_probed_chip_if_halted(bank, &chip); + res = nrf5_get_probed_chip_if_halted(bank, &chip); if (res != ERROR_OK) return res; /* For each sector to be erased */ for (int s = first; s <= last && res == ERROR_OK; s++) - res = nrf51_erase_page(bank, chip, &bank->sectors[s]); + res = nrf5_erase_page(bank, chip, &bank->sectors[s]); return res; } -static int nrf51_code_flash_write(struct flash_bank *bank, - struct nrf51_info *chip, +static int nrf5_code_flash_write(struct flash_bank *bank, + struct nrf5_info *chip, const uint8_t *buffer, uint32_t offset, uint32_t count) { @@ -1062,58 +851,58 @@ static int nrf51_code_flash_write(struct flash_bank *bank, return res; } - return nrf51_write_pages(bank, first_page_offset, last_page_offset, buffer_to_flash); + return nrf5_write_pages(bank, first_page_offset, last_page_offset, buffer_to_flash); } -static int nrf51_uicr_flash_write(struct flash_bank *bank, - struct nrf51_info *chip, +static int nrf5_uicr_flash_write(struct flash_bank *bank, + struct nrf5_info *chip, const uint8_t *buffer, uint32_t offset, uint32_t count) { int res; - uint8_t uicr[NRF51_UICR_SIZE]; + uint8_t uicr[NRF5_UICR_SIZE]; struct flash_sector *sector = &bank->sectors[0]; - if ((offset + count) > NRF51_UICR_SIZE) + if ((offset + count) > NRF5_UICR_SIZE) return ERROR_FAIL; res = target_read_memory(bank->target, - NRF51_UICR_BASE, + NRF5_UICR_BASE, 1, - NRF51_UICR_SIZE, + NRF5_UICR_SIZE, uicr); if (res != ERROR_OK) return res; if (sector->is_erased != 1) { - res = nrf51_erase_page(bank, chip, sector); + res = nrf5_erase_page(bank, chip, sector); if (res != ERROR_OK) return res; } - res = nrf51_nvmc_write_enable(chip); + res = nrf5_nvmc_write_enable(chip); if (res != ERROR_OK) return res; memcpy(&uicr[offset], buffer, count); - res = nrf51_ll_flash_write(chip, NRF51_UICR_BASE, uicr, NRF51_UICR_SIZE); + res = nrf5_ll_flash_write(chip, NRF5_UICR_BASE, uicr, NRF5_UICR_SIZE); if (res != ERROR_OK) { - nrf51_nvmc_read_only(chip); + nrf5_nvmc_read_only(chip); return res; } - return nrf51_nvmc_read_only(chip); + return nrf5_nvmc_read_only(chip); } -static int nrf51_write(struct flash_bank *bank, const uint8_t *buffer, +static int nrf5_write(struct flash_bank *bank, const uint8_t *buffer, uint32_t offset, uint32_t count) { int res; - struct nrf51_info *chip; + struct nrf5_info *chip; - res = nrf51_get_probed_chip_if_halted(bank, &chip); + res = nrf5_get_probed_chip_if_halted(bank, &chip); if (res != ERROR_OK) return res; @@ -1121,15 +910,15 @@ static int nrf51_write(struct flash_bank *bank, const uint8_t *buffer, } -FLASH_BANK_COMMAND_HANDLER(nrf51_flash_bank_command) +FLASH_BANK_COMMAND_HANDLER(nrf5_flash_bank_command) { - static struct nrf51_info *chip; + static struct nrf5_info *chip; switch (bank->base) { - case NRF51_FLASH_BASE: + case NRF5_FLASH_BASE: bank->bank_number = 0; break; - case NRF51_UICR_BASE: + case NRF5_UICR_BASE: bank->bank_number = 1; break; default: @@ -1147,11 +936,11 @@ FLASH_BANK_COMMAND_HANDLER(nrf51_flash_bank_command) } switch (bank->base) { - case NRF51_FLASH_BASE: - chip->bank[bank->bank_number].write = nrf51_code_flash_write; + case NRF5_FLASH_BASE: + chip->bank[bank->bank_number].write = nrf5_code_flash_write; break; - case NRF51_UICR_BASE: - chip->bank[bank->bank_number].write = nrf51_uicr_flash_write; + case NRF5_UICR_BASE: + chip->bank[bank->bank_number].write = nrf5_uicr_flash_write; break; } @@ -1161,27 +950,27 @@ FLASH_BANK_COMMAND_HANDLER(nrf51_flash_bank_command) return ERROR_OK; } -COMMAND_HANDLER(nrf51_handle_mass_erase_command) +COMMAND_HANDLER(nrf5_handle_mass_erase_command) { int res; struct flash_bank *bank = NULL; struct target *target = get_current_target(CMD_CTX); - res = get_flash_bank_by_addr(target, NRF51_FLASH_BASE, true, &bank); + res = get_flash_bank_by_addr(target, NRF5_FLASH_BASE, true, &bank); if (res != ERROR_OK) return res; assert(bank != NULL); - struct nrf51_info *chip; + struct nrf5_info *chip; - res = nrf51_get_probed_chip_if_halted(bank, &chip); + res = nrf5_get_probed_chip_if_halted(bank, &chip); if (res != ERROR_OK) return res; uint32_t ppfc; - res = target_read_u32(target, NRF51_FICR_PPFC, + res = target_read_u32(target, NRF5_FICR_PPFC, &ppfc); if (res != ERROR_OK) { LOG_ERROR("Couldn't read PPFC register"); @@ -1194,23 +983,23 @@ COMMAND_HANDLER(nrf51_handle_mass_erase_command) return ERROR_FAIL; } - res = nrf51_erase_all(chip); + res = nrf5_erase_all(chip); if (res != ERROR_OK) { LOG_ERROR("Failed to erase the chip"); - nrf51_protect_check(bank); + nrf5_protect_check(bank); return res; } for (int i = 0; i < bank->num_sectors; i++) bank->sectors[i].is_erased = 1; - res = nrf51_protect_check(bank); + res = nrf5_protect_check(bank); if (res != ERROR_OK) { LOG_ERROR("Failed to check chip's write protection"); return res; } - res = get_flash_bank_by_addr(target, NRF51_UICR_BASE, true, &bank); + res = get_flash_bank_by_addr(target, NRF5_UICR_BASE, true, &bank); if (res != ERROR_OK) return res; @@ -1219,13 +1008,13 @@ COMMAND_HANDLER(nrf51_handle_mass_erase_command) return ERROR_OK; } -static int nrf51_info(struct flash_bank *bank, char *buf, int buf_size) +static int nrf5_info(struct flash_bank *bank, char *buf, int buf_size) { int res; - struct nrf51_info *chip; + struct nrf5_info *chip; - res = nrf51_get_probed_chip_if_halted(bank, &chip); + res = nrf5_get_probed_chip_if_halted(bank, &chip); if (res != ERROR_OK) return res; @@ -1233,45 +1022,45 @@ static int nrf51_info(struct flash_bank *bank, char *buf, int buf_size) const uint32_t address; uint32_t value; } ficr[] = { - { .address = NRF51_FICR_CODEPAGESIZE }, - { .address = NRF51_FICR_CODESIZE }, - { .address = NRF51_FICR_CLENR0 }, - { .address = NRF51_FICR_PPFC }, - { .address = NRF51_FICR_NUMRAMBLOCK }, - { .address = NRF51_FICR_SIZERAMBLOCK0 }, - { .address = NRF51_FICR_SIZERAMBLOCK1 }, - { .address = NRF51_FICR_SIZERAMBLOCK2 }, - { .address = NRF51_FICR_SIZERAMBLOCK3 }, - { .address = NRF51_FICR_CONFIGID }, - { .address = NRF51_FICR_DEVICEID0 }, - { .address = NRF51_FICR_DEVICEID1 }, - { .address = NRF51_FICR_ER0 }, - { .address = NRF51_FICR_ER1 }, - { .address = NRF51_FICR_ER2 }, - { .address = NRF51_FICR_ER3 }, - { .address = NRF51_FICR_IR0 }, - { .address = NRF51_FICR_IR1 }, - { .address = NRF51_FICR_IR2 }, - { .address = NRF51_FICR_IR3 }, - { .address = NRF51_FICR_DEVICEADDRTYPE }, - { .address = NRF51_FICR_DEVICEADDR0 }, - { .address = NRF51_FICR_DEVICEADDR1 }, - { .address = NRF51_FICR_OVERRIDEN }, - { .address = NRF51_FICR_NRF_1MBIT0 }, - { .address = NRF51_FICR_NRF_1MBIT1 }, - { .address = NRF51_FICR_NRF_1MBIT2 }, - { .address = NRF51_FICR_NRF_1MBIT3 }, - { .address = NRF51_FICR_NRF_1MBIT4 }, - { .address = NRF51_FICR_BLE_1MBIT0 }, - { .address = NRF51_FICR_BLE_1MBIT1 }, - { .address = NRF51_FICR_BLE_1MBIT2 }, - { .address = NRF51_FICR_BLE_1MBIT3 }, - { .address = NRF51_FICR_BLE_1MBIT4 }, + { .address = NRF5_FICR_CODEPAGESIZE }, + { .address = NRF5_FICR_CODESIZE }, + { .address = NRF5_FICR_CLENR0 }, + { .address = NRF5_FICR_PPFC }, + { .address = NRF5_FICR_NUMRAMBLOCK }, + { .address = NRF5_FICR_SIZERAMBLOCK0 }, + { .address = NRF5_FICR_SIZERAMBLOCK1 }, + { .address = NRF5_FICR_SIZERAMBLOCK2 }, + { .address = NRF5_FICR_SIZERAMBLOCK3 }, + { .address = NRF5_FICR_CONFIGID }, + { .address = NRF5_FICR_DEVICEID0 }, + { .address = NRF5_FICR_DEVICEID1 }, + { .address = NRF5_FICR_ER0 }, + { .address = NRF5_FICR_ER1 }, + { .address = NRF5_FICR_ER2 }, + { .address = NRF5_FICR_ER3 }, + { .address = NRF5_FICR_IR0 }, + { .address = NRF5_FICR_IR1 }, + { .address = NRF5_FICR_IR2 }, + { .address = NRF5_FICR_IR3 }, + { .address = NRF5_FICR_DEVICEADDRTYPE }, + { .address = NRF5_FICR_DEVICEADDR0 }, + { .address = NRF5_FICR_DEVICEADDR1 }, + { .address = NRF5_FICR_OVERRIDEN }, + { .address = NRF5_FICR_NRF_1MBIT0 }, + { .address = NRF5_FICR_NRF_1MBIT1 }, + { .address = NRF5_FICR_NRF_1MBIT2 }, + { .address = NRF5_FICR_NRF_1MBIT3 }, + { .address = NRF5_FICR_NRF_1MBIT4 }, + { .address = NRF5_FICR_BLE_1MBIT0 }, + { .address = NRF5_FICR_BLE_1MBIT1 }, + { .address = NRF5_FICR_BLE_1MBIT2 }, + { .address = NRF5_FICR_BLE_1MBIT3 }, + { .address = NRF5_FICR_BLE_1MBIT4 }, }, uicr[] = { - { .address = NRF51_UICR_CLENR0, }, - { .address = NRF51_UICR_RBPCONF }, - { .address = NRF51_UICR_XTALFREQ }, - { .address = NRF51_UICR_FWID }, + { .address = NRF5_UICR_CLENR0, }, + { .address = NRF5_UICR_RBPCONF }, + { .address = NRF5_UICR_XTALFREQ }, + { .address = NRF5_UICR_FWID }, }; for (size_t i = 0; i < ARRAY_SIZE(ficr); i++) { @@ -1343,38 +1132,62 @@ static int nrf51_info(struct flash_bank *bank, char *buf, int buf_size) return ERROR_OK; } -static const struct command_registration nrf51_exec_command_handlers[] = { +static const struct command_registration nrf5_exec_command_handlers[] = { { .name = "mass_erase", - .handler = nrf51_handle_mass_erase_command, + .handler = nrf5_handle_mass_erase_command, .mode = COMMAND_EXEC, .help = "Erase all flash contents of the chip.", }, COMMAND_REGISTRATION_DONE }; -static const struct command_registration nrf51_command_handlers[] = { +static const struct command_registration nrf5_command_handlers[] = { + { + .name = "nrf5", + .mode = COMMAND_ANY, + .help = "nrf5 flash command group", + .usage = "", + .chain = nrf5_exec_command_handlers, + }, { .name = "nrf51", .mode = COMMAND_ANY, .help = "nrf51 flash command group", .usage = "", - .chain = nrf51_exec_command_handlers, + .chain = nrf5_exec_command_handlers, }, COMMAND_REGISTRATION_DONE }; +struct flash_driver nrf5_flash = { + .name = "nrf5", + .commands = nrf5_command_handlers, + .flash_bank_command = nrf5_flash_bank_command, + .info = nrf5_info, + .erase = nrf5_erase, + .protect = nrf5_protect, + .write = nrf5_write, + .read = default_flash_read, + .probe = nrf5_probe, + .auto_probe = nrf5_auto_probe, + .erase_check = default_flash_blank_check, + .protect_check = nrf5_protect_check, +}; + +/* We need to retain the flash-driver name as well as the commands + * for backwards compatability */ struct flash_driver nrf51_flash = { .name = "nrf51", - .commands = nrf51_command_handlers, - .flash_bank_command = nrf51_flash_bank_command, - .info = nrf51_info, - .erase = nrf51_erase, - .protect = nrf51_protect, - .write = nrf51_write, + .commands = nrf5_command_handlers, + .flash_bank_command = nrf5_flash_bank_command, + .info = nrf5_info, + .erase = nrf5_erase, + .protect = nrf5_protect, + .write = nrf5_write, .read = default_flash_read, - .probe = nrf51_probe, - .auto_probe = nrf51_auto_probe, + .probe = nrf5_probe, + .auto_probe = nrf5_auto_probe, .erase_check = default_flash_blank_check, - .protect_check = nrf51_protect_check, + .protect_check = nrf5_protect_check, }; diff --git a/src/flash/nor/spi.c b/src/flash/nor/spi.c index c8239d2d2..b255edba6 100644 --- a/src/flash/nor/spi.c +++ b/src/flash/nor/spi.c @@ -31,53 +31,56 @@ * from device datasheets and Linux SPI flash drivers. */ const struct flash_device flash_devices[] = { /* name, erase_cmd, chip_erase_cmd, device_id, pagesize, sectorsize, size_in_bytes */ - FLASH_ID("st m25p05", 0xd8, 0xc7, 0x00102020, 0x80, 0x8000, 0x10000), - FLASH_ID("st m25p10", 0xd8, 0xc7, 0x00112020, 0x80, 0x8000, 0x20000), - FLASH_ID("st m25p20", 0xd8, 0xc7, 0x00122020, 0x100, 0x10000, 0x40000), - FLASH_ID("st m25p40", 0xd8, 0xc7, 0x00132020, 0x100, 0x10000, 0x80000), - FLASH_ID("st m25p80", 0xd8, 0xc7, 0x00142020, 0x100, 0x10000, 0x100000), - FLASH_ID("st m25p16", 0xd8, 0xc7, 0x00152020, 0x100, 0x10000, 0x200000), - FLASH_ID("st m25p32", 0xd8, 0xc7, 0x00162020, 0x100, 0x10000, 0x400000), - FLASH_ID("st m25p64", 0xd8, 0xc7, 0x00172020, 0x100, 0x10000, 0x800000), - FLASH_ID("st m25p128", 0xd8, 0xc7, 0x00182020, 0x100, 0x40000, 0x1000000), - FLASH_ID("st m45pe10", 0xd8, 0xd8, 0x00114020, 0x100, 0x10000, 0x20000), - FLASH_ID("st m45pe20", 0xd8, 0xd8, 0x00124020, 0x100, 0x10000, 0x40000), - FLASH_ID("st m45pe40", 0xd8, 0xd8, 0x00134020, 0x100, 0x10000, 0x80000), - FLASH_ID("st m45pe80", 0xd8, 0xd8, 0x00144020, 0x100, 0x10000, 0x100000), - FLASH_ID("sp s25fl004", 0xd8, 0xc7, 0x00120201, 0x100, 0x10000, 0x80000), - FLASH_ID("sp s25fl008", 0xd8, 0xc7, 0x00130201, 0x100, 0x10000, 0x100000), - FLASH_ID("sp s25fl016", 0xd8, 0xc7, 0x00140201, 0x100, 0x10000, 0x200000), - FLASH_ID("sp s25fl116k", 0xd8, 0xc7, 0x00154001, 0x100, 0x10000, 0x200000), - FLASH_ID("sp s25fl032", 0xd8, 0xc7, 0x00150201, 0x100, 0x10000, 0x400000), - FLASH_ID("sp s25fl132k", 0xd8, 0xc7, 0x00164001, 0x100, 0x10000, 0x400000), - FLASH_ID("sp s25fl064", 0xd8, 0xc7, 0x00160201, 0x100, 0x10000, 0x800000), - FLASH_ID("sp s25fl164k", 0xd8, 0xc7, 0x00174001, 0x100, 0x10000, 0x800000), - FLASH_ID("sp s25fl128", 0xd8, 0xc7, 0x00182001, 0x100, 0x10000, 0x1000000), - FLASH_ID("sp s25fl256", 0xd8, 0xc7, 0x00190201, 0x100, 0x10000, 0x2000000), - FLASH_ID("atmel 25f512", 0x52, 0xc7, 0x0065001f, 0x80, 0x8000, 0x10000), - FLASH_ID("atmel 25f1024", 0x52, 0x62, 0x0060001f, 0x100, 0x8000, 0x20000), - FLASH_ID("atmel 25f2048", 0x52, 0x62, 0x0063001f, 0x100, 0x10000, 0x40000), - FLASH_ID("atmel 25f4096", 0x52, 0x62, 0x0064001f, 0x100, 0x10000, 0x80000), - FLASH_ID("atmel 25fs040", 0xd7, 0xc7, 0x0004661f, 0x100, 0x10000, 0x80000), - FLASH_ID("mac 25l512", 0xd8, 0xc7, 0x001020c2, 0x010, 0x10000, 0x10000), - FLASH_ID("mac 25l1005", 0xd8, 0xc7, 0x001120c2, 0x010, 0x10000, 0x20000), - FLASH_ID("mac 25l2005", 0xd8, 0xc7, 0x001220c2, 0x010, 0x10000, 0x40000), - FLASH_ID("mac 25l4005", 0xd8, 0xc7, 0x001320c2, 0x010, 0x10000, 0x80000), - FLASH_ID("mac 25l8005", 0xd8, 0xc7, 0x001420c2, 0x010, 0x10000, 0x100000), - FLASH_ID("mac 25l1605", 0xd8, 0xc7, 0x001520c2, 0x100, 0x10000, 0x200000), - FLASH_ID("mac 25l3205", 0xd8, 0xc7, 0x001620c2, 0x100, 0x10000, 0x400000), - FLASH_ID("mac 25l6405", 0xd8, 0xc7, 0x001720c2, 0x100, 0x10000, 0x800000), - FLASH_ID("micron n25q064", 0xd8, 0xc7, 0x0017ba20, 0x100, 0x10000, 0x800000), - FLASH_ID("micron n25q128", 0xd8, 0xc7, 0x0018ba20, 0x100, 0x10000, 0x1000000), - FLASH_ID("issi is25lp128", 0xd8, 0xc7, 0x0018609d, 0x100, 0x10000, 0x1000000), - FLASH_ID("win w25q80bv", 0xd8, 0xc7, 0x001440ef, 0x100, 0x10000, 0x100000), - FLASH_ID("win w25q32fv", 0xd8, 0xc7, 0x001640ef, 0x100, 0x10000, 0x400000), - FLASH_ID("win w25q32dw", 0xd8, 0xc7, 0x001660ef, 0x100, 0x10000, 0x400000), - FLASH_ID("win w25q64cv", 0xd8, 0xc7, 0x001740ef, 0x100, 0x10000, 0x800000), - FLASH_ID("win w25q128fv", 0xd8, 0xc7, 0x001840ef, 0x100, 0x10000, 0x1000000), - FLASH_ID("gd gd25q20", 0x20, 0xc7, 0x00c84012, 0x100, 0x1000, 0x80000), - FLASH_ID("gd gd25q16c", 0xd8, 0xc7, 0x001540c8, 0x100, 0x10000, 0x200000), - FLASH_ID("gd gd25q32c", 0xd8, 0xc7, 0x001640c8, 0x100, 0x10000, 0x400000), - FLASH_ID("gd gd25q128c", 0xd8, 0xc7, 0x001840c8, 0x100, 0x10000, 0x1000000), - FLASH_ID(NULL, 0, 0, 0, 0, 0, 0) + FLASH_ID("st m25p05", 0xd8, 0xc7, 0x00102020, 0x80, 0x8000, 0x10000), + FLASH_ID("st m25p10", 0xd8, 0xc7, 0x00112020, 0x80, 0x8000, 0x20000), + FLASH_ID("st m25p20", 0xd8, 0xc7, 0x00122020, 0x100, 0x10000, 0x40000), + FLASH_ID("st m25p40", 0xd8, 0xc7, 0x00132020, 0x100, 0x10000, 0x80000), + FLASH_ID("st m25p80", 0xd8, 0xc7, 0x00142020, 0x100, 0x10000, 0x100000), + FLASH_ID("st m25p16", 0xd8, 0xc7, 0x00152020, 0x100, 0x10000, 0x200000), + FLASH_ID("st m25p32", 0xd8, 0xc7, 0x00162020, 0x100, 0x10000, 0x400000), + FLASH_ID("st m25p64", 0xd8, 0xc7, 0x00172020, 0x100, 0x10000, 0x800000), + FLASH_ID("st m25p128", 0xd8, 0xc7, 0x00182020, 0x100, 0x40000, 0x1000000), + FLASH_ID("st m45pe10", 0xd8, 0xd8, 0x00114020, 0x100, 0x10000, 0x20000), + FLASH_ID("st m45pe20", 0xd8, 0xd8, 0x00124020, 0x100, 0x10000, 0x40000), + FLASH_ID("st m45pe40", 0xd8, 0xd8, 0x00134020, 0x100, 0x10000, 0x80000), + FLASH_ID("st m45pe80", 0xd8, 0xd8, 0x00144020, 0x100, 0x10000, 0x100000), + FLASH_ID("sp s25fl004", 0xd8, 0xc7, 0x00120201, 0x100, 0x10000, 0x80000), + FLASH_ID("sp s25fl008", 0xd8, 0xc7, 0x00130201, 0x100, 0x10000, 0x100000), + FLASH_ID("sp s25fl016", 0xd8, 0xc7, 0x00140201, 0x100, 0x10000, 0x200000), + FLASH_ID("sp s25fl116k", 0xd8, 0xc7, 0x00154001, 0x100, 0x10000, 0x200000), + FLASH_ID("sp s25fl032", 0xd8, 0xc7, 0x00150201, 0x100, 0x10000, 0x400000), + FLASH_ID("sp s25fl132k", 0xd8, 0xc7, 0x00164001, 0x100, 0x10000, 0x400000), + FLASH_ID("sp s25fl064", 0xd8, 0xc7, 0x00160201, 0x100, 0x10000, 0x800000), + FLASH_ID("sp s25fl164k", 0xd8, 0xc7, 0x00174001, 0x100, 0x10000, 0x800000), + FLASH_ID("sp s25fl128", 0xd8, 0xc7, 0x00182001, 0x100, 0x10000, 0x1000000), + FLASH_ID("sp s25fl256", 0xd8, 0xc7, 0x00190201, 0x100, 0x10000, 0x2000000), + FLASH_ID("atmel 25f512", 0x52, 0xc7, 0x0065001f, 0x80, 0x8000, 0x10000), + FLASH_ID("atmel 25f1024", 0x52, 0x62, 0x0060001f, 0x100, 0x8000, 0x20000), + FLASH_ID("atmel 25f2048", 0x52, 0x62, 0x0063001f, 0x100, 0x10000, 0x40000), + FLASH_ID("atmel 25f4096", 0x52, 0x62, 0x0064001f, 0x100, 0x10000, 0x80000), + FLASH_ID("atmel 25fs040", 0xd7, 0xc7, 0x0004661f, 0x100, 0x10000, 0x80000), + FLASH_ID("mac 25l512", 0xd8, 0xc7, 0x001020c2, 0x010, 0x10000, 0x10000), + FLASH_ID("mac 25l1005", 0xd8, 0xc7, 0x001120c2, 0x010, 0x10000, 0x20000), + FLASH_ID("mac 25l2005", 0xd8, 0xc7, 0x001220c2, 0x010, 0x10000, 0x40000), + FLASH_ID("mac 25l4005", 0xd8, 0xc7, 0x001320c2, 0x010, 0x10000, 0x80000), + FLASH_ID("mac 25l8005", 0xd8, 0xc7, 0x001420c2, 0x010, 0x10000, 0x100000), + FLASH_ID("mac 25l1605", 0xd8, 0xc7, 0x001520c2, 0x100, 0x10000, 0x200000), + FLASH_ID("mac 25l3205", 0xd8, 0xc7, 0x001620c2, 0x100, 0x10000, 0x400000), + FLASH_ID("mac 25l6405", 0xd8, 0xc7, 0x001720c2, 0x100, 0x10000, 0x800000), + FLASH_ID("micron n25q064", 0xd8, 0xc7, 0x0017ba20, 0x100, 0x10000, 0x800000), + FLASH_ID("micron n25q128", 0xd8, 0xc7, 0x0018ba20, 0x100, 0x10000, 0x1000000), + FLASH_ID("micron n25q256 3v", 0xd8, 0xc7, 0x0019ba20, 0x100, 0x10000, 0x2000000), + FLASH_ID("micron n25q256 1.8v", 0xd8, 0xc7, 0x0019bb20, 0x100, 0x10000, 0x2000000), + FLASH_ID("issi is25lp128", 0xd8, 0xc7, 0x0018609d, 0x100, 0x10000, 0x1000000), + FLASH_ID("issi is25wp256d", 0xd8, 0xc7, 0x0019709d, 0x100, 0x10000, 0x2000000), + FLASH_ID("win w25q80bv", 0xd8, 0xc7, 0x001440ef, 0x100, 0x10000, 0x100000), + FLASH_ID("win w25q32fv", 0xd8, 0xc7, 0x001640ef, 0x100, 0x10000, 0x400000), + FLASH_ID("win w25q32dw", 0xd8, 0xc7, 0x001660ef, 0x100, 0x10000, 0x400000), + FLASH_ID("win w25q64cv", 0xd8, 0xc7, 0x001740ef, 0x100, 0x10000, 0x800000), + FLASH_ID("win w25q128fv", 0xd8, 0xc7, 0x001840ef, 0x100, 0x10000, 0x1000000), + FLASH_ID("gd gd25q20", 0x20, 0xc7, 0x00c84012, 0x100, 0x1000, 0x80000), + FLASH_ID("gd gd25q16c", 0xd8, 0xc7, 0x001540c8, 0x100, 0x10000, 0x200000), + FLASH_ID("gd gd25q32c", 0xd8, 0xc7, 0x001640c8, 0x100, 0x10000, 0x400000), + FLASH_ID("gd gd25q128c", 0xd8, 0xc7, 0x001840c8, 0x100, 0x10000, 0x1000000), + FLASH_ID(NULL, 0, 0, 0, 0, 0, 0) }; diff --git a/src/flash/nor/stm32f2x.c b/src/flash/nor/stm32f2x.c index 65cb212b6..b0992b404 100644 --- a/src/flash/nor/stm32f2x.c +++ b/src/flash/nor/stm32f2x.c @@ -143,9 +143,8 @@ #define FLASH_PSIZE_16 (1 << 8) #define FLASH_PSIZE_32 (2 << 8) #define FLASH_PSIZE_64 (3 << 8) -/* The sector number encoding is not straight binary for dual bank flash. - * Warning: evaluates the argument multiple times */ -#define FLASH_SNB(a) ((((a) >= 12) ? 0x10 | ((a) - 12) : (a)) << 3) +/* The sector number encoding is not straight binary for dual bank flash. */ +#define FLASH_SNB(a) ((a) << 3) #define FLASH_LOCK (1 << 31) /* FLASH_SR register bits */ @@ -489,6 +488,7 @@ static int stm32x_protect_check(struct flash_bank *bank) static int stm32x_erase(struct flash_bank *bank, int first, int last) { + struct stm32x_flash_bank *stm32x_info = bank->driver_priv; struct target *target = bank->target; int i; @@ -516,8 +516,14 @@ static int stm32x_erase(struct flash_bank *bank, int first, int last) */ for (i = first; i <= last; i++) { + int snb; + if (stm32x_info->has_large_mem && i >= 12) + snb = (i - 12) | 0x10; + else + snb = i; + retval = target_write_u32(target, - stm32x_get_flash_reg(bank, STM32_FLASH_CR), FLASH_SER | FLASH_SNB(i) | FLASH_STRT); + stm32x_get_flash_reg(bank, STM32_FLASH_CR), FLASH_SER | FLASH_SNB(snb) | FLASH_STRT); if (retval != ERROR_OK) return retval; @@ -1047,7 +1053,8 @@ static int stm32x_probe(struct flash_bank *bank) if (device_id == 0x451) { for (i = 0; i < num_prot_blocks; i++) { bank->prot_blocks[i].offset = bank->sectors[i << 1].offset; - bank->prot_blocks[i].size = bank->sectors[i << 1].size << 1; + bank->prot_blocks[i].size = bank->sectors[i << 1].size + + bank->sectors[(i << 1) + 1].size; } } } else { diff --git a/src/flash/nor/stm32h7x.c b/src/flash/nor/stm32h7x.c new file mode 100644 index 000000000..01e6f06dc --- /dev/null +++ b/src/flash/nor/stm32h7x.c @@ -0,0 +1,1183 @@ +/*************************************************************************** + * Copyright (C) 2017 by STMicroelectronics * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program. If not, see . * + ***************************************************************************/ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "imp.h" +#include +#include +#include + + +/* Erase time can be as high as 1000ms, 10x this and it's toast... */ +#define FLASH_ERASE_TIMEOUT 10000 +#define FLASH_WRITE_TIMEOUT 5 + +/* RM 433 */ +/* Same Flash registers for both banks, */ +/* access depends on Flash Base address */ +#define FLASH_ACR 0x00 +#define FLASH_KEYR 0x04 +#define FLASH_OPTKEYR 0x08 +#define FLASH_CR 0x0C +#define FLASH_SR 0x10 +#define FLASH_CCR 0x14 +#define FLASH_OPTCR 0x18 +#define FLASH_OPTCUR 0x1C +#define FLASH_OPTPRG 0x20 +#define FLASH_OPTCCR 0x24 +#define FLASH_WPSNCUR 0x38 +#define FLASH_WPSNPRG 0x3C + + +/* FLASH_CR register bits */ +#define FLASH_LOCK (1 << 0) +#define FLASH_PG (1 << 1) +#define FLASH_SER (1 << 2) +#define FLASH_BER_CMD (1 << 3) +#define FLASH_PSIZE_8 (0 << 4) +#define FLASH_PSIZE_16 (1 << 4) +#define FLASH_PSIZE_32 (2 << 4) +#define FLASH_PSIZE_64 (3 << 4) +#define FLASH_FW (1 << 6) +#define FLASH_START (1 << 7) + +#define FLASH_SNB(a) ((a) << 8) + +/* FLASH_SR register bits */ +#define FLASH_BSY (1 << 0) /* Operation in progress */ +#define FLASH_WRPERR (1 << 17) /* Write protection error */ +#define FLASH_PGSERR (1 << 18) /* Programming sequence error */ +#define FLASH_STRBERR (1 << 19) /* Strobe error */ +#define FLASH_INCERR (1 << 21) /* Increment error */ +#define FLASH_OPERR (1 << 22) /* Operation error */ +#define FLASH_RDPERR (1 << 23) /* Read Protection error */ +#define FLASH_RDSERR (1 << 24) /* Secure Protection error */ +#define FLASH_SNECCERR (1 << 25) /* Single ECC error */ +#define FLASH_DBECCERR (1 << 26) /* Double ECC error */ + +#define FLASH_ERROR (FLASH_WRPERR | FLASH_PGSERR | FLASH_STRBERR | FLASH_INCERR | FLASH_OPERR | \ + FLASH_RDPERR | FLASH_RDSERR | FLASH_SNECCERR | FLASH_DBECCERR) + +/* FLASH_OPTCR register bits */ +#define OPT_LOCK (1 << 0) +#define OPT_START (1 << 1) + +/* FLASH_OPTCUR bit definitions (reading) */ +#define IWDG1_HW (1 << 4) + +/* register unlock keys */ +#define KEY1 0x45670123 +#define KEY2 0xCDEF89AB + +/* option register unlock key */ +#define OPTKEY1 0x08192A3B +#define OPTKEY2 0x4C5D6E7F + +#define DBGMCU_IDCODE_REGISTER 0x5C001000 +#define FLASH_BANK0_ADDRESS 0x08000000 +#define FLASH_BANK1_ADDRESS 0x08100000 +#define FLASH_REG_BASE_B0 0x52002000 +#define FLASH_REG_BASE_B1 0x52002100 +#define FLASH_SIZE_ADDRESS 0x1FF1E880 +#define FLASH_BLOCK_SIZE 32 + +struct stm32h7x_rev { + uint16_t rev; + const char *str; +}; + +struct stm32x_options { + uint8_t RDP; + uint32_t protection; /* bank1 WRP */ + uint32_t protection2; /* bank2 WRP */ + uint8_t user_options; + uint8_t user2_options; + uint8_t user3_options; + uint8_t independent_watchdog_selection; +}; + +struct stm32h7x_part_info { + uint16_t id; + const char *device_str; + const struct stm32h7x_rev *revs; + size_t num_revs; + unsigned int page_size; + unsigned int pages_per_sector; + uint16_t max_flash_size_kb; + uint8_t has_dual_bank; + uint16_t first_bank_size_kb; /* Used when has_dual_bank is true */ + uint32_t flash_base; /* Flash controller registers location */ + uint32_t fsize_base; /* Location of FSIZE register */ +}; + +struct stm32h7x_flash_bank { + int probed; + uint32_t idcode; + uint32_t user_bank_size; + uint32_t flash_base; /* Address of flash reg controller */ + struct stm32x_options option_bytes; + const struct stm32h7x_part_info *part_info; +}; + +static const struct stm32h7x_rev stm32_450_revs[] = { + { 0x1000, "A" }, { 0x1001, "Z" }, { 0x1003, "Y" }, +}; + +static const struct stm32h7x_part_info stm32h7x_parts[] = { + { + .id = 0x450, + .revs = stm32_450_revs, + .num_revs = ARRAY_SIZE(stm32_450_revs), + .device_str = "STM32H7xx 2M", + .page_size = 128, /* 128 KB */ + .max_flash_size_kb = 2048, + .first_bank_size_kb = 1024, + .has_dual_bank = 1, + .flash_base = FLASH_REG_BASE_B0, + .fsize_base = FLASH_SIZE_ADDRESS, + }, +}; + +static int stm32x_unlock_reg(struct flash_bank *bank); +static int stm32x_lock_reg(struct flash_bank *bank); +static int stm32x_probe(struct flash_bank *bank); + +/* flash bank stm32x 0 0 */ + +FLASH_BANK_COMMAND_HANDLER(stm32x_flash_bank_command) +{ + struct stm32h7x_flash_bank *stm32x_info; + + if (CMD_ARGC < 6) + return ERROR_COMMAND_SYNTAX_ERROR; + + stm32x_info = malloc(sizeof(struct stm32h7x_flash_bank)); + bank->driver_priv = stm32x_info; + + stm32x_info->probed = 0; + stm32x_info->user_bank_size = bank->size; + + return ERROR_OK; +} + +static inline uint32_t stm32x_get_flash_reg(struct flash_bank *bank, uint32_t reg) +{ + struct stm32h7x_flash_bank *stm32x_info = bank->driver_priv; + return reg + stm32x_info->flash_base; +} + +static inline int stm32x_get_flash_status(struct flash_bank *bank, uint32_t *status) +{ + struct target *target = bank->target; + return target_read_u32(target, stm32x_get_flash_reg(bank, FLASH_SR), status); +} + +static int stm32x_wait_status_busy(struct flash_bank *bank, int timeout) +{ + struct target *target = bank->target; + struct stm32h7x_flash_bank *stm32x_info = bank->driver_priv; + uint32_t status; + int retval; + + /* wait for busy to clear */ + for (;;) { + retval = stm32x_get_flash_status(bank, &status); + if (retval != ERROR_OK) { + LOG_INFO("wait_status_busy, target_read_u32 : error : remote address 0x%x", stm32x_info->flash_base); + return retval; + } + + if ((status & FLASH_BSY) == 0) + break; + + if (timeout-- <= 0) { + LOG_INFO("wait_status_busy, time out expired, status: 0x%" PRIx32 "", status); + return ERROR_FAIL; + } + alive_sleep(1); + } + + if (status & FLASH_WRPERR) { + LOG_INFO("wait_status_busy, WRPERR : error : remote address 0x%x", stm32x_info->flash_base); + retval = ERROR_FAIL; + } + + /* Clear error + EOP flags but report errors */ + if (status & FLASH_ERROR) { + /* If this operation fails, we ignore it and report the original retval */ + target_write_u32(target, stm32x_get_flash_reg(bank, FLASH_CCR), status); + } + return retval; +} + +static int stm32x_unlock_reg(struct flash_bank *bank) +{ + uint32_t ctrl; + struct target *target = bank->target; + + /* first check if not already unlocked + * otherwise writing on FLASH_KEYR will fail + */ + int retval = target_read_u32(target, stm32x_get_flash_reg(bank, FLASH_CR), &ctrl); + if (retval != ERROR_OK) + return retval; + + if ((ctrl & FLASH_LOCK) == 0) + return ERROR_OK; + + /* unlock flash registers for bank */ + retval = target_write_u32(target, stm32x_get_flash_reg(bank, FLASH_KEYR), KEY1); + if (retval != ERROR_OK) + return retval; + + retval = target_write_u32(target, stm32x_get_flash_reg(bank, FLASH_KEYR), KEY2); + if (retval != ERROR_OK) + return retval; + + retval = target_read_u32(target, stm32x_get_flash_reg(bank, FLASH_CR), &ctrl); + if (retval != ERROR_OK) + return retval; + + if (ctrl & FLASH_LOCK) { + LOG_ERROR("flash not unlocked STM32_FLASH_CRx: %" PRIx32, ctrl); + return ERROR_TARGET_FAILURE; + } + return ERROR_OK; +} + +static int stm32x_unlock_option_reg(struct flash_bank *bank) +{ + uint32_t ctrl; + struct target *target = bank->target; + + int retval = target_read_u32(target, FLASH_REG_BASE_B0 + FLASH_OPTCR, &ctrl); + if (retval != ERROR_OK) + return retval; + + if ((ctrl & OPT_LOCK) == 0) + return ERROR_OK; + + /* unlock option registers */ + retval = target_write_u32(target, FLASH_REG_BASE_B0 + FLASH_OPTKEYR, OPTKEY1); + if (retval != ERROR_OK) + return retval; + + retval = target_write_u32(target, FLASH_REG_BASE_B0 + FLASH_OPTKEYR, OPTKEY2); + if (retval != ERROR_OK) + return retval; + + retval = target_read_u32(target, FLASH_REG_BASE_B0 + FLASH_OPTCR, &ctrl); + if (retval != ERROR_OK) + return retval; + + if (ctrl & OPT_LOCK) { + LOG_ERROR("options not unlocked STM32_FLASH_OPTCR: %" PRIx32, ctrl); + return ERROR_TARGET_FAILURE; + } + + return ERROR_OK; +} + +static int stm32x_lock_reg(struct flash_bank *bank) +{ + struct target *target = bank->target; + + /* Lock bank reg */ + int retval = target_write_u32(target, stm32x_get_flash_reg(bank, FLASH_CR), FLASH_LOCK); + if (retval != ERROR_OK) + return retval; + + return ERROR_OK; +} + +static int stm32x_read_options(struct flash_bank *bank) +{ + uint32_t optiondata; + struct stm32h7x_flash_bank *stm32x_info = NULL; + struct target *target = bank->target; + + stm32x_info = bank->driver_priv; + + /* read current option bytes */ + int retval = target_read_u32(target, FLASH_REG_BASE_B0 + FLASH_OPTCUR, &optiondata); + if (retval != ERROR_OK) + return retval; + + /* decode option data */ + stm32x_info->option_bytes.user_options = optiondata & 0xfc; + stm32x_info->option_bytes.RDP = (optiondata >> 8) & 0xff; + stm32x_info->option_bytes.user2_options = (optiondata >> 16) & 0xff; + stm32x_info->option_bytes.user3_options = (optiondata >> 24) & 0x83; + + if (optiondata & IWDG1_HW) + stm32x_info->option_bytes.independent_watchdog_selection = 1; + else + stm32x_info->option_bytes.independent_watchdog_selection = 0; + + if (stm32x_info->option_bytes.RDP != 0xAA) + LOG_INFO("Device Security Bit Set"); + + /* read current WPSN option bytes */ + retval = target_read_u32(target, FLASH_REG_BASE_B0 + FLASH_WPSNCUR, &optiondata); + if (retval != ERROR_OK) + return retval; + stm32x_info->option_bytes.protection = optiondata & 0xff; + + /* read current WPSN2 option bytes */ + retval = target_read_u32(target, FLASH_REG_BASE_B1 + FLASH_WPSNCUR, &optiondata); + if (retval != ERROR_OK) + return retval; + stm32x_info->option_bytes.protection2 = optiondata & 0xff; + + return ERROR_OK; +} + +static int stm32x_write_options(struct flash_bank *bank) +{ + struct stm32h7x_flash_bank *stm32x_info = NULL; + struct target *target = bank->target; + uint32_t optiondata; + + stm32x_info = bank->driver_priv; + + int retval = stm32x_unlock_option_reg(bank); + if (retval != ERROR_OK) + return retval; + + /* rebuild option data */ + optiondata = stm32x_info->option_bytes.user_options; + optiondata |= (stm32x_info->option_bytes.RDP << 8); + optiondata |= (stm32x_info->option_bytes.user2_options & 0xff) << 16; + optiondata |= (stm32x_info->option_bytes.user3_options & 0x83) << 24; + + if (stm32x_info->option_bytes.independent_watchdog_selection) + optiondata |= IWDG1_HW; + else + optiondata &= ~IWDG1_HW; + + /* program options */ + retval = target_write_u32(target, FLASH_REG_BASE_B0 + FLASH_OPTPRG, optiondata); + if (retval != ERROR_OK) + return retval; + + optiondata = stm32x_info->option_bytes.protection & 0xff; + /* Program protection WPSNPRG */ + retval = target_write_u32(target, FLASH_REG_BASE_B0 + FLASH_WPSNPRG, optiondata); + if (retval != ERROR_OK) + return retval; + + optiondata = stm32x_info->option_bytes.protection2 & 0xff; + /* Program protection WPSNPRG2 */ + retval = target_write_u32(target, FLASH_REG_BASE_B1 + FLASH_WPSNPRG, optiondata); + if (retval != ERROR_OK) + return retval; + + optiondata = 0x40000000; + /* Remove OPT error flag before programming */ + retval = target_write_u32(target, FLASH_REG_BASE_B0 + FLASH_OPTCCR, optiondata); + if (retval != ERROR_OK) + return retval; + + /* start programming cycle */ + retval = target_write_u32(target, FLASH_REG_BASE_B0 + FLASH_OPTCR, OPT_START); + if (retval != ERROR_OK) + return retval; + + /* wait for completion */ + int timeout = FLASH_ERASE_TIMEOUT; + for (;;) { + uint32_t status; + retval = target_read_u32(target, FLASH_REG_BASE_B0 + FLASH_SR, &status); + if (retval != ERROR_OK) { + LOG_INFO("stm32x_write_options: wait_status_busy : error"); + return retval; + } + if ((status & FLASH_BSY) == 0) + break; + + if (timeout-- <= 0) { + LOG_INFO("wait_status_busy, time out expired, status: 0x%" PRIx32 "", status); + return ERROR_FAIL; + } + alive_sleep(1); + } + + /* relock option registers */ + retval = target_write_u32(target, FLASH_REG_BASE_B0 + FLASH_OPTCR, OPT_LOCK); + if (retval != ERROR_OK) + return retval; + + return ERROR_OK; +} + +static int stm32x_protect_check(struct flash_bank *bank) +{ + struct stm32h7x_flash_bank *stm32x_info = bank->driver_priv; + + /* read 'write protection' settings */ + int retval = stm32x_read_options(bank); + if (retval != ERROR_OK) { + LOG_DEBUG("unable to read option bytes"); + return retval; + } + + for (int i = 0; i < bank->num_sectors; i++) { + if (stm32x_info->flash_base == FLASH_REG_BASE_B0) { + if (stm32x_info->option_bytes.protection & (1 << i)) + bank->sectors[i].is_protected = 0; + else + bank->sectors[i].is_protected = 1; + } else { + if (stm32x_info->option_bytes.protection2 & (1 << i)) + bank->sectors[i].is_protected = 0; + else + bank->sectors[i].is_protected = 1; + } + } + return ERROR_OK; +} + +static int stm32x_erase(struct flash_bank *bank, int first, int last) +{ + struct target *target = bank->target; + int retval; + + assert(first < bank->num_sectors); + assert(last < bank->num_sectors); + + if (bank->target->state != TARGET_HALTED) + return ERROR_TARGET_NOT_HALTED; + + retval = stm32x_unlock_reg(bank); + if (retval != ERROR_OK) + return retval; + + /* + Sector Erase + To erase a sector, follow the procedure below: + 1. Check that no Flash memory operation is ongoing by checking the BSY bit in the + FLASH_SR register + 2. Set the SER bit and select the sector + you wish to erase (SNB) in the FLASH_CR register + 3. Set the STRT bit in the FLASH_CR register + 4. Wait for the BSY bit to be cleared + */ + for (int i = first; i <= last; i++) { + LOG_DEBUG("erase sector %d", i); + retval = target_write_u32(target, stm32x_get_flash_reg(bank, FLASH_CR), + FLASH_SER | FLASH_SNB(i) | FLASH_PSIZE_64); + if (retval != ERROR_OK) { + LOG_ERROR("Error erase sector %d", i); + return retval; + } + retval = target_write_u32(target, stm32x_get_flash_reg(bank, FLASH_CR), + FLASH_SER | FLASH_SNB(i) | FLASH_PSIZE_64 | FLASH_START); + if (retval != ERROR_OK) { + LOG_ERROR("Error erase sector %d", i); + return retval; + } + retval = stm32x_wait_status_busy(bank, FLASH_ERASE_TIMEOUT); + + if (retval != ERROR_OK) { + LOG_ERROR("erase time-out error sector %d", i); + return retval; + } + bank->sectors[i].is_erased = 1; + } + + retval = stm32x_lock_reg(bank); + if (retval != ERROR_OK) { + LOG_ERROR("error during the lock of flash"); + return retval; + } + + return ERROR_OK; +} + +static int stm32x_protect(struct flash_bank *bank, int set, int first, int last) +{ + struct target *target = bank->target; + struct stm32h7x_flash_bank *stm32x_info = bank->driver_priv; + + if (target->state != TARGET_HALTED) { + LOG_ERROR("Target not halted"); + return ERROR_TARGET_NOT_HALTED; + } + /* read protection settings */ + int retval = stm32x_read_options(bank); + if (retval != ERROR_OK) { + LOG_DEBUG("unable to read option bytes"); + return retval; + } + + for (int i = first; i <= last; i++) { + if (stm32x_info->flash_base == FLASH_REG_BASE_B0) { + if (set) + stm32x_info->option_bytes.protection &= ~(1 << i); + else + stm32x_info->option_bytes.protection |= (1 << i); + } else { + if (set) + stm32x_info->option_bytes.protection2 &= ~(1 << i); + else + stm32x_info->option_bytes.protection2 |= (1 << i); + } + } + + LOG_INFO("stm32x_protect, option_bytes written WRP1 0x%x , WRP2 0x%x", + (stm32x_info->option_bytes.protection & 0xff), (stm32x_info->option_bytes.protection2 & 0xff)); + + retval = stm32x_write_options(bank); + if (retval != ERROR_OK) + return retval; + + return ERROR_OK; +} + +static int stm32x_write_block(struct flash_bank *bank, const uint8_t *buffer, + uint32_t offset, uint32_t count) +{ + struct target *target = bank->target; + /* + * If the size of the data part of the buffer is not a multiple of FLASH_BLOCK_SIZE, we get + * "corrupted fifo read" pointer in target_run_flash_async_algorithm() + */ + uint32_t data_size = 512 * FLASH_BLOCK_SIZE; /* 16384 */ + uint32_t buffer_size = 8 + data_size; + struct working_area *write_algorithm; + struct working_area *source; + uint32_t address = bank->base + offset; + struct reg_param reg_params[5]; + struct armv7m_algorithm armv7m_info; + struct stm32h7x_flash_bank *stm32x_info = bank->driver_priv; + int retval = ERROR_OK; + + /* see contrib/loaders/flash/smt32h7x.S for src */ + static const uint8_t stm32x_flash_write_code[] = { + /* : */ + 0x45, 0x68, /* ldr r5, [r0, #4] */ + /* : */ + 0x06, 0x68, /* ldr r6, [r0, #0] */ + 0x26, 0xb3, /* cbz r6, */ + 0x76, 0x1b, /* subs r6, r6, r5 */ + 0x42, 0xbf, /* ittt mi */ + 0x76, 0x18, /* addmi r6, r6, r1 */ + 0x36, 0x1a, /* submi r6, r6, r0 */ + 0x08, 0x3e, /* submi r6, #8 */ + 0x20, 0x2e, /* cmp r6, #32 */ + 0xf6, 0xd3, /* bcc.n */ + 0x4f, 0xf0, 0x32, 0x06, /* mov.w r6, #STM32_PROG */ + 0xe6, 0x60, /* str r6, [r4, #STM32_FLASH_CR_OFFSET] */ + 0x4f, 0xf0, 0x08, 0x07, /* mov.w r7, #8 */ + /* : */ + 0x55, 0xf8, 0x04, 0x6b, /* ldr.w r6, [r5], #4 */ + 0x42, 0xf8, 0x04, 0x6b, /* str.w r6, [r2], #4 */ + 0xbf, 0xf3, 0x4f, 0x8f, /* dsb sy */ + 0x8d, 0x42, /* cmp r5, r1 */ + 0x28, 0xbf, /* it cs */ + 0x00, 0xf1, 0x08, 0x05, /* addcs.w r5, r0, #8 */ + 0x01, 0x3f, /* subs r7, #1 */ + 0xf3, 0xd1, /* bne.n */ + /* : */ + 0x26, 0x69, /* ldr r6, [r4, #STM32_FLASH_SR_OFFSET] */ + 0x16, 0xf0, 0x01, 0x0f, /* tst.w r6, #STM32_SR_BUSY_MASK */ + 0xfb, 0xd1, /* bne.n */ + 0x05, 0x4f, /* ldr r7, [pc, #20] ; () */ + 0x3e, 0x42, /* tst r6, r7 */ + 0x03, 0xd1, /* bne.n */ + 0x45, 0x60, /* str r5, [r0, #4] */ + 0x01, 0x3b, /* subs r3, #1 */ + 0xdb, 0xd1, /* bne.n */ + 0x01, 0xe0, /* b.n */ + /* : */ + 0x00, 0x27, /* movs r7, #0 */ + 0x47, 0x60, /* str r7, [r0, #4] */ + /* : */ + 0x30, 0x46, /* mov r0, r6 */ + 0x00, 0xbe, /* bkpt 0x0000 */ + /* : */ + 0x00, 0x00, 0xee, 0x03 /* .word 0x03ee0000 ; (STM32_SR_ERROR_MASK) */ + }; + + if (target_alloc_working_area(target, sizeof(stm32x_flash_write_code), + &write_algorithm) != ERROR_OK) { + LOG_WARNING("no working area available, can't do block memory writes"); + return ERROR_TARGET_RESOURCE_NOT_AVAILABLE; + } + + retval = target_write_buffer(target, write_algorithm->address, + sizeof(stm32x_flash_write_code), + stm32x_flash_write_code); + if (retval != ERROR_OK) + return retval; + + /* memory buffer */ + while (target_alloc_working_area_try(target, buffer_size, &source) != ERROR_OK) { + data_size /= 2; + buffer_size = 8 + data_size; + if (data_size <= 256) { + /* we already allocated the writing code, but failed to get a + * buffer, free the algorithm */ + target_free_working_area(target, write_algorithm); + + LOG_WARNING("no large enough working area available, can't do block memory writes"); + return ERROR_TARGET_RESOURCE_NOT_AVAILABLE; + } + } + + LOG_DEBUG("target_alloc_working_area_try : buffer_size -> 0x%x", buffer_size); + + armv7m_info.common_magic = ARMV7M_COMMON_MAGIC; + armv7m_info.core_mode = ARM_MODE_THREAD; + + init_reg_param(®_params[0], "r0", 32, PARAM_IN_OUT); /* buffer start, status (out) */ + init_reg_param(®_params[1], "r1", 32, PARAM_OUT); /* buffer end */ + init_reg_param(®_params[2], "r2", 32, PARAM_OUT); /* target address */ + init_reg_param(®_params[3], "r3", 32, PARAM_OUT); /* count (word-256 bits) */ + init_reg_param(®_params[4], "r4", 32, PARAM_OUT); /* flash reg base */ + + buf_set_u32(reg_params[0].value, 0, 32, source->address); + buf_set_u32(reg_params[1].value, 0, 32, source->address + source->size); + buf_set_u32(reg_params[2].value, 0, 32, address); + buf_set_u32(reg_params[3].value, 0, 32, count); + buf_set_u32(reg_params[4].value, 0, 32, stm32x_info->flash_base); + + retval = target_run_flash_async_algorithm(target, + buffer, + count, + FLASH_BLOCK_SIZE, + 0, NULL, + 5, reg_params, + source->address, source->size, + write_algorithm->address, 0, + &armv7m_info); + + if (retval == ERROR_FLASH_OPERATION_FAILED) { + LOG_INFO("error executing stm32h7x flash write algorithm"); + + uint32_t flash_sr = buf_get_u32(reg_params[0].value, 0, 32); + + if (flash_sr & FLASH_WRPERR) + LOG_ERROR("flash memory write protected"); + + if ((flash_sr & FLASH_ERROR) != 0) { + LOG_ERROR("flash write failed, FLASH_SR = %08" PRIx32, flash_sr); + /* Clear error + EOP flags but report errors */ + target_write_u32(target, stm32x_get_flash_reg(bank, FLASH_CCR), flash_sr); + retval = ERROR_FAIL; + } + } + + target_free_working_area(target, source); + target_free_working_area(target, write_algorithm); + + destroy_reg_param(®_params[0]); + destroy_reg_param(®_params[1]); + destroy_reg_param(®_params[2]); + destroy_reg_param(®_params[3]); + destroy_reg_param(®_params[4]); + return retval; +} + +static int stm32x_write(struct flash_bank *bank, const uint8_t *buffer, + uint32_t offset, uint32_t count) +{ + struct target *target = bank->target; + uint32_t address = bank->base + offset; + int retval, retval2; + + if (bank->target->state != TARGET_HALTED) { + LOG_ERROR("Target not halted"); + return ERROR_TARGET_NOT_HALTED; + } + + if (offset % FLASH_BLOCK_SIZE) { + LOG_WARNING("offset 0x%" PRIx32 " breaks required 32-byte alignment", offset); + return ERROR_FLASH_DST_BREAKS_ALIGNMENT; + } + + retval = stm32x_unlock_reg(bank); + if (retval != ERROR_OK) + return retval; + + uint32_t blocks_remaining = count / FLASH_BLOCK_SIZE; + uint32_t bytes_remaining = count % FLASH_BLOCK_SIZE; + + /* multiple words (32-bytes) to be programmed in block */ + if (blocks_remaining) { + retval = stm32x_write_block(bank, buffer, offset, blocks_remaining); + if (retval != ERROR_OK) { + if (retval == ERROR_TARGET_RESOURCE_NOT_AVAILABLE) { + /* if block write failed (no sufficient working area), + * we use normal (slow) dword accesses */ + LOG_WARNING("couldn't use block writes, falling back to single memory accesses"); + } + } else { + buffer += blocks_remaining * FLASH_BLOCK_SIZE; + address += blocks_remaining * FLASH_BLOCK_SIZE; + blocks_remaining = 0; + } + if ((retval != ERROR_OK) && (retval != ERROR_TARGET_RESOURCE_NOT_AVAILABLE)) + goto flash_lock; + } + + /* + Standard programming + The Flash memory programming sequence is as follows: + 1. Check that no main Flash memory operation is ongoing by checking the BSY bit in the + FLASH_SR register. + 2. Set the PG bit in the FLASH_CR register + 3. 8 x Word access (or Force Write FW) + 4. Wait for the BSY bit to be cleared + */ + while (blocks_remaining > 0) { + retval = target_write_u32(target, stm32x_get_flash_reg(bank, FLASH_CR), FLASH_PG | FLASH_PSIZE_64); + if (retval != ERROR_OK) + goto flash_lock; + + retval = target_write_buffer(target, address, FLASH_BLOCK_SIZE, buffer); + if (retval != ERROR_OK) + goto flash_lock; + + retval = stm32x_wait_status_busy(bank, FLASH_WRITE_TIMEOUT); + if (retval != ERROR_OK) + goto flash_lock; + + buffer += FLASH_BLOCK_SIZE; + address += FLASH_BLOCK_SIZE; + blocks_remaining--; + } + + if (bytes_remaining) { + retval = target_write_u32(target, stm32x_get_flash_reg(bank, FLASH_CR), FLASH_PG | FLASH_PSIZE_64); + if (retval != ERROR_OK) + goto flash_lock; + + retval = target_write_buffer(target, address, bytes_remaining, buffer); + if (retval != ERROR_OK) + goto flash_lock; + + /* Force Write buffer of FLASH_BLOCK_SIZE = 32 bytes */ + retval = target_write_u32(target, stm32x_get_flash_reg(bank, FLASH_CR), FLASH_PG | FLASH_PSIZE_64 | FLASH_FW); + if (retval != ERROR_OK) + goto flash_lock; + + retval = stm32x_wait_status_busy(bank, FLASH_WRITE_TIMEOUT); + if (retval != ERROR_OK) + goto flash_lock; + } + +flash_lock: + retval2 = stm32x_lock_reg(bank); + if (retval2 != ERROR_OK) + LOG_ERROR("error during the lock of flash"); + + if (retval == ERROR_OK) + retval = retval2; + + return retval; +} + +static void setup_sector(struct flash_bank *bank, int start, int num, int size) +{ + for (int i = start; i < (start + num) ; i++) { + assert(i < bank->num_sectors); + bank->sectors[i].offset = bank->size; + bank->sectors[i].size = size; + bank->size += bank->sectors[i].size; + } +} + +static int stm32x_read_id_code(struct flash_bank *bank, uint32_t *id) +{ + /* read stm32 device id register */ + int retval = target_read_u32(bank->target, DBGMCU_IDCODE_REGISTER, id); + if (retval != ERROR_OK) + return retval; + return ERROR_OK; +} + +static int stm32x_probe(struct flash_bank *bank) +{ + struct target *target = bank->target; + struct stm32h7x_flash_bank *stm32x_info = bank->driver_priv; + int i; + uint16_t flash_size_in_kb; + uint32_t device_id; + uint32_t base_address = FLASH_BANK0_ADDRESS; + uint32_t second_bank_base; + + stm32x_info->probed = 0; + stm32x_info->part_info = NULL; + + int retval = stm32x_read_id_code(bank, &stm32x_info->idcode); + if (retval != ERROR_OK) + return retval; + + LOG_DEBUG("device id = 0x%08" PRIx32 "", stm32x_info->idcode); + + device_id = stm32x_info->idcode & 0xfff; + + for (unsigned int n = 0; n < ARRAY_SIZE(stm32h7x_parts); n++) { + if (device_id == stm32h7x_parts[n].id) + stm32x_info->part_info = &stm32h7x_parts[n]; + } + if (!stm32x_info->part_info) { + LOG_WARNING("Cannot identify target as a STM32H7xx family."); + return ERROR_FAIL; + } else { + LOG_INFO("Device: %s", stm32x_info->part_info->device_str); + } + + /* update the address of controller from data base */ + stm32x_info->flash_base = stm32x_info->part_info->flash_base; + + /* get flash size from target */ + retval = target_read_u16(target, stm32x_info->part_info->fsize_base, &flash_size_in_kb); + if (retval != ERROR_OK) { + /* read error when device has invalid value, set max flash size */ + flash_size_in_kb = stm32x_info->part_info->max_flash_size_kb; + } else + LOG_INFO("flash size probed value %d", flash_size_in_kb); + + /* Lower flash size devices are single bank */ + if (stm32x_info->part_info->has_dual_bank && (flash_size_in_kb > stm32x_info->part_info->first_bank_size_kb)) { + /* Use the configured base address to determine if this is the first or second flash bank. + * Verify that the base address is reasonably correct and determine the flash bank size + */ + second_bank_base = base_address + stm32x_info->part_info->first_bank_size_kb * 1024; + if (bank->base == second_bank_base) { + /* This is the second bank */ + base_address = second_bank_base; + flash_size_in_kb = flash_size_in_kb - stm32x_info->part_info->first_bank_size_kb; + /* bank1 also uses a register offset */ + stm32x_info->flash_base = FLASH_REG_BASE_B1; + } else if (bank->base == base_address) { + /* This is the first bank */ + flash_size_in_kb = stm32x_info->part_info->first_bank_size_kb; + } else { + LOG_WARNING("STM32H flash bank base address config is incorrect." + " 0x%" PRIx32 " but should rather be 0x%" PRIx32 " or 0x%" PRIx32, + bank->base, base_address, second_bank_base); + return ERROR_FAIL; + } + LOG_INFO("STM32H flash has dual banks. Bank (%d) size is %dkb, base address is 0x%" PRIx32, + bank->bank_number, flash_size_in_kb, base_address); + } else { + LOG_INFO("STM32H flash size is %dkb, base address is 0x%" PRIx32, flash_size_in_kb, base_address); + } + + /* if the user sets the size manually then ignore the probed value + * this allows us to work around devices that have an invalid flash size register value */ + if (stm32x_info->user_bank_size) { + LOG_INFO("ignoring flash probed value, using configured bank size"); + flash_size_in_kb = stm32x_info->user_bank_size / 1024; + } else if (flash_size_in_kb == 0xffff) { + /* die flash size */ + flash_size_in_kb = stm32x_info->part_info->max_flash_size_kb; + } + + /* did we assign flash size? */ + assert(flash_size_in_kb != 0xffff); + + /* calculate numbers of pages */ + int num_pages = flash_size_in_kb / stm32x_info->part_info->page_size; + + /* check that calculation result makes sense */ + assert(num_pages > 0); + + if (bank->sectors) { + free(bank->sectors); + bank->sectors = NULL; + } + + bank->base = base_address; + bank->num_sectors = num_pages; + bank->sectors = malloc(sizeof(struct flash_sector) * num_pages); + if (bank->sectors == NULL) { + LOG_ERROR("failed to allocate bank sectors"); + return ERROR_FAIL; + } + bank->size = 0; + + /* fixed memory */ + setup_sector(bank, 0, num_pages, stm32x_info->part_info->page_size * 1024); + + for (i = 0; i < num_pages; i++) { + bank->sectors[i].is_erased = -1; + bank->sectors[i].is_protected = 0; + } + + stm32x_info->probed = 1; + return ERROR_OK; +} + +static int stm32x_auto_probe(struct flash_bank *bank) +{ + struct stm32h7x_flash_bank *stm32x_info = bank->driver_priv; + + if (stm32x_info->probed) + return ERROR_OK; + + return stm32x_probe(bank); +} + +/* This method must return a string displaying information about the bank */ +static int stm32x_get_info(struct flash_bank *bank, char *buf, int buf_size) +{ + struct stm32h7x_flash_bank *stm32x_info = bank->driver_priv; + const struct stm32h7x_part_info *info = stm32x_info->part_info; + + if (!stm32x_info->probed) { + int retval = stm32x_probe(bank); + if (retval != ERROR_OK) { + snprintf(buf, buf_size, "Unable to find bank information."); + return retval; + } + } + + if (info) { + const char *rev_str = NULL; + uint16_t rev_id = stm32x_info->idcode >> 16; + + for (unsigned int i = 0; i < info->num_revs; i++) + if (rev_id == info->revs[i].rev) + rev_str = info->revs[i].str; + + if (rev_str != NULL) { + snprintf(buf, buf_size, "%s - Rev: %s", + stm32x_info->part_info->device_str, rev_str); + } else { + snprintf(buf, buf_size, + "%s - Rev: unknown (0x%04x)", + stm32x_info->part_info->device_str, rev_id); + } + } else { + snprintf(buf, buf_size, "Cannot identify target as a STM32H7x"); + return ERROR_FAIL; + } + return ERROR_OK; +} + +COMMAND_HANDLER(stm32x_handle_lock_command) +{ + struct target *target = NULL; + struct stm32h7x_flash_bank *stm32x_info = NULL; + + if (CMD_ARGC < 1) + return ERROR_COMMAND_SYNTAX_ERROR; + + struct flash_bank *bank; + int retval = CALL_COMMAND_HANDLER(flash_command_get_bank, 0, &bank); + if (ERROR_OK != retval) + return retval; + + stm32x_info = bank->driver_priv; + target = bank->target; + + /* if we have a dual flash bank device then + * we need to perform option byte lock on bank0 only */ + if (stm32x_info->flash_base != FLASH_REG_BASE_B0) { + LOG_ERROR("Option Byte Lock Operation must use bank0"); + return ERROR_FLASH_OPERATION_FAILED; + } + + if (target->state != TARGET_HALTED) { + LOG_ERROR("Target not halted"); + return ERROR_TARGET_NOT_HALTED; + } + + if (stm32x_read_options(bank) != ERROR_OK) { + command_print(CMD_CTX, "%s failed to read options", + bank->driver->name); + return ERROR_OK; + } + /* set readout protection */ + stm32x_info->option_bytes.RDP = 0; + + if (stm32x_write_options(bank) != ERROR_OK) { + command_print(CMD_CTX, "%s failed to lock device", + bank->driver->name); + return ERROR_OK; + } + command_print(CMD_CTX, "%s locked", bank->driver->name); + + return ERROR_OK; +} + +COMMAND_HANDLER(stm32x_handle_unlock_command) +{ + struct target *target = NULL; + struct stm32h7x_flash_bank *stm32x_info = NULL; + + if (CMD_ARGC < 1) + return ERROR_COMMAND_SYNTAX_ERROR; + + struct flash_bank *bank; + int retval = CALL_COMMAND_HANDLER(flash_command_get_bank, 0, &bank); + if (ERROR_OK != retval) + return retval; + + stm32x_info = bank->driver_priv; + target = bank->target; + + /* if we have a dual flash bank device then + * we need to perform option byte unlock on bank0 only */ + if (stm32x_info->flash_base != FLASH_REG_BASE_B0) { + LOG_ERROR("Option Byte Unlock Operation must use bank0"); + return ERROR_FLASH_OPERATION_FAILED; + } + + if (target->state != TARGET_HALTED) { + LOG_ERROR("Target not halted"); + return ERROR_TARGET_NOT_HALTED; + } + + if (stm32x_read_options(bank) != ERROR_OK) { + command_print(CMD_CTX, "%s failed to read options", bank->driver->name); + return ERROR_OK; + } + + /* clear readout protection option byte + * this will also force a device unlock if set */ + stm32x_info->option_bytes.RDP = 0xAA; + + if (stm32x_write_options(bank) != ERROR_OK) { + command_print(CMD_CTX, "%s failed to unlock device", bank->driver->name); + return ERROR_OK; + } + command_print(CMD_CTX, "%s unlocked.\n", bank->driver->name); + + return ERROR_OK; +} + +static int stm32x_mass_erase(struct flash_bank *bank) +{ + int retval; + struct target *target = bank->target; + + if (target->state != TARGET_HALTED) { + LOG_ERROR("Target not halted"); + return ERROR_TARGET_NOT_HALTED; + } + + retval = stm32x_unlock_reg(bank); + if (retval != ERROR_OK) + return retval; + + /* mass erase flash memory bank */ + retval = target_write_u32(target, stm32x_get_flash_reg(bank, FLASH_CR), FLASH_BER_CMD | FLASH_PSIZE_64); + if (retval != ERROR_OK) + return retval; + + retval = target_write_u32(target, stm32x_get_flash_reg(bank, FLASH_CR), + FLASH_BER_CMD | FLASH_PSIZE_64 | FLASH_START); + if (retval != ERROR_OK) + return retval; + + retval = stm32x_wait_status_busy(bank, 30000); + if (retval != ERROR_OK) + return retval; + + retval = stm32x_lock_reg(bank); + if (retval != ERROR_OK) { + LOG_ERROR("error during the lock of flash"); + return retval; + } + return ERROR_OK; +} + +COMMAND_HANDLER(stm32x_handle_mass_erase_command) +{ + int i; + + if (CMD_ARGC < 1) { + command_print(CMD_CTX, "stm32h7x mass_erase "); + return ERROR_COMMAND_SYNTAX_ERROR; + } + + struct flash_bank *bank; + int retval = CALL_COMMAND_HANDLER(flash_command_get_bank, 0, &bank); + if (ERROR_OK != retval) + return retval; + + retval = stm32x_mass_erase(bank); + if (retval == ERROR_OK) { + /* set all sectors as erased */ + for (i = 0; i < bank->num_sectors; i++) + bank->sectors[i].is_erased = 1; + + command_print(CMD_CTX, "stm32h7x mass erase complete"); + } else { + command_print(CMD_CTX, "stm32h7x mass erase failed"); + } + + return retval; +} + +static const struct command_registration stm32x_exec_command_handlers[] = { + { + .name = "lock", + .handler = stm32x_handle_lock_command, + .mode = COMMAND_EXEC, + .usage = "bank_id", + .help = "Lock entire flash device.", + }, + { + .name = "unlock", + .handler = stm32x_handle_unlock_command, + .mode = COMMAND_EXEC, + .usage = "bank_id", + .help = "Unlock entire protected flash device.", + }, + { + .name = "mass_erase", + .handler = stm32x_handle_mass_erase_command, + .mode = COMMAND_EXEC, + .usage = "bank_id", + .help = "Erase entire flash device.", + }, + COMMAND_REGISTRATION_DONE +}; + +static const struct command_registration stm32x_command_handlers[] = { + { + .name = "stm32h7x", + .mode = COMMAND_ANY, + .help = "stm32h7x flash command group", + .usage = "", + .chain = stm32x_exec_command_handlers, + }, + COMMAND_REGISTRATION_DONE +}; + +struct flash_driver stm32h7x_flash = { + .name = "stm32h7x", + .commands = stm32x_command_handlers, + .flash_bank_command = stm32x_flash_bank_command, + .erase = stm32x_erase, + .protect = stm32x_protect, + .write = stm32x_write, + .read = default_flash_read, + .probe = stm32x_probe, + .auto_probe = stm32x_auto_probe, + .erase_check = default_flash_blank_check, + .protect_check = stm32x_protect_check, + .info = stm32x_get_info, +}; diff --git a/src/flash/nor/stm32lx.c b/src/flash/nor/stm32lx.c index 0c2fddc92..fdfaad4cf 100644 --- a/src/flash/nor/stm32lx.c +++ b/src/flash/nor/stm32lx.c @@ -726,16 +726,13 @@ reset_pg_and_lock: static int stm32lx_read_id_code(struct target *target, uint32_t *id) { - /* read stm32 device id register */ - int retval = target_read_u32(target, DBGMCU_IDCODE, id); - if (retval != ERROR_OK) - return retval; - - /* STM32L0 parts will have 0 there, try reading the L0's location for - * DBG_IDCODE in case this is an L0 part. */ - if (*id == 0) + struct armv7m_common *armv7m = target_to_armv7m(target); + int retval; + if (armv7m->arm.is_armv6m == true) retval = target_read_u32(target, DBGMCU_IDCODE_L0, id); - + else + /* read stm32 device id register */ + retval = target_read_u32(target, DBGMCU_IDCODE, id); return retval; } diff --git a/src/flash/nor/xmc4xxx.c b/src/flash/nor/xmc4xxx.c index 02df46a3f..5677ef0f1 100644 --- a/src/flash/nor/xmc4xxx.c +++ b/src/flash/nor/xmc4xxx.c @@ -931,13 +931,13 @@ static int xmc4xxx_get_info_command(struct flash_bank *bank, char *buf, int buf_ /* If OTP Write protection is enabled (User 2), list each * sector that has it enabled */ - char otp_str[8]; + char otp_str[14]; if (otp_enabled) { strcat(prot_str, "\nOTP Protection is enabled for sectors:\n"); for (int i = 0; i < bank->num_sectors; i++) { if (fb->write_prot_otp[i]) { snprintf(otp_str, sizeof(otp_str), "- %d\n", i); - strncat(prot_str, otp_str, ARRAY_SIZE(otp_str)); + strncat(prot_str, otp_str, sizeof(prot_str) - strlen(prot_str) - 1); } } } diff --git a/src/helper/command.c b/src/helper/command.c index 5deaee859..40e8b0582 100644 --- a/src/helper/command.c +++ b/src/helper/command.c @@ -1456,8 +1456,8 @@ COMMAND_HELPER(handle_command_parse_bool, bool *out, const char *label) LOG_ERROR("%s: argument '%s' is not valid", CMD_NAME, in); return ERROR_COMMAND_SYNTAX_ERROR; } - /* fall through */ } + /* fallthrough */ case 0: LOG_INFO("%s is %s", label, *out ? "enabled" : "disabled"); break; diff --git a/src/helper/options.c b/src/helper/options.c index 1cfa55376..12755e010 100644 --- a/src/helper/options.c +++ b/src/helper/options.c @@ -37,6 +37,9 @@ #ifdef HAVE_SYS_SYSCTL_H #include #endif +#if IS_WIN32 && !IS_CYGWIN +#include +#endif static int help_flag, version_flag; diff --git a/src/helper/replacements.h b/src/helper/replacements.h index 1e2fbf20f..f43b7e0f3 100644 --- a/src/helper/replacements.h +++ b/src/helper/replacements.h @@ -199,6 +199,17 @@ static inline int close_socket(int sock) #endif } +static inline void socket_block(int fd) +{ +#ifdef _WIN32 + unsigned long nonblock = 0; + ioctlsocket(fd, FIONBIO, &nonblock); +#else + int oldopts = fcntl(fd, F_GETFL, 0); + fcntl(fd, F_SETFL, oldopts & ~O_NONBLOCK); +#endif +} + static inline void socket_nonblock(int fd) { #ifdef _WIN32 diff --git a/src/jtag/core.c b/src/jtag/core.c index e21d47644..71a731de1 100644 --- a/src/jtag/core.c +++ b/src/jtag/core.c @@ -838,108 +838,57 @@ int default_interface_jtag_execute_queue(void) int result = jtag->execute_queue(); -#if 0 - // TODO: I like these better than some of the other JTAG debug statements, - // but having both is silly. struct jtag_command *cmd = jtag_command_queue; while (debug_level >= LOG_LVL_DEBUG && cmd) { switch (cmd->type) { case JTAG_SCAN: -#if 0 - LOG_DEBUG("JTAG %s SCAN to %s", + DEBUG_JTAG_IO("JTAG %s SCAN to %s", cmd->cmd.scan->ir_scan ? "IR" : "DR", tap_state_name(cmd->cmd.scan->end_state)); for (int i = 0; i < cmd->cmd.scan->num_fields; i++) { struct scan_field *field = cmd->cmd.scan->fields + i; if (field->out_value) { char *str = buf_to_str(field->out_value, field->num_bits, 16); - LOG_DEBUG(" %db out: %s", field->num_bits, str); + DEBUG_JTAG_IO(" %db out: %s", field->num_bits, str); free(str); } if (field->in_value) { char *str = buf_to_str(field->in_value, field->num_bits, 16); - LOG_DEBUG(" %db in: %s", field->num_bits, str); + DEBUG_JTAG_IO(" %db in: %s", field->num_bits, str); free(str); } - if (field->check_value) { - char *str = buf_to_str(field->check_value, field->num_bits, 16); - LOG_DEBUG(" %db check: %s", field->num_bits, str); - free(str); - } - if (field->check_mask) { - char *str = buf_to_str(field->check_mask, field->num_bits, 16); - LOG_DEBUG(" %db mask: %s", field->num_bits, str); - free(str); - } - } -#endif - { - uint8_t *buf = NULL; - int scan_bits = jtag_build_buffer(cmd->cmd.scan, &buf); - char *str_out = buf_to_str(buf, scan_bits, 16); - free(buf); - LOG_DEBUG("vvv jtag_scan(%d, %d, %d'h%s, %d); // %s", - cmd->cmd.scan->ir_scan, - scan_bits, - scan_bits, str_out, - cmd->cmd.scan->end_state, tap_state_name(cmd->cmd.scan->end_state)); - free(str_out); - - struct scan_field *last_field = cmd->cmd.scan->fields + cmd->cmd.scan->num_fields - 1; - if (last_field->in_value) { - char *str_in = buf_to_str(last_field->in_value, last_field->num_bits, 16); - LOG_DEBUG("vvv jtag_check_tdo(%d, %d'h%s);", - last_field->num_bits, - last_field->num_bits, str_in); - free(str_in); - } } break; case JTAG_TLR_RESET: -#if 0 - LOG_DEBUG("JTAG TLR RESET to %s", - tap_state_name(cmd->cmd.statemove->end_state)); -#endif - LOG_DEBUG("vvv jtag_tlr_reset(%d); // %s", - cmd->cmd.statemove->end_state, + DEBUG_JTAG_IO("JTAG TLR RESET to %s", tap_state_name(cmd->cmd.statemove->end_state)); break; case JTAG_RUNTEST: -#if 0 - LOG_DEBUG("JTAG RUNTEST %d cycles to %s", + DEBUG_JTAG_IO("JTAG RUNTEST %d cycles to %s", cmd->cmd.runtest->num_cycles, tap_state_name(cmd->cmd.runtest->end_state)); -#endif - LOG_DEBUG("vvv jtag_runtest(%d, %d); // %s", - cmd->cmd.runtest->num_cycles, - cmd->cmd.runtest->end_state, - tap_state_name(cmd->cmd.runtest->end_state)); break; case JTAG_RESET: { -#if 0 const char *reset_str[3] = { "leave", "deassert", "assert" }; - LOG_DEBUG("JTAG RESET %s TRST, %s SRST", + DEBUG_JTAG_IO("JTAG RESET %s TRST, %s SRST", reset_str[cmd->cmd.reset->trst + 1], reset_str[cmd->cmd.reset->srst + 1]); -#endif - LOG_DEBUG("vvv jtag_reset(%d, %d);", - cmd->cmd.reset->trst, cmd->cmd.reset->srst); } break; case JTAG_PATHMOVE: - LOG_DEBUG("JTAG PATHMOVE (TODO)"); + DEBUG_JTAG_IO("JTAG PATHMOVE (TODO)"); break; case JTAG_SLEEP: - LOG_DEBUG("JTAG SLEEP (TODO)"); + DEBUG_JTAG_IO("JTAG SLEEP (TODO)"); break; case JTAG_STABLECLOCKS: - LOG_DEBUG("JTAG STABLECLOCKS (TODO)"); + DEBUG_JTAG_IO("JTAG STABLECLOCKS (TODO)"); break; case JTAG_TMS: - LOG_DEBUG("JTAG STABLECLOCKS (TODO)"); + DEBUG_JTAG_IO("JTAG STABLECLOCKS (TODO)"); break; default: LOG_ERROR("Unknown JTAG command: %d", cmd->type); @@ -947,7 +896,6 @@ int default_interface_jtag_execute_queue(void) } cmd = cmd->next; } -#endif return result; } diff --git a/src/jtag/drivers/cmsis_dap_usb.c b/src/jtag/drivers/cmsis_dap_usb.c index 19c3b19c9..345c1fd7e 100644 --- a/src/jtag/drivers/cmsis_dap_usb.c +++ b/src/jtag/drivers/cmsis_dap_usb.c @@ -958,11 +958,14 @@ static int cmsis_dap_init(void) retval = cmsis_dap_cmd_DAP_TFER_Configure(0, 64, 0); if (retval != ERROR_OK) return ERROR_FAIL; - /* Data Phase (bit 2) must be set to 1 if sticky overrun - * detection is enabled */ - retval = cmsis_dap_cmd_DAP_SWD_Configure(0); /* 1 TRN, no Data Phase */ - if (retval != ERROR_OK) - return ERROR_FAIL; + + if (swd_mode) { + /* Data Phase (bit 2) must be set to 1 if sticky overrun + * detection is enabled */ + retval = cmsis_dap_cmd_DAP_SWD_Configure(0); /* 1 TRN, no Data Phase */ + if (retval != ERROR_OK) + return ERROR_FAIL; + } retval = cmsis_dap_cmd_DAP_LED(0x03); /* Both LEDs on */ if (retval != ERROR_OK) diff --git a/src/jtag/drivers/ftdi.c b/src/jtag/drivers/ftdi.c index 342e32102..32876bac5 100644 --- a/src/jtag/drivers/ftdi.c +++ b/src/jtag/drivers/ftdi.c @@ -855,6 +855,7 @@ COMMAND_HANDLER(ftdi_handle_set_signal_command) ftdi_set_signal(sig, *CMD_ARGV[1]); break; } + /* fallthrough */ default: LOG_ERROR("unknown signal level '%s', use 0, 1 or z", CMD_ARGV[1]); return ERROR_COMMAND_SYNTAX_ERROR; @@ -1217,14 +1218,17 @@ static int ftdi_swd_switch_seq(enum swd_special_seq seq) switch (seq) { case LINE_RESET: LOG_DEBUG("SWD line reset"); + ftdi_swd_swdio_en(true); mpsse_clock_data_out(mpsse_ctx, swd_seq_line_reset, 0, swd_seq_line_reset_len, SWD_MODE); break; case JTAG_TO_SWD: LOG_DEBUG("JTAG-to-SWD"); + ftdi_swd_swdio_en(true); mpsse_clock_data_out(mpsse_ctx, swd_seq_jtag_to_swd, 0, swd_seq_jtag_to_swd_len, SWD_MODE); break; case SWD_TO_JTAG: LOG_DEBUG("SWD-to-JTAG"); + ftdi_swd_swdio_en(true); mpsse_clock_data_out(mpsse_ctx, swd_seq_swd_to_jtag, 0, swd_seq_swd_to_jtag_len, SWD_MODE); break; default: diff --git a/src/jtag/drivers/jlink.c b/src/jtag/drivers/jlink.c index bd3c5e03c..132ef06e9 100644 --- a/src/jtag/drivers/jlink.c +++ b/src/jtag/drivers/jlink.c @@ -322,7 +322,7 @@ static int jlink_speed(int speed) if (ret != JAYLINK_OK) { LOG_ERROR("jaylink_get_speeds() failed: %s.", - jaylink_strerror_name(ret)); + jaylink_strerror(ret)); return ERROR_JTAG_DEVICE_ERROR; } @@ -349,7 +349,7 @@ static int jlink_speed(int speed) if (ret != JAYLINK_OK) { LOG_ERROR("jaylink_set_speed() failed: %s.", - jaylink_strerror_name(ret)); + jaylink_strerror(ret)); return ERROR_JTAG_DEVICE_ERROR; } @@ -378,7 +378,7 @@ static bool read_device_config(struct device_config *cfg) if (ret != JAYLINK_OK) { LOG_ERROR("jaylink_read_raw_config() failed: %s.", - jaylink_strerror_name(ret)); + jaylink_strerror(ret)); return false; } @@ -409,7 +409,7 @@ static int select_interface(void) if (ret != JAYLINK_OK) { LOG_ERROR("jaylink_get_available_interfaces() failed: %s.", - jaylink_strerror_name(ret)); + jaylink_strerror(ret)); return ERROR_JTAG_INIT_FAILED; } @@ -422,7 +422,7 @@ static int select_interface(void) if (ret < 0) { LOG_ERROR("jaylink_select_interface() failed: %s.", - jaylink_strerror_name(ret)); + jaylink_strerror(ret)); return ERROR_JTAG_INIT_FAILED; } @@ -442,8 +442,7 @@ static int jlink_register(void) ret = jaylink_register(devh, &conn, connlist, &count); if (ret != JAYLINK_OK) { - LOG_ERROR("jaylink_register() failed: %s.", - jaylink_strerror_name(ret)); + LOG_ERROR("jaylink_register() failed: %s.", jaylink_strerror(ret)); return ERROR_FAIL; } @@ -482,7 +481,7 @@ static bool adjust_swd_buffer_size(void) if (ret != JAYLINK_OK) { LOG_ERROR("jaylink_get_free_memory() failed: %s.", - jaylink_strerror_name(ret)); + jaylink_strerror(ret)); return false; } @@ -523,6 +522,9 @@ static int jaylink_log_handler(const struct jaylink_context *ctx, case JAYLINK_LOG_LEVEL_DEBUG: tmp = LOG_LVL_DEBUG; break; + case JAYLINK_LOG_LEVEL_DEBUG_IO: + tmp = LOG_LVL_DEBUG_IO; + break; default: tmp = LOG_LVL_WARNING; } @@ -544,15 +546,21 @@ static int jlink_init(void) struct jaylink_hardware_status hwstatus; enum jaylink_usb_address address; size_t length; + size_t num_devices; + uint32_t host_interfaces; LOG_DEBUG("Using libjaylink %s (compiled with %s).", jaylink_version_package_get_string(), JAYLINK_VERSION_PACKAGE_STRING); + if (!jaylink_library_has_cap(JAYLINK_CAP_HIF_USB) && use_usb_address) { + LOG_ERROR("J-Link driver does not support USB devices."); + return ERROR_JTAG_INIT_FAILED; + } + ret = jaylink_init(&jayctx); if (ret != JAYLINK_OK) { - LOG_ERROR("jaylink_init() failed: %s.", - jaylink_strerror_name(ret)); + LOG_ERROR("jaylink_init() failed: %s.", jaylink_strerror(ret)); return ERROR_JTAG_INIT_FAILED; } @@ -560,34 +568,42 @@ static int jlink_init(void) if (ret != JAYLINK_OK) { LOG_ERROR("jaylink_log_set_callback() failed: %s.", - jaylink_strerror_name(ret)); + jaylink_strerror(ret)); jaylink_exit(jayctx); return ERROR_JTAG_INIT_FAILED; } - ret = jaylink_discovery_scan(jayctx, 0); + host_interfaces = JAYLINK_HIF_USB; + + if (use_serial_number) + host_interfaces |= JAYLINK_HIF_TCP; + + ret = jaylink_discovery_scan(jayctx, host_interfaces); if (ret != JAYLINK_OK) { LOG_ERROR("jaylink_discovery_scan() failed: %s.", - jaylink_strerror_name(ret)); + jaylink_strerror(ret)); jaylink_exit(jayctx); return ERROR_JTAG_INIT_FAILED; } - ret = jaylink_get_devices(jayctx, &devs, NULL); + ret = jaylink_get_devices(jayctx, &devs, &num_devices); if (ret != JAYLINK_OK) { - LOG_ERROR("jaylink_get_devices() failed: %s.", - jaylink_strerror_name(ret)); + LOG_ERROR("jaylink_get_devices() failed: %s.", jaylink_strerror(ret)); + jaylink_exit(jayctx); + return ERROR_JTAG_INIT_FAILED; + } + + if (!use_serial_number && !use_usb_address && num_devices > 1) { + LOG_ERROR("Multiple devices found, specify the desired device."); + jaylink_free_devices(devs, true); jaylink_exit(jayctx); return ERROR_JTAG_INIT_FAILED; } found_device = false; - if (!use_serial_number && !use_usb_address) - LOG_INFO("No device selected, using first device."); - for (i = 0; devs[i]; i++) { if (use_serial_number) { ret = jaylink_device_get_serial_number(devs[i], &tmp); @@ -596,7 +612,7 @@ static int jlink_init(void) continue; } else if (ret != JAYLINK_OK) { LOG_WARNING("jaylink_device_get_serial_number() failed: %s.", - jaylink_strerror_name(ret)); + jaylink_strerror(ret)); continue; } @@ -607,9 +623,11 @@ static int jlink_init(void) if (use_usb_address) { ret = jaylink_device_get_usb_address(devs[i], &address); - if (ret != JAYLINK_OK) { + if (ret == JAYLINK_ERR_NOT_SUPPORTED) { + continue; + } else if (ret != JAYLINK_OK) { LOG_WARNING("jaylink_device_get_usb_address() failed: %s.", - jaylink_strerror_name(ret)); + jaylink_strerror(ret)); continue; } @@ -624,7 +642,7 @@ static int jlink_init(void) break; } - LOG_ERROR("Failed to open device: %s.", jaylink_strerror_name(ret)); + LOG_ERROR("Failed to open device: %s.", jaylink_strerror(ret)); } jaylink_free_devices(devs, true); @@ -644,7 +662,7 @@ static int jlink_init(void) if (ret != JAYLINK_OK) { LOG_ERROR("jaylink_get_firmware_version() failed: %s.", - jaylink_strerror_name(ret)); + jaylink_strerror(ret)); jaylink_close(devh); jaylink_exit(jayctx); return ERROR_JTAG_INIT_FAILED; @@ -659,7 +677,7 @@ static int jlink_init(void) ret = jaylink_get_caps(devh, caps); if (ret != JAYLINK_OK) { - LOG_ERROR("jaylink_get_caps() failed: %s.", jaylink_strerror_name(ret)); + LOG_ERROR("jaylink_get_caps() failed: %s.", jaylink_strerror(ret)); jaylink_close(devh); jaylink_exit(jayctx); return ERROR_JTAG_INIT_FAILED; @@ -670,7 +688,7 @@ static int jlink_init(void) if (ret != JAYLINK_OK) { LOG_ERROR("jaylink_get_extended_caps() failed: %s.", - jaylink_strerror_name(ret)); + jaylink_strerror(ret)); jaylink_close(devh); jaylink_exit(jayctx); return ERROR_JTAG_INIT_FAILED; @@ -684,7 +702,7 @@ static int jlink_init(void) if (ret != JAYLINK_OK) { LOG_ERROR("Failed to retrieve hardware version: %s.", - jaylink_strerror_name(ret)); + jaylink_strerror(ret)); jaylink_close(devh); jaylink_exit(jayctx); return ERROR_JTAG_INIT_FAILED; @@ -725,7 +743,7 @@ static int jlink_init(void) if (ret != JAYLINK_OK) { LOG_ERROR("jaylink_get_hardware_status() failed: %s.", - jaylink_strerror_name(ret)); + jaylink_strerror(ret)); jaylink_close(devh); jaylink_exit(jayctx); return ERROR_JTAG_INIT_FAILED; @@ -786,8 +804,7 @@ static int jlink_quit(void) ret = jaylink_swo_stop(devh); if (ret != JAYLINK_OK) - LOG_ERROR("jaylink_swo_stop() failed: %s.", - jaylink_strerror_name(ret)); + LOG_ERROR("jaylink_swo_stop() failed: %s.", jaylink_strerror(ret)); } if (jaylink_has_cap(caps, JAYLINK_DEV_CAP_REGISTER)) { @@ -795,7 +812,7 @@ static int jlink_quit(void) if (ret != JAYLINK_OK) LOG_ERROR("jaylink_unregister() failed: %s.", - jaylink_strerror_name(ret)); + jaylink_strerror(ret)); } jaylink_close(devh); @@ -944,7 +961,7 @@ COMMAND_HANDLER(jlink_serial_command) return ERROR_FAIL; } else if (ret != JAYLINK_OK) { command_print(CMD_CTX, "jaylink_parse_serial_number() failed: %s.", - jaylink_strerror_name(ret)); + jaylink_strerror(ret)); return ERROR_FAIL; } @@ -963,7 +980,7 @@ COMMAND_HANDLER(jlink_handle_hwstatus_command) if (ret != JAYLINK_OK) { command_print(CMD_CTX, "jaylink_get_hardware_status() failed: %s.", - jaylink_strerror_name(ret)); + jaylink_strerror(ret)); return ERROR_FAIL; } @@ -995,7 +1012,7 @@ COMMAND_HANDLER(jlink_handle_free_memory_command) if (ret != JAYLINK_OK) { command_print(CMD_CTX, "jaylink_get_free_memory() failed: %s.", - jaylink_strerror_name(ret)); + jaylink_strerror(ret)); return ERROR_FAIL; } @@ -1077,7 +1094,7 @@ COMMAND_HANDLER(jlink_handle_target_power_command) if (ret != JAYLINK_OK) { command_print(CMD_CTX, "jaylink_set_target_power() failed: %s.", - jaylink_strerror_name(ret)); + jaylink_strerror(ret)); return ERROR_FAIL; } @@ -1183,7 +1200,7 @@ static int poll_trace(uint8_t *buf, size_t *size) ret = jaylink_swo_read(devh, buf, &length); if (ret != JAYLINK_OK) { - LOG_ERROR("jaylink_swo_read() failed: %s.", jaylink_strerror_name(ret)); + LOG_ERROR("jaylink_swo_read() failed: %s.", jaylink_strerror(ret)); return ERROR_FAIL; } @@ -1204,7 +1221,7 @@ static uint32_t calculate_trace_buffer_size(void) if (ret != JAYLINK_OK) { LOG_ERROR("jaylink_get_free_memory() failed: %s.", - jaylink_strerror_name(ret)); + jaylink_strerror(ret)); return ERROR_FAIL; } @@ -1268,7 +1285,7 @@ static int config_trace(bool enabled, enum tpio_pin_protocol pin_protocol, ret = jaylink_swo_stop(devh); if (ret != JAYLINK_OK) { - LOG_ERROR("jaylink_swo_stop() failed: %s.", jaylink_strerror_name(ret)); + LOG_ERROR("jaylink_swo_stop() failed: %s.", jaylink_strerror(ret)); return ERROR_FAIL; } @@ -1294,7 +1311,7 @@ static int config_trace(bool enabled, enum tpio_pin_protocol pin_protocol, if (ret != JAYLINK_OK) { LOG_ERROR("jaylink_swo_get_speeds() failed: %s.", - jaylink_strerror_name(ret)); + jaylink_strerror(ret)); return ERROR_FAIL; } @@ -1310,8 +1327,7 @@ static int config_trace(bool enabled, enum tpio_pin_protocol pin_protocol, buffer_size); if (ret != JAYLINK_OK) { - LOG_ERROR("jaylink_start_swo() failed: %s.", - jaylink_strerror_name(ret)); + LOG_ERROR("jaylink_start_swo() failed: %s.", jaylink_strerror(ret)); return ERROR_FAIL; } @@ -1570,7 +1586,7 @@ COMMAND_HANDLER(jlink_handle_config_write_command) if (ret != JAYLINK_OK) { LOG_ERROR("jaylink_write_raw_config() failed: %s.", - jaylink_strerror_name(ret)); + jaylink_strerror(ret)); return ERROR_FAIL; } @@ -1655,8 +1671,7 @@ COMMAND_HANDLER(jlink_handle_emucom_write_command) LOG_ERROR("Channel not supported by the device."); return ERROR_FAIL; } else if (ret != JAYLINK_OK) { - LOG_ERROR("Failed to write to channel: %s.", - jaylink_strerror_name(ret)); + LOG_ERROR("Failed to write to channel: %s.", jaylink_strerror(ret)); return ERROR_FAIL; } @@ -1704,8 +1719,7 @@ COMMAND_HANDLER(jlink_handle_emucom_read_command) free(buf); return ERROR_FAIL; } else if (ret != JAYLINK_OK) { - LOG_ERROR("Failed to read from channel: %s.", - jaylink_strerror_name(ret)); + LOG_ERROR("Failed to read from channel: %s.", jaylink_strerror(ret)); free(buf); return ERROR_FAIL; } @@ -1972,7 +1986,7 @@ static int jlink_flush(void) tap_length, jtag_command_version); if (ret != JAYLINK_OK) { - LOG_ERROR("jaylink_jtag_io() failed: %s.", jaylink_strerror_name(ret)); + LOG_ERROR("jaylink_jtag_io() failed: %s.", jaylink_strerror(ret)); jlink_tap_init(); return ERROR_JTAG_QUEUE_FAILED; } @@ -2078,7 +2092,7 @@ static int jlink_swd_run_queue(void) ret = jaylink_swd_io(devh, tms_buffer, tdi_buffer, tdo_buffer, tap_length); if (ret != JAYLINK_OK) { - LOG_ERROR("jaylink_swd_io() failed: %s.", jaylink_strerror_name(ret)); + LOG_ERROR("jaylink_swd_io() failed: %s.", jaylink_strerror(ret)); goto skip; } diff --git a/src/jtag/drivers/kitprog.c b/src/jtag/drivers/kitprog.c index c689848c8..584da8c94 100644 --- a/src/jtag/drivers/kitprog.c +++ b/src/jtag/drivers/kitprog.c @@ -657,6 +657,7 @@ static int kitprog_swd_switch_seq(enum swd_special_seq seq) LOG_DEBUG("JTAG to SWD not supported"); /* Fall through to fix target reset issue */ } + /* fallthrough */ case LINE_RESET: LOG_DEBUG("SWD line reset"); if (kitprog_swd_seq(SEQUENCE_LINE_RESET) != ERROR_OK) diff --git a/src/jtag/drivers/remote_bitbang.c b/src/jtag/drivers/remote_bitbang.c index 5e78ccb45..669e0250c 100644 --- a/src/jtag/drivers/remote_bitbang.c +++ b/src/jtag/drivers/remote_bitbang.c @@ -59,7 +59,7 @@ static int remote_bitbang_buf_full(void) /* Read any incoming data, placing it into the buffer. */ static void remote_bitbang_fill_buf(void) { - fcntl(remote_bitbang_fd, F_SETFL, O_NONBLOCK); + socket_nonblock(remote_bitbang_fd); while (!remote_bitbang_buf_full()) { unsigned contiguous_available_space; if (remote_bitbang_end >= remote_bitbang_start) { @@ -76,10 +76,8 @@ static void remote_bitbang_fill_buf(void) contiguous_available_space); if (count > 0) { remote_bitbang_end += count; - // TODO: check for overflow. - if (remote_bitbang_end == sizeof(remote_bitbang_buf)) { + if (remote_bitbang_end == sizeof(remote_bitbang_buf)) remote_bitbang_end = 0; - } } else if (count == 0) { return; } else if (count < 0) { @@ -148,7 +146,7 @@ static int remote_bitbang_rread(void) } /* Enable blocking access. */ - fcntl(remote_bitbang_fd, F_SETFL, 0); + socket_block(remote_bitbang_fd); char c; ssize_t count = read(remote_bitbang_fd, &c, 1); if (count == 1) { @@ -171,7 +169,7 @@ static int remote_bitbang_read_sample(void) { if (remote_bitbang_start != remote_bitbang_end) { int c = remote_bitbang_buf[remote_bitbang_start]; - remote_bitbang_start = + remote_bitbang_start = (remote_bitbang_start + 1) % sizeof(remote_bitbang_buf); return char_to_int(c); } diff --git a/src/jtag/drivers/stlink_usb.c b/src/jtag/drivers/stlink_usb.c index 0bdcd316b..64868ea9e 100644 --- a/src/jtag/drivers/stlink_usb.c +++ b/src/jtag/drivers/stlink_usb.c @@ -1650,13 +1650,11 @@ static int stlink_usb_open(struct hl_interface_param_s *param, void **fd) h->transport = param->transport; - const uint16_t vids[] = { param->vid, 0 }; - const uint16_t pids[] = { param->pid, 0 }; - const char *serial = param->serial; - - LOG_DEBUG("transport: %d vid: 0x%04x pid: 0x%04x serial: %s", - param->transport, param->vid, param->pid, - param->serial ? param->serial : ""); + for (unsigned i = 0; param->vid[i]; i++) { + LOG_DEBUG("transport: %d vid: 0x%04x pid: 0x%04x serial: %s", + param->transport, param->vid[i], param->pid[i], + param->serial ? param->serial : ""); + } /* On certain host USB configurations(e.g. MacBook Air) @@ -1668,7 +1666,7 @@ static int stlink_usb_open(struct hl_interface_param_s *param, void **fd) in order to become operational. */ do { - if (jtag_libusb_open(vids, pids, serial, &h->fd) != ERROR_OK) { + if (jtag_libusb_open(param->vid, param->pid, param->serial, &h->fd) != ERROR_OK) { LOG_ERROR("open failed"); goto error_open; } @@ -1683,8 +1681,14 @@ static int stlink_usb_open(struct hl_interface_param_s *param, void **fd) /* RX EP is common for all versions */ h->rx_ep = STLINK_RX_EP; + uint16_t pid; + if (jtag_libusb_get_pid(jtag_libusb_get_device(h->fd), &pid) != ERROR_OK) { + LOG_DEBUG("libusb_get_pid failed"); + goto error_open; + } + /* wrap version for first read */ - switch (param->pid) { + switch (pid) { case STLINK_V1_PID: h->version.stlink = 1; h->tx_ep = STLINK_TX_EP; @@ -1736,12 +1740,6 @@ static int stlink_usb_open(struct hl_interface_param_s *param, void **fd) } } while (1); - /* compare usb vid/pid */ - if ((param->vid != h->vid) || (param->pid != h->pid)) - LOG_INFO("vid/pid are not identical: 0x%04X/0x%04X 0x%04X/0x%04X", - param->vid, param->pid, - h->vid, h->pid); - /* check if mode is supported */ err = ERROR_OK; diff --git a/src/jtag/drivers/ti_icdi_usb.c b/src/jtag/drivers/ti_icdi_usb.c index 171ac66c4..f316c8256 100644 --- a/src/jtag/drivers/ti_icdi_usb.c +++ b/src/jtag/drivers/ti_icdi_usb.c @@ -688,14 +688,18 @@ static int icdi_usb_open(struct hl_interface_param_s *param, void **fd) } LOG_DEBUG("transport: %d vid: 0x%04x pid: 0x%04x", param->transport, - param->vid, param->pid); + param->vid[0], param->pid[0]); + + /* TODO: convert libusb_ calls to jtag_libusb_ */ + if (param->vid[1]) + LOG_WARNING("Bad configuration: 'hla_vid_pid' command does not accept more than one VID PID pair on ti-icdi!"); if (libusb_init(&h->usb_ctx) != 0) { LOG_ERROR("libusb init failed"); goto error_open; } - h->usb_dev = libusb_open_device_with_vid_pid(h->usb_ctx, param->vid, param->pid); + h->usb_dev = libusb_open_device_with_vid_pid(h->usb_ctx, param->vid[0], param->pid[0]); if (!h->usb_dev) { LOG_ERROR("open failed"); goto error_open; diff --git a/src/jtag/hla/hla_interface.c b/src/jtag/hla/hla_interface.c index 9217631b0..62a8f5947 100644 --- a/src/jtag/hla/hla_interface.c +++ b/src/jtag/hla/hla_interface.c @@ -35,7 +35,7 @@ #include -static struct hl_interface_s hl_if = { {0, 0, 0, 0, 0, HL_TRANSPORT_UNKNOWN, false, -1}, 0, 0 }; +static struct hl_interface_s hl_if = { {0, 0, { 0 }, { 0 }, 0, HL_TRANSPORT_UNKNOWN, false, -1}, 0, 0 }; int hl_interface_open(enum hl_transports tr) { @@ -264,15 +264,27 @@ COMMAND_HANDLER(hl_interface_handle_layout_command) COMMAND_HANDLER(hl_interface_handle_vid_pid_command) { - LOG_DEBUG("hl_interface_handle_vid_pid_command"); - - if (CMD_ARGC != 2) { - LOG_WARNING("ignoring extra IDs in hl_vid_pid (maximum is 1 pair)"); + if (CMD_ARGC > HLA_MAX_USB_IDS * 2) { + LOG_WARNING("ignoring extra IDs in hla_vid_pid " + "(maximum is %d pairs)", HLA_MAX_USB_IDS); + CMD_ARGC = HLA_MAX_USB_IDS * 2; + } + if (CMD_ARGC < 2 || (CMD_ARGC & 1)) { + LOG_WARNING("incomplete hla_vid_pid configuration directive"); return ERROR_COMMAND_SYNTAX_ERROR; } - COMMAND_PARSE_NUMBER(u16, CMD_ARGV[0], hl_if.param.vid); - COMMAND_PARSE_NUMBER(u16, CMD_ARGV[1], hl_if.param.pid); + unsigned i; + for (i = 0; i < CMD_ARGC; i += 2) { + COMMAND_PARSE_NUMBER(u16, CMD_ARGV[i], hl_if.param.vid[i / 2]); + COMMAND_PARSE_NUMBER(u16, CMD_ARGV[i + 1], hl_if.param.pid[i / 2]); + } + + /* + * Explicitly terminate, in case there are multiple instances of + * hla_vid_pid. + */ + hl_if.param.vid[i / 2] = hl_if.param.pid[i / 2] = 0; return ERROR_OK; } diff --git a/src/jtag/hla/hla_interface.h b/src/jtag/hla/hla_interface.h index 0992d1cad..262025e98 100644 --- a/src/jtag/hla/hla_interface.h +++ b/src/jtag/hla/hla_interface.h @@ -29,15 +29,17 @@ enum e_hl_transports; /** */ extern const char *hl_transports[]; +#define HLA_MAX_USB_IDS 8 + struct hl_interface_param_s { /** */ const char *device_desc; /** */ const char *serial; - /** */ - uint16_t vid; - /** */ - uint16_t pid; + /** List of recognised VIDs */ + uint16_t vid[HLA_MAX_USB_IDS + 1]; + /** List of recognised PIDs */ + uint16_t pid[HLA_MAX_USB_IDS + 1]; /** */ unsigned api; /** */ diff --git a/src/rtos/ChibiOS.c b/src/rtos/ChibiOS.c index 1bc1af8fc..ef0bb16cf 100644 --- a/src/rtos/ChibiOS.c +++ b/src/rtos/ChibiOS.c @@ -103,7 +103,7 @@ static struct ChibiOS_params ChibiOS_params_list[] = { }; #define CHIBIOS_NUM_PARAMS ((int)(sizeof(ChibiOS_params_list)/sizeof(struct ChibiOS_params))) -static int ChibiOS_detect_rtos(struct target *target); +static bool ChibiOS_detect_rtos(struct target *target); static int ChibiOS_create(struct target *target); static int ChibiOS_update_threads(struct rtos *rtos); static int ChibiOS_get_thread_reg_list(struct rtos *rtos, int64_t thread_id, char **hex_reg_list); @@ -510,7 +510,7 @@ static int ChibiOS_get_symbol_list_to_lookup(symbol_table_elem_t *symbol_list[]) return 0; } -static int ChibiOS_detect_rtos(struct target *target) +static bool ChibiOS_detect_rtos(struct target *target) { if ((target->rtos->symbols != NULL) && ((target->rtos->symbols[ChibiOS_VAL_rlist].address != 0) || @@ -519,14 +519,14 @@ static int ChibiOS_detect_rtos(struct target *target) if (target->rtos->symbols[ChibiOS_VAL_ch_debug].address == 0) { LOG_INFO("It looks like the target may be running ChibiOS " "without ch_debug."); - return 0; + return false; } /* looks like ChibiOS with memory map enabled.*/ - return 1; + return true; } - return 0; + return false; } static int ChibiOS_create(struct target *target) diff --git a/src/rtos/FreeRTOS.c b/src/rtos/FreeRTOS.c index 83961eb95..6027d6739 100644 --- a/src/rtos/FreeRTOS.c +++ b/src/rtos/FreeRTOS.c @@ -99,7 +99,7 @@ static const struct FreeRTOS_params FreeRTOS_params_list[] = { #define FREERTOS_NUM_PARAMS ((int)(sizeof(FreeRTOS_params_list)/sizeof(struct FreeRTOS_params))) -static int FreeRTOS_detect_rtos(struct target *target); +static bool FreeRTOS_detect_rtos(struct target *target); static int FreeRTOS_create(struct target *target); static int FreeRTOS_update_threads(struct rtos *rtos); static int FreeRTOS_get_thread_reg_list(struct rtos *rtos, int64_t thread_id, char **hex_reg_list); @@ -528,14 +528,14 @@ static int FreeRTOS_get_thread_ascii_info(struct rtos *rtos, threadid_t thread_i #endif -static int FreeRTOS_detect_rtos(struct target *target) +static bool FreeRTOS_detect_rtos(struct target *target) { if ((target->rtos->symbols != NULL) && (target->rtos->symbols[FreeRTOS_VAL_pxReadyTasksLists].address != 0)) { /* looks like FreeRTOS */ - return 1; + return true; } - return 0; + return false; } static int FreeRTOS_create(struct target *target) diff --git a/src/rtos/ThreadX.c b/src/rtos/ThreadX.c index ab8a66e93..b7dbe6d11 100644 --- a/src/rtos/ThreadX.c +++ b/src/rtos/ThreadX.c @@ -35,7 +35,7 @@ static const struct rtos_register_stacking *get_stacking_info_arm926ejs(const st static int is_thread_id_valid(const struct rtos *rtos, int64_t thread_id); static int is_thread_id_valid_arm926ejs(const struct rtos *rtos, int64_t thread_id); -static int ThreadX_detect_rtos(struct target *target); +static bool ThreadX_detect_rtos(struct target *target); static int ThreadX_create(struct target *target); static int ThreadX_update_threads(struct rtos *rtos); static int ThreadX_get_thread_reg_list(struct rtos *rtos, int64_t thread_id, char **hex_reg_list); @@ -492,14 +492,14 @@ static int ThreadX_get_symbol_list_to_lookup(symbol_table_elem_t *symbol_list[]) return 0; } -static int ThreadX_detect_rtos(struct target *target) +static bool ThreadX_detect_rtos(struct target *target) { if ((target->rtos->symbols != NULL) && (target->rtos->symbols[ThreadX_VAL_tx_thread_created_ptr].address != 0)) { /* looks like ThreadX */ - return 1; + return true; } - return 0; + return false; } #if 0 diff --git a/src/rtos/eCos.c b/src/rtos/eCos.c index edc3d8b51..9e41030ee 100644 --- a/src/rtos/eCos.c +++ b/src/rtos/eCos.c @@ -27,7 +27,7 @@ #include "helper/types.h" #include "rtos_ecos_stackings.h" -static int eCos_detect_rtos(struct target *target); +static bool eCos_detect_rtos(struct target *target); static int eCos_create(struct target *target); static int eCos_update_threads(struct rtos *rtos); static int eCos_get_thread_reg_list(struct rtos *rtos, int64_t thread_id, char **hex_reg_list); @@ -363,14 +363,14 @@ static int eCos_get_symbol_list_to_lookup(symbol_table_elem_t *symbol_list[]) return 0; } -static int eCos_detect_rtos(struct target *target) +static bool eCos_detect_rtos(struct target *target) { if ((target->rtos->symbols != NULL) && (target->rtos->symbols[eCos_VAL_thread_list].address != 0)) { /* looks like eCos */ - return 1; + return true; } - return 0; + return false; } static int eCos_create(struct target *target) diff --git a/src/rtos/embKernel.c b/src/rtos/embKernel.c index e515383ac..a40c86c3f 100644 --- a/src/rtos/embKernel.c +++ b/src/rtos/embKernel.c @@ -31,7 +31,7 @@ #define EMBKERNEL_MAX_THREAD_NAME_STR_SIZE (64) -static int embKernel_detect_rtos(struct target *target); +static bool embKernel_detect_rtos(struct target *target); static int embKernel_create(struct target *target); static int embKernel_update_threads(struct rtos *rtos); static int embKernel_get_thread_reg_list(struct rtos *rtos, int64_t thread_id, char **hex_reg_list); @@ -107,13 +107,13 @@ static const struct embKernel_params embKernel_params_list[] = { } }; -static int embKernel_detect_rtos(struct target *target) +static bool embKernel_detect_rtos(struct target *target) { if (target->rtos->symbols != NULL) { if (target->rtos->symbols[SYMBOL_ID_sCurrentTask].address != 0) - return 1; + return true; } - return 0; + return false; } static int embKernel_create(struct target *target) diff --git a/src/rtos/linux.c b/src/rtos/linux.c index 3efaab133..3b2a2b0c3 100644 --- a/src/rtos/linux.c +++ b/src/rtos/linux.c @@ -309,10 +309,10 @@ static int linux_os_thread_reg_list(struct rtos *rtos, return ERROR_OK; } -static int linux_os_detect(struct target *target) +static bool linux_os_detect(struct target *target) { LOG_INFO("should no be called"); - return 0; + return false; } static int linux_os_smp_init(struct target *target); diff --git a/src/rtos/mqx.c b/src/rtos/mqx.c index 63a48c54b..531b03b57 100644 --- a/src/rtos/mqx.c +++ b/src/rtos/mqx.c @@ -244,9 +244,9 @@ static int mqx_is_scheduler_running( } /* - * API function, return 1 if MQX is present + * API function, return true if MQX is present */ -static int mqx_detect_rtos( +static bool mqx_detect_rtos( struct target *target ) { @@ -254,9 +254,9 @@ static int mqx_detect_rtos( (target->rtos->symbols != NULL) && (target->rtos->symbols[mqx_VAL_mqx_kernel_data].address != 0) ) { - return 1; + return true; } - return 0; + return false; } /* diff --git a/src/rtos/riscv_debug.c b/src/rtos/riscv_debug.c index 3abdf74f0..b1a714d48 100644 --- a/src/rtos/riscv_debug.c +++ b/src/rtos/riscv_debug.c @@ -10,7 +10,7 @@ static int riscv_gdb_thread_packet(struct connection *connection, const char *packet, int packet_size); static int riscv_gdb_v_packet(struct connection *connection, const char *packet, int packet_size); -static int riscv_detect_rtos(struct target *target) +static bool riscv_detect_rtos(struct target *target) { LOG_ERROR("riscv_detect_rtos() unimplemented"); return -1; @@ -79,7 +79,7 @@ static int riscv_gdb_thread_packet(struct connection *connection, const char *pa if (strncmp(packet, "qfThreadInfo", 12) == 0) { riscv_update_threads(target->rtos); r->qs_thread_info_offset = 1; - + char m[16]; snprintf(m, 16, "m%08x", (int)rtos->thread_details[0].threadid); gdb_put_packet(connection, m, strlen(m)); @@ -264,7 +264,7 @@ static int riscv_gdb_v_packet(struct connection *connection, const char *packet, static int riscv_get_thread_reg_list(struct rtos *rtos, int64_t thread_id, char **hex_reg_list) { - LOG_DEBUG("Updating RISC-V regiser list for hart %d", (int)(thread_id - 1)); + LOG_DEBUG("Updating RISC-V register list for hart %d", (int)(thread_id - 1)); #if 0 LOG_ERROR(" Not actually updating"); @@ -279,18 +279,25 @@ static int riscv_get_thread_reg_list(struct rtos *rtos, int64_t thread_id, char ssize_t hex_reg_list_length = n_regs * reg_chars + 2; *hex_reg_list = malloc(hex_reg_list_length); *hex_reg_list[0] = '\0'; + char *p = hex_reg_list[0]; for (size_t i = 0; i < n_regs; ++i) { + assert(p - hex_reg_list[0] > 3); if (riscv_has_register(rtos->target, thread_id, i)) { - uint64_t reg_value = riscv_get_register_on_hart(rtos->target, thread_id - 1, i); + uint64_t reg_value; + int result = riscv_get_register_on_hart(rtos->target, ®_value, + thread_id - 1, i); + if (result != ERROR_OK) + return JIM_ERR; + for (size_t byte = 0; byte < xlen / 8; ++byte) { uint8_t reg_byte = reg_value >> (byte * 8); - char hex[3] = {'x', 'x', 'x'}; - snprintf(hex, 3, "%02x", reg_byte); - strncat(*hex_reg_list, hex, hex_reg_list_length); + p += snprintf(p, 3, "%02x", reg_byte); } } else { - for (size_t byte = 0; byte < xlen / 8; ++byte) - strncat(*hex_reg_list, "xx", hex_reg_list_length); + for (size_t byte = 0; byte < xlen / 8; ++byte) { + strcpy(p, "xx"); + p += 2; + } } } LOG_DEBUG("%s", *hex_reg_list); @@ -305,8 +312,7 @@ static int riscv_get_symbol_list_to_lookup(symbol_table_elem_t *symbol_list[]) return JIM_OK; } -const struct rtos_type riscv_rtos = -{ +const struct rtos_type riscv_rtos = { .name = "riscv", .detect_rtos = riscv_detect_rtos, .create = riscv_create_rtos, diff --git a/src/rtos/rtos.h b/src/rtos/rtos.h index 70cec870a..9da035adb 100644 --- a/src/rtos/rtos.h +++ b/src/rtos/rtos.h @@ -60,7 +60,7 @@ struct rtos { struct rtos_type { const char *name; - int (*detect_rtos)(struct target *target); + bool (*detect_rtos)(struct target *target); int (*create)(struct target *target); int (*smp_init)(struct target *target); int (*update_threads)(struct rtos *rtos); diff --git a/src/rtos/uCOS-III.c b/src/rtos/uCOS-III.c index 0a0fb3e9e..8e63ea4e6 100644 --- a/src/rtos/uCOS-III.c +++ b/src/rtos/uCOS-III.c @@ -241,7 +241,7 @@ static int uCOS_III_update_thread_offsets(struct rtos *rtos) return ERROR_OK; } -static int uCOS_III_detect_rtos(struct target *target) +static bool uCOS_III_detect_rtos(struct target *target) { return target->rtos->symbols != NULL && target->rtos->symbols[uCOS_III_VAL_OSRunning].address != 0; diff --git a/src/server/gdb_server.c b/src/server/gdb_server.c index ecf46add3..ed17f5046 100644 --- a/src/server/gdb_server.c +++ b/src/server/gdb_server.c @@ -71,8 +71,8 @@ struct gdb_connection { int ctrl_c; enum target_state frontend_state; struct image *vflash_image; - int closed; - int busy; + bool closed; + bool busy; int noack_mode; /* set flag to true if you want the next stepi to return immediately. * allowing GDB to pick up a fresh set of register values from the target @@ -215,7 +215,7 @@ static int gdb_get_char_inner(struct connection *connection, int *next_char) if (gdb_con->buf_cnt > 0) break; if (gdb_con->buf_cnt == 0) { - gdb_con->closed = 1; + gdb_con->closed = true; return ERROR_SERVER_REMOTE_CLOSED; } @@ -227,10 +227,10 @@ static int gdb_get_char_inner(struct connection *connection, int *next_char) usleep(1000); break; case WSAECONNABORTED: - gdb_con->closed = 1; + gdb_con->closed = true; return ERROR_SERVER_REMOTE_CLOSED; case WSAECONNRESET: - gdb_con->closed = 1; + gdb_con->closed = true; return ERROR_SERVER_REMOTE_CLOSED; default: LOG_ERROR("read: %d", errno); @@ -242,14 +242,14 @@ static int gdb_get_char_inner(struct connection *connection, int *next_char) usleep(1000); break; case ECONNABORTED: - gdb_con->closed = 1; + gdb_con->closed = true; return ERROR_SERVER_REMOTE_CLOSED; case ECONNRESET: - gdb_con->closed = 1; + gdb_con->closed = true; return ERROR_SERVER_REMOTE_CLOSED; default: LOG_ERROR("read: %s", strerror(errno)); - gdb_con->closed = 1; + gdb_con->closed = true; return ERROR_SERVER_REMOTE_CLOSED; } #endif @@ -341,7 +341,7 @@ static int gdb_write(struct connection *connection, void *data, int len) if (connection_write(connection, data, len) == len) return ERROR_OK; - gdb_con->closed = 1; + gdb_con->closed = true; return ERROR_SERVER_REMOTE_CLOSED; } @@ -448,7 +448,7 @@ static int gdb_put_packet_inner(struct connection *connection, return ERROR_OK; } else { LOG_ERROR("unknown character(1) 0x%2.2x in reply, dropping connection", reply); - gdb_con->closed = 1; + gdb_con->closed = true; return ERROR_SERVER_REMOTE_CLOSED; } } else if (reply == '$') { @@ -458,7 +458,7 @@ static int gdb_put_packet_inner(struct connection *connection, } else { LOG_ERROR("unknown character(2) 0x%2.2x in reply, dropping connection", reply); - gdb_con->closed = 1; + gdb_con->closed = true; return ERROR_SERVER_REMOTE_CLOSED; } } @@ -471,9 +471,9 @@ static int gdb_put_packet_inner(struct connection *connection, int gdb_put_packet(struct connection *connection, char *buffer, int len) { struct gdb_connection *gdb_con = connection->priv; - gdb_con->busy = 1; + gdb_con->busy = true; int retval = gdb_put_packet_inner(connection, buffer, len); - gdb_con->busy = 0; + gdb_con->busy = false; /* we sent some data, reset timer for keep alive messages */ kept_alive(); @@ -679,9 +679,9 @@ static int gdb_get_packet_inner(struct connection *connection, static int gdb_get_packet(struct connection *connection, char *buffer, int *len) { struct gdb_connection *gdb_con = connection->priv; - gdb_con->busy = 1; + gdb_con->busy = true; int retval = gdb_get_packet_inner(connection, buffer, len); - gdb_con->busy = 0; + gdb_con->busy = false; return retval; } @@ -917,10 +917,11 @@ static int gdb_target_callback_event_handler(struct target *target, static int gdb_new_connection(struct connection *connection) { struct gdb_connection *gdb_connection = malloc(sizeof(struct gdb_connection)); - struct gdb_service *gdb_service = connection->service->priv; + struct target *target; int retval; int initial_ack; + target = get_target_from_connection(connection); connection->priv = gdb_connection; /* initialize gdb connection information */ @@ -929,8 +930,8 @@ static int gdb_new_connection(struct connection *connection) gdb_connection->ctrl_c = 0; gdb_connection->frontend_state = TARGET_HALTED; gdb_connection->vflash_image = NULL; - gdb_connection->closed = 0; - gdb_connection->busy = 0; + gdb_connection->closed = false; + gdb_connection->busy = false; gdb_connection->noack_mode = 0; gdb_connection->sync = false; gdb_connection->mem_write_error = false; @@ -949,12 +950,12 @@ static int gdb_new_connection(struct connection *connection) * GDB session could leave dangling breakpoints if e.g. communication * timed out. */ - breakpoint_clear_target(gdb_service->target); - watchpoint_clear_target(gdb_service->target); + breakpoint_clear_target(target); + watchpoint_clear_target(target); /* clean previous rtos session if supported*/ - if ((gdb_service->target->rtos) && (gdb_service->target->rtos->type->clean)) - gdb_service->target->rtos->type->clean(gdb_service->target); + if ((target->rtos) && (target->rtos->type->clean)) + target->rtos->type->clean(target); /* remove the initial ACK from the incoming buffer */ retval = gdb_get_char(connection, &initial_ack); @@ -966,7 +967,7 @@ static int gdb_new_connection(struct connection *connection) */ if (initial_ack != '+') gdb_putback_char(connection, initial_ack); - target_call_event_callbacks(gdb_service->target, TARGET_EVENT_GDB_ATTACH); + target_call_event_callbacks(target, TARGET_EVENT_GDB_ATTACH); if (gdb_use_memory_map) { /* Connect must fail if the memory map can't be set up correctly. @@ -978,7 +979,7 @@ static int gdb_new_connection(struct connection *connection) for (i = 0; i < flash_get_bank_count(); i++) { struct flash_bank *p; p = get_flash_bank_by_num_noprobe(i); - if (p->target != gdb_service->target) + if (p->target != target) continue; retval = get_flash_bank_by_num(i, &p); if (retval != ERROR_OK) { @@ -992,8 +993,8 @@ static int gdb_new_connection(struct connection *connection) gdb_actual_connections++; LOG_DEBUG("New GDB Connection: %d, Target %s, state: %s", gdb_actual_connections, - target_name(gdb_service->target), - target_state_name(gdb_service->target)); + target_name(target), + target_state_name(target)); /* DANGER! If we fail subsequently, we must remove this handler, * otherwise we occasionally see crashes as the timer can invoke the @@ -1007,9 +1008,11 @@ static int gdb_new_connection(struct connection *connection) static int gdb_connection_closed(struct connection *connection) { - struct gdb_service *gdb_service = connection->service->priv; + struct target *target; struct gdb_connection *gdb_connection = connection->priv; + target = get_target_from_connection(connection); + /* we're done forwarding messages. Tear down callback before * cleaning up connection. */ @@ -1017,8 +1020,8 @@ static int gdb_connection_closed(struct connection *connection) gdb_actual_connections--; LOG_DEBUG("GDB Close, Target: %s, state: %s, gdb_actual_connections=%d", - target_name(gdb_service->target), - target_state_name(gdb_service->target), + target_name(target), + target_state_name(target), gdb_actual_connections); /* see if an image built with vFlash commands is left */ @@ -1029,7 +1032,7 @@ static int gdb_connection_closed(struct connection *connection) } /* if this connection registered a debug-message receiver delete it */ - delete_debug_msg_receiver(connection->cmd_ctx, gdb_service->target); + delete_debug_msg_receiver(connection->cmd_ctx, target); if (connection->priv) { free(connection->priv); @@ -1039,9 +1042,9 @@ static int gdb_connection_closed(struct connection *connection) target_unregister_event_callback(gdb_target_callback_event_handler, connection); - target_call_event_callbacks(gdb_service->target, TARGET_EVENT_GDB_END); + target_call_event_callbacks(target, TARGET_EVENT_GDB_END); - target_call_event_callbacks(gdb_service->target, TARGET_EVENT_GDB_DETACH); + target_call_event_callbacks(target, TARGET_EVENT_GDB_DETACH); return ERROR_OK; } @@ -1283,10 +1286,10 @@ static int gdb_get_register_packet(struct connection *connection, } if (!reg_list[reg_num]->valid) { - retval = reg_list[reg_num]->type->get(reg_list[reg_num]); + retval = reg_list[reg_num]->type->get(reg_list[reg_num]); if (retval != ERROR_OK) { LOG_DEBUG("Couldn't get register %s.", reg_list[reg_num]->name); - free (reg_list); + free(reg_list); return gdb_error(connection, retval); } } @@ -1344,7 +1347,7 @@ static int gdb_set_register_packet(struct connection *connection, gdb_target_to_reg(target, separator + 1, chars, bin_buf); retval = reg_list[reg_num]->type->set(reg_list[reg_num], bin_buf); - if (retval != ERROR_OK){ + if (retval != ERROR_OK) { LOG_DEBUG("Couldn't set register %s.", reg_list[reg_num]->name); free(bin_buf); free(reg_list); @@ -2441,7 +2444,7 @@ static int gdb_query_packet(struct connection *connection, /* skip command character */ packet += 5; - addr = strtoul(packet, &separator, 16); + addr = strtoull(packet, &separator, 16); if (*separator != ',') { LOG_ERROR("incomplete read memory packet received, dropping connection"); @@ -2583,7 +2586,6 @@ static int gdb_v_packet(struct connection *connection, char const *packet, int packet_size) { struct gdb_connection *gdb_connection = connection->priv; - struct gdb_service *gdb_service = connection->service->priv; int result; struct target *target = get_target_from_connection(connection); @@ -2629,18 +2631,18 @@ static int gdb_v_packet(struct connection *connection, flash_set_dirty(); /* perform any target specific operations before the erase */ - target_call_event_callbacks(gdb_service->target, + target_call_event_callbacks(target, TARGET_EVENT_GDB_FLASH_ERASE_START); /* vFlashErase:addr,length messages require region start and * end to be "block" aligned ... if padding is ever needed, * GDB will have become dangerously confused. */ - result = flash_erase_address_range(gdb_service->target, - false, addr, length); + result = flash_erase_address_range(target, false, addr, + length); /* perform any target specific operations after the erase */ - target_call_event_callbacks(gdb_service->target, + target_call_event_callbacks(target, TARGET_EVENT_GDB_FLASH_ERASE_END); /* perform erase */ @@ -2695,10 +2697,12 @@ static int gdb_v_packet(struct connection *connection, /* process the flashing buffer. No need to erase as GDB * always issues a vFlashErase first. */ - target_call_event_callbacks(gdb_service->target, + target_call_event_callbacks(target, TARGET_EVENT_GDB_FLASH_WRITE_START); - result = flash_write(gdb_service->target, gdb_connection->vflash_image, &written, 0); - target_call_event_callbacks(gdb_service->target, TARGET_EVENT_GDB_FLASH_WRITE_END); + result = flash_write(target, gdb_connection->vflash_image, + &written, 0); + target_call_event_callbacks(target, + TARGET_EVENT_GDB_FLASH_WRITE_END); if (result != ERROR_OK) { if (result == ERROR_FLASH_DST_OUT_OF_BANK) gdb_put_packet(connection, "E.memtype", 9); @@ -2722,9 +2726,8 @@ static int gdb_v_packet(struct connection *connection, static int gdb_detach(struct connection *connection) { - struct gdb_service *gdb_service = connection->service->priv; - - target_call_event_callbacks(gdb_service->target, TARGET_EVENT_GDB_DETACH); + target_call_event_callbacks(get_target_from_connection(connection), + TARGET_EVENT_GDB_DETACH); return gdb_put_packet(connection, "OK", 2); } @@ -2803,14 +2806,15 @@ static int gdb_input_inner(struct connection *connection) /* Do not allocate this on the stack */ static char gdb_packet_buffer[GDB_BUFFER_SIZE]; - struct gdb_service *gdb_service = connection->service->priv; - struct target *target = gdb_service->target; + struct target *target; char const *packet = gdb_packet_buffer; int packet_size; int retval; struct gdb_connection *gdb_con = connection->priv; static int extended_protocol; + target = get_target_from_connection(connection); + /* drain input buffer. If one of the packets fail, then an error * packet is replied, if applicable. * @@ -2980,8 +2984,8 @@ static int gdb_input_inner(struct connection *connection) break; case 'R': /* handle extended restart packet */ - breakpoint_clear_target(gdb_service->target); - watchpoint_clear_target(gdb_service->target); + breakpoint_clear_target(target); + watchpoint_clear_target(target); command_run_linef(connection->cmd_ctx, "ocd_gdb_restart %s", target_name(target)); /* set connection as attached after reset */ diff --git a/src/server/telnet_server.c b/src/server/telnet_server.c index e33188b5e..7507afea8 100644 --- a/src/server/telnet_server.c +++ b/src/server/telnet_server.c @@ -222,7 +222,6 @@ static int telnet_new_connection(struct connection *connection) telnet_connection->closed = 0; telnet_connection->line_size = 0; telnet_connection->line_cursor = 0; - telnet_connection->option_size = 0; telnet_connection->prompt = strdup("> "); telnet_connection->state = TELNET_STATE_DATA; diff --git a/src/server/telnet_server.h b/src/server/telnet_server.h index 04ba96570..f8fb82689 100644 --- a/src/server/telnet_server.h +++ b/src/server/telnet_server.h @@ -27,11 +27,10 @@ #include -#define TELNET_BUFFER_SIZE (1024) +#define TELNET_BUFFER_SIZE (10*1024) -#define TELNET_OPTION_MAX_SIZE (128) #define TELNET_LINE_HISTORY_SIZE (128) -#define TELNET_LINE_MAX_SIZE (256) +#define TELNET_LINE_MAX_SIZE (10*256) enum telnet_states { TELNET_STATE_DATA, @@ -51,8 +50,6 @@ struct telnet_connection { char line[TELNET_LINE_MAX_SIZE]; int line_size; int line_cursor; - char option[TELNET_OPTION_MAX_SIZE]; - int option_size; char last_escape; char *history[TELNET_LINE_HISTORY_SIZE]; int next_history; diff --git a/src/svf/svf.c b/src/svf/svf.c index e7e815c10..1d686ba61 100644 --- a/src/svf/svf.c +++ b/src/svf/svf.c @@ -661,11 +661,13 @@ static int svf_read_command_from_file(FILE *fd) if (svf_getline(&svf_read_line, &svf_read_line_size, svf_fd) <= 0) return ERROR_FAIL; i = -1; + /* fallthrough */ case '\r': slash = 0; /* Don't save '\r' and '\n' if no data is parsed */ if (!cmd_pos) break; + /* fallthrough */ default: /* The parsing code currently expects a space * before parentheses -- "TDI (123)". Also a diff --git a/src/target/Makefile.am b/src/target/Makefile.am index 1e81f4a13..9576b2357 100644 --- a/src/target/Makefile.am +++ b/src/target/Makefile.am @@ -19,6 +19,7 @@ noinst_LTLIBRARIES += %D%/libtarget.la $(AVR32_SRC) \ $(MIPS32_SRC) \ $(NDS32_SRC) \ + $(STM8_SRC) \ $(INTEL_IA32_SRC) \ $(RISCV_SRC) \ %D%/avrt.c \ @@ -125,6 +126,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 \ @@ -213,6 +217,7 @@ RISCV_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/adi_v5_swd.c b/src/target/adi_v5_swd.c index 41ddbd789..a6aada326 100644 --- a/src/target/adi_v5_swd.c +++ b/src/target/adi_v5_swd.c @@ -427,7 +427,10 @@ static int swd_init(struct command_context *ctx) /* First connect after init is not reconnecting. */ dap->do_reconnect = false; - return swd_connect(dap); + int retval = swd_connect(dap); + if (retval != ERROR_OK) + LOG_ERROR("SWD connect failed"); + return retval; } static struct transport swd_transport = { diff --git a/src/target/arm.h b/src/target/arm.h index d63ead215..f89aa6884 100644 --- a/src/target/arm.h +++ b/src/target/arm.h @@ -157,6 +157,9 @@ struct arm { int (*setup_semihosting)(struct target *target, int enable); + /** Semihosting command line. */ + char *semihosting_cmdline; + /** Backpointer to the target. */ struct target *target; diff --git a/src/target/arm_adi_v5.c b/src/target/arm_adi_v5.c index eafc2ddc0..200629023 100644 --- a/src/target/arm_adi_v5.c +++ b/src/target/arm_adi_v5.c @@ -346,8 +346,10 @@ static int mem_ap_write(struct adiv5_ap *ap, const uint8_t *buffer, uint32_t siz case 4: outvalue |= (uint32_t)*buffer++ << 8 * (address++ & 3); outvalue |= (uint32_t)*buffer++ << 8 * (address++ & 3); + /* fallthrough */ case 2: outvalue |= (uint32_t)*buffer++ << 8 * (address++ & 3); + /* fallthrough */ case 1: outvalue |= (uint32_t)*buffer++ << 8 * (address++ & 3); } @@ -509,8 +511,10 @@ static int mem_ap_read(struct adiv5_ap *ap, uint8_t *buffer, uint32_t size, uint case 4: *buffer++ = *read_ptr >> 8 * (3 - (address++ & 3)); *buffer++ = *read_ptr >> 8 * (3 - (address++ & 3)); + /* fallthrough */ case 2: *buffer++ = *read_ptr >> 8 * (3 - (address++ & 3)); + /* fallthrough */ case 1: *buffer++ = *read_ptr >> 8 * (3 - (address++ & 3)); } @@ -519,8 +523,10 @@ static int mem_ap_read(struct adiv5_ap *ap, uint8_t *buffer, uint32_t size, uint case 4: *buffer++ = *read_ptr >> 8 * (address++ & 3); *buffer++ = *read_ptr >> 8 * (address++ & 3); + /* fallthrough */ case 2: *buffer++ = *read_ptr >> 8 * (address++ & 3); + /* fallthrough */ case 1: *buffer++ = *read_ptr >> 8 * (address++ & 3); } @@ -1053,7 +1059,7 @@ static int dap_rom_display(struct command_context *cmd_ctx, int retval; uint64_t pid; uint32_t cid; - char tabs[7] = ""; + char tabs[16] = ""; if (depth > 16) { command_print(cmd_ctx, "\tTables too deep"); diff --git a/src/target/arm_disassembler.c b/src/target/arm_disassembler.c index 5277b94d8..3f1daca4d 100644 --- a/src/target/arm_disassembler.c +++ b/src/target/arm_disassembler.c @@ -3299,6 +3299,7 @@ static int t2ev_data_immed(uint32_t opcode, uint32_t address, case 0x10: case 0x12: is_signed = true; + /* fallthrough */ case 0x18: case 0x1a: /* signed/unsigned saturated add */ diff --git a/src/target/arm_semihosting.c b/src/target/arm_semihosting.c index 252511962..f31f901f0 100644 --- a/src/target/arm_semihosting.c +++ b/src/target/arm_semihosting.c @@ -465,7 +465,7 @@ static int do_semihosting(struct target *target) else { uint32_t a = target_buffer_get_u32(target, params+0); uint32_t l = target_buffer_get_u32(target, params+4); - char *arg = "foobar"; + char *arg = arm->semihosting_cmdline != NULL ? arm->semihosting_cmdline : ""; uint32_t s = strlen(arg) + 1; if (l < s) arm->semihosting_result = -1; diff --git a/src/target/armv4_5.c b/src/target/armv4_5.c index 2029ca92a..48050b078 100644 --- a/src/target/armv4_5.c +++ b/src/target/armv4_5.c @@ -1091,6 +1091,42 @@ COMMAND_HANDLER(handle_arm_semihosting_fileio_command) return ERROR_OK; } +COMMAND_HANDLER(handle_arm_semihosting_cmdline) +{ + struct target *target = get_current_target(CMD_CTX); + unsigned int i; + + if (target == NULL) { + LOG_ERROR("No target selected"); + return ERROR_FAIL; + } + + struct arm *arm = target_to_arm(target); + + if (!is_arm(arm)) { + command_print(CMD_CTX, "current target isn't an ARM"); + return ERROR_FAIL; + } + + if (!arm->setup_semihosting) { + command_print(CMD_CTX, "semihosting not supported for current target"); + return ERROR_FAIL; + } + + free(arm->semihosting_cmdline); + arm->semihosting_cmdline = CMD_ARGC > 0 ? strdup(CMD_ARGV[0]) : NULL; + + for (i = 1; i < CMD_ARGC; i++) { + char *cmdline = alloc_printf("%s %s", arm->semihosting_cmdline, CMD_ARGV[i]); + if (cmdline == NULL) + break; + free(arm->semihosting_cmdline); + arm->semihosting_cmdline = cmdline; + } + + return ERROR_OK; +} + static const struct command_registration arm_exec_command_handlers[] = { { .name = "reg", @@ -1133,6 +1169,13 @@ static const struct command_registration arm_exec_command_handlers[] = { .usage = "['enable'|'disable']", .help = "activate support for semihosting operations", }, + { + "semihosting_cmdline", + .handler = handle_arm_semihosting_cmdline, + .mode = COMMAND_EXEC, + .usage = "arguments", + .help = "command line arguments to be passed to program", + }, { "semihosting_fileio", .handler = handle_arm_semihosting_fileio_command, diff --git a/src/target/armv7a.c b/src/target/armv7a.c index 6021def4e..db72afd21 100644 --- a/src/target/armv7a.c +++ b/src/target/armv7a.c @@ -355,7 +355,7 @@ int armv7a_mmu_translate_va_pa(struct target *target, uint32_t va, break; case 7: LOG_INFO("inner: Write-Back, no Write-Allocate"); - + break; default: LOG_INFO("inner: %" PRIx32 " ???", INNER); } diff --git a/src/target/armv7m.h b/src/target/armv7m.h index 284bb9caa..6f5d6f995 100644 --- a/src/target/armv7m.h +++ b/src/target/armv7m.h @@ -174,7 +174,7 @@ target_to_armv7m(struct target *target) return container_of(target->arch_info, struct armv7m_common, arm); } -static inline bool is_armv7m(struct armv7m_common *armv7m) +static inline bool is_armv7m(const struct armv7m_common *armv7m) { return armv7m->common_magic == ARMV7M_COMMON_MAGIC; } diff --git a/src/target/cortex_m.c b/src/target/cortex_m.c index e80cd2356..2f8c2a2c8 100644 --- a/src/target/cortex_m.c +++ b/src/target/cortex_m.c @@ -168,12 +168,8 @@ static int cortex_m_single_step_core(struct target *target) { struct cortex_m_common *cortex_m = target_to_cm(target); struct armv7m_common *armv7m = &cortex_m->armv7m; - uint32_t dhcsr_save; int retval; - /* backup dhcsr reg */ - dhcsr_save = cortex_m->dcb_dhcsr; - /* Mask interrupts before clearing halt, if done already. This avoids * Erratum 377497 (fixed in r1p0) where setting MASKINTS while clearing * HALT can put the core into an unknown state. @@ -191,7 +187,6 @@ static int cortex_m_single_step_core(struct target *target) LOG_DEBUG(" "); /* restore dhcsr reg */ - cortex_m->dcb_dhcsr = dhcsr_save; cortex_m_clear_halt(target); return ERROR_OK; @@ -242,7 +237,7 @@ static int cortex_m_endreset_event(struct target *target) if (retval != ERROR_OK) return retval; if (!(cortex_m->dcb_dhcsr & C_DEBUGEN)) { - retval = mem_ap_write_u32(armv7m->debug_ap, DCB_DHCSR, DBGKEY | C_DEBUGEN); + retval = cortex_m_write_debug_halt_mask(target, 0, C_HALT | C_STEP | C_MASKINTS); if (retval != ERROR_OK) return retval; } @@ -1005,12 +1000,12 @@ static int cortex_m_assert_reset(struct target *target) /* Store important errors instead of failing and proceed to reset assert */ if (retval != ERROR_OK || !(cortex_m->dcb_dhcsr & C_DEBUGEN)) - retval = mem_ap_write_u32(armv7m->debug_ap, DCB_DHCSR, DBGKEY | C_DEBUGEN); + retval = cortex_m_write_debug_halt_mask(target, 0, C_HALT | C_STEP | C_MASKINTS); /* If the processor is sleeping in a WFI or WFE instruction, the * C_HALT bit must be asserted to regain control */ if (retval == ERROR_OK && (cortex_m->dcb_dhcsr & S_SLEEP)) - retval = mem_ap_write_u32(armv7m->debug_ap, DCB_DHCSR, DBGKEY | C_HALT | C_DEBUGEN); + retval = cortex_m_write_debug_halt_mask(target, C_HALT, 0); mem_ap_write_u32(armv7m->debug_ap, DCB_DCRDR, 0); /* Ignore less important errors */ @@ -1018,8 +1013,7 @@ static int cortex_m_assert_reset(struct target *target) if (!target->reset_halt) { /* Set/Clear C_MASKINTS in a separate operation */ if (cortex_m->dcb_dhcsr & C_MASKINTS) - mem_ap_write_atomic_u32(armv7m->debug_ap, DCB_DHCSR, - DBGKEY | C_DEBUGEN | C_HALT); + cortex_m_write_debug_halt_mask(target, 0, C_MASKINTS); /* clear any debug flags before resuming */ cortex_m_clear_halt(target); diff --git a/src/target/nds32_cmd.c b/src/target/nds32_cmd.c index edb4872e4..500651dbc 100644 --- a/src/target/nds32_cmd.c +++ b/src/target/nds32_cmd.c @@ -816,7 +816,7 @@ static int jim_nds32_bulk_read(Jim_Interp *interp, int argc, Jim_Obj * const *ar uint32_t *data = malloc(count * sizeof(uint32_t)); int result; result = target_read_buffer(target, address, count * 4, (uint8_t *)data); - char data_str[11]; + char data_str[12]; jim_wide i; Jim_SetResult(interp, Jim_NewEmptyStringObj(interp)); diff --git a/src/target/openrisc/jsp_server.c b/src/target/openrisc/jsp_server.c index e581fb870..2d90114fa 100644 --- a/src/target/openrisc/jsp_server.c +++ b/src/target/openrisc/jsp_server.c @@ -88,7 +88,6 @@ static int jsp_new_connection(struct connection *connection) telnet_connection->closed = 0; telnet_connection->line_size = 0; telnet_connection->line_cursor = 0; - telnet_connection->option_size = 0; telnet_connection->state = TELNET_STATE_DATA; /* negotiate telnet options */ diff --git a/src/target/register.h b/src/target/register.h index d4c328160..dc18e9a89 100644 --- a/src/target/register.h +++ b/src/target/register.h @@ -114,9 +114,9 @@ struct reg_data_type { }; struct reg { - /** Canonical name of the register. */ + /* Canonical name of the register. */ const char *name; - /** Number that gdb uses to access this register. */ + /* Number that gdb uses to access this register. */ uint32_t number; /* TODO. This should probably be const. */ struct reg_feature *feature; diff --git a/src/target/riscv/asm.h b/src/target/riscv/asm.h index f976e2065..d81aa0285 100644 --- a/src/target/riscv/asm.h +++ b/src/target/riscv/asm.h @@ -17,7 +17,7 @@ static uint32_t load(const struct target *target, unsigned int rd, return ld(rd, base, offset); } assert(0); - return 0; // Silence -Werror=return-type + return 0; /* Silence -Werror=return-type */ } static uint32_t store(const struct target *target, unsigned int src, @@ -32,7 +32,7 @@ static uint32_t store(const struct target *target, unsigned int src, return sd(src, base, offset); } assert(0); - return 0; // Silence -Werror=return-type + return 0; /* Silence -Werror=return-type */ } #endif diff --git a/src/target/riscv/batch.c b/src/target/riscv/batch.c index 1e6a4be92..117119dd6 100644 --- a/src/target/riscv/batch.c +++ b/src/target/riscv/batch.c @@ -42,20 +42,19 @@ bool riscv_batch_full(struct riscv_batch *batch) return batch->used_scans > (batch->allocated_scans - 4); } -void riscv_batch_run(struct riscv_batch *batch) +int riscv_batch_run(struct riscv_batch *batch) { if (batch->used_scans == 0) { LOG_DEBUG("Ignoring empty batch."); - return; + return ERROR_OK; } - keep_alive(); + keep_alive(); LOG_DEBUG("running a batch of %ld scans", (long)batch->used_scans); riscv_batch_add_nop(batch); for (size_t i = 0; i < batch->used_scans; ++i) { - dump_field(batch->fields + i); jtag_add_dr_scan(batch->target->tap, 1, batch->fields + i, TAP_IDLE); if (batch->idle_count > 0) jtag_add_runtest(batch->idle_count, TAP_IDLE); @@ -64,11 +63,13 @@ void riscv_batch_run(struct riscv_batch *batch) LOG_DEBUG("executing queue"); if (jtag_execute_queue() != ERROR_OK) { LOG_ERROR("Unable to execute JTAG queue"); - abort(); + return ERROR_FAIL; } for (size_t i = 0; i < batch->used_scans; ++i) dump_field(batch->fields + i); + + return ERROR_OK; } void riscv_batch_add_dmi_write(struct riscv_batch *batch, unsigned address, uint64_t data) @@ -139,8 +140,8 @@ void riscv_batch_add_nop(struct riscv_batch *batch) void dump_field(const struct scan_field *field) { - static const char *op_string[] = {"-", "r", "w", "?"}; - static const char *status_string[] = {"+", "?", "F", "b"}; + static const char * const op_string[] = {"-", "r", "w", "?"}; + static const char * const status_string[] = {"+", "?", "F", "b"}; if (debug_level < LOG_LVL_DEBUG) return; diff --git a/src/target/riscv/batch.h b/src/target/riscv/batch.h index 835829b2b..70690a601 100644 --- a/src/target/riscv/batch.h +++ b/src/target/riscv/batch.h @@ -48,7 +48,7 @@ void riscv_batch_free(struct riscv_batch *batch); bool riscv_batch_full(struct riscv_batch *batch); /* Executes this scan batch. */ -void riscv_batch_run(struct riscv_batch *batch); +int riscv_batch_run(struct riscv_batch *batch); /* Adds a DMI write to this batch. */ void riscv_batch_add_dmi_write(struct riscv_batch *batch, unsigned address, uint64_t data); diff --git a/src/target/riscv/debug_defines.h b/src/target/riscv/debug_defines.h index 6e88a55ec..04500e572 100644 --- a/src/target/riscv/debug_defines.h +++ b/src/target/riscv/debug_defines.h @@ -173,12 +173,6 @@ #define CSR_DCSR_EBREAKM_LENGTH 1 #define CSR_DCSR_EBREAKM (0x1U << CSR_DCSR_EBREAKM_OFFSET) /* -* When 1, {\tt ebreak} instructions in Hypervisor Mode enter Debug Mode. - */ -#define CSR_DCSR_EBREAKH_OFFSET 14 -#define CSR_DCSR_EBREAKH_LENGTH 1 -#define CSR_DCSR_EBREAKH (0x1U << CSR_DCSR_EBREAKH_OFFSET) -/* * When 1, {\tt ebreak} instructions in Supervisor Mode enter Debug Mode. */ #define CSR_DCSR_EBREAKS_OFFSET 13 @@ -207,9 +201,10 @@ /* * 0: Increment counters as usual. * -* 1: Don't increment any counters while in Debug Mode. This includes -* the {\tt cycle} and {\tt instret} CSRs. This is preferred for most -* debugging scenarios. +* 1: Don't increment any counters while in Debug Mode or on {\tt +* ebreak} instructions that cause entry into Debug Mode. These +* counters include the {\tt cycle} and {\tt instret} CSRs. This is +* preferred for most debugging scenarios. * * An implementation may choose not to support writing to this bit. * The debugger must read back the value it writes to check whether @@ -300,7 +295,7 @@ * * Other values are reserved for future use. */ -#define CSR_TDATA1_TYPE_OFFSET XLEN-4 +#define CSR_TDATA1_TYPE_OFFSET (XLEN-4) #define CSR_TDATA1_TYPE_LENGTH 4 #define CSR_TDATA1_TYPE (0xfULL << CSR_TDATA1_TYPE_OFFSET) /* @@ -312,14 +307,14 @@ * * This bit is only writable from Debug Mode. */ -#define CSR_TDATA1_HMODE_OFFSET XLEN-5 -#define CSR_TDATA1_HMODE_LENGTH 1 -#define CSR_TDATA1_HMODE (0x1ULL << CSR_TDATA1_HMODE_OFFSET) +#define CSR_TDATA1_DMODE_OFFSET (XLEN-5) +#define CSR_TDATA1_DMODE_LENGTH 1 +#define CSR_TDATA1_DMODE (0x1ULL << CSR_TDATA1_DMODE_OFFSET) /* * Trigger-specific data. */ #define CSR_TDATA1_DATA_OFFSET 0 -#define CSR_TDATA1_DATA_LENGTH XLEN - 5 +#define CSR_TDATA1_DATA_LENGTH (XLEN - 5) #define CSR_TDATA1_DATA (((1L<> lo) & ((1 << (hi+1-lo)) - 1); } -static uint32_t bit(uint32_t value, unsigned int b) { +static uint32_t bit(uint32_t value, unsigned int b) +{ return (value >> b) & 1; } static uint32_t jal(unsigned int rd, uint32_t imm) __attribute__ ((unused)); -static uint32_t jal(unsigned int rd, uint32_t imm) { +static uint32_t jal(unsigned int rd, uint32_t imm) +{ return (bit(imm, 20) << 31) | (bits(imm, 10, 1) << 21) | (bit(imm, 11) << 20) | @@ -24,7 +27,8 @@ static uint32_t jal(unsigned int rd, uint32_t imm) { } static uint32_t csrsi(unsigned int csr, uint16_t imm) __attribute__ ((unused)); -static uint32_t csrsi(unsigned int csr, uint16_t imm) { +static uint32_t csrsi(unsigned int csr, uint16_t imm) +{ return (csr << 20) | (bits(imm, 4, 0) << 15) | MATCH_CSRRSI; @@ -107,7 +111,8 @@ static uint32_t lb(unsigned int rd, unsigned int base, uint16_t offset) } static uint32_t csrw(unsigned int source, unsigned int csr) __attribute__ ((unused)); -static uint32_t csrw(unsigned int source, unsigned int csr) { +static uint32_t csrw(unsigned int source, unsigned int csr) +{ return (csr << 20) | (source << 15) | MATCH_CSRRW; } @@ -121,17 +126,20 @@ static uint32_t addi(unsigned int dest, unsigned int src, uint16_t imm) } static uint32_t csrr(unsigned int rd, unsigned int csr) __attribute__ ((unused)); -static uint32_t csrr(unsigned int rd, unsigned int csr) { +static uint32_t csrr(unsigned int rd, unsigned int csr) +{ return (csr << 20) | (rd << 7) | MATCH_CSRRS; } static uint32_t csrrs(unsigned int rd, unsigned int rs, unsigned int csr) __attribute__ ((unused)); -static uint32_t csrrs(unsigned int rd, unsigned int rs, unsigned int csr) { +static uint32_t csrrs(unsigned int rd, unsigned int rs, unsigned int csr) +{ return (csr << 20) | (rs << 15) | (rd << 7) | MATCH_CSRRS; } static uint32_t csrrw(unsigned int rd, unsigned int rs, unsigned int csr) __attribute__ ((unused)); -static uint32_t csrrw(unsigned int rd, unsigned int rs, unsigned int csr) { +static uint32_t csrrw(unsigned int rd, unsigned int rs, unsigned int csr) +{ return (csr << 20) | (rs << 15) | (rd << 7) | MATCH_CSRRW; } @@ -173,12 +181,12 @@ static uint32_t fld(unsigned int dest, unsigned int base, uint16_t offset) MATCH_FLD; } -static uint32_t fmv_x_s(unsigned dest, unsigned src) __attribute__ ((unused)); -static uint32_t fmv_x_s(unsigned dest, unsigned src) +static uint32_t fmv_x_w(unsigned dest, unsigned src) __attribute__ ((unused)); +static uint32_t fmv_x_w(unsigned dest, unsigned src) { return src << 15 | dest << 7 | - MATCH_FMV_X_S; + MATCH_FMV_X_W; } static uint32_t fmv_x_d(unsigned dest, unsigned src) __attribute__ ((unused)); @@ -189,12 +197,12 @@ static uint32_t fmv_x_d(unsigned dest, unsigned src) MATCH_FMV_X_D; } -static uint32_t fmv_s_x(unsigned dest, unsigned src) __attribute__ ((unused)); -static uint32_t fmv_s_x(unsigned dest, unsigned src) +static uint32_t fmv_w_x(unsigned dest, unsigned src) __attribute__ ((unused)); +static uint32_t fmv_w_x(unsigned dest, unsigned src) { return src << 15 | dest << 7 | - MATCH_FMV_S_X; + MATCH_FMV_W_X; } static uint32_t fmv_d_x(unsigned dest, unsigned src) __attribute__ ((unused)); @@ -206,9 +214,15 @@ static uint32_t fmv_d_x(unsigned dest, unsigned src) } static uint32_t ebreak(void) __attribute__ ((unused)); -static uint32_t ebreak(void) { return MATCH_EBREAK; } +static uint32_t ebreak(void) +{ + return MATCH_EBREAK; +} static uint32_t ebreak_c(void) __attribute__ ((unused)); -static uint32_t ebreak_c(void) { return MATCH_C_EBREAK; } +static uint32_t ebreak_c(void) +{ + return MATCH_C_EBREAK; +} static uint32_t wfi(void) __attribute__ ((unused)); static uint32_t wfi(void) { return MATCH_WFI; } @@ -229,7 +243,8 @@ static uint32_t lui(unsigned int dest, uint32_t imm) /* static uint32_t csrci(unsigned int csr, uint16_t imm) __attribute__ ((unused)); -static uint32_t csrci(unsigned int csr, uint16_t imm) { +static uint32_t csrci(unsigned int csr, uint16_t imm) +{ return (csr << 20) | (bits(imm, 4, 0) << 15) | MATCH_CSRRCI; diff --git a/src/target/riscv/program.c b/src/target/riscv/program.c index d052574d4..25a06760c 100644 --- a/src/target/riscv/program.c +++ b/src/target/riscv/program.c @@ -11,78 +11,57 @@ #include "asm.h" #include "encoding.h" -riscv_addr_t riscv_program_gal(struct riscv_program *p, riscv_addr_t addr); -int riscv_program_lah(struct riscv_program *p, enum gdb_regno d, riscv_addr_t addr); -int riscv_program_lal(struct riscv_program *p, enum gdb_regno d, riscv_addr_t addr); - /* Program interface. */ int riscv_program_init(struct riscv_program *p, struct target *target) { memset(p, 0, sizeof(*p)); p->target = target; p->instruction_count = 0; - p->data_count = 0; - p->writes_memory = 0; p->target_xlen = riscv_xlen(target); - for (size_t i = 0; i < RISCV_REGISTER_COUNT; ++i) { + for (size_t i = 0; i < RISCV_REGISTER_COUNT; ++i) p->writes_xreg[i] = 0; - p->in_use[i] = 0; - } - for(size_t i = 0; i < RISCV_MAX_DEBUG_BUFFER_SIZE; ++i) + for (size_t i = 0; i < RISCV_MAX_DEBUG_BUFFER_SIZE; ++i) p->debug_buffer[i] = -1; - if (riscv_debug_buffer_enter(target, p) != ERROR_OK) { - LOG_ERROR("unable to write progam buffer enter code"); - return ERROR_FAIL; - } - return ERROR_OK; } +int riscv_program_write(struct riscv_program *program) +{ + for (unsigned i = 0; i < program->instruction_count; ++i) { + LOG_DEBUG("%p: debug_buffer[%02x] = DASM(0x%08x)", program, i, program->debug_buffer[i]); + if (riscv_write_debug_buffer(program->target, i, + program->debug_buffer[i]) != ERROR_OK) + return ERROR_FAIL; + } + return ERROR_OK; +} + +/** Add ebreak and execute the program. */ int riscv_program_exec(struct riscv_program *p, struct target *t) { keep_alive(); - if (riscv_debug_buffer_leave(t, p) != ERROR_OK) { - LOG_ERROR("unable to write program buffer exit code"); - return ERROR_FAIL; - } - riscv_reg_t saved_registers[GDB_REGNO_XPR31 + 1]; - for (size_t i = GDB_REGNO_XPR0 + 1; i <= GDB_REGNO_XPR31; ++i) { + for (size_t i = GDB_REGNO_ZERO + 1; i <= GDB_REGNO_XPR31; ++i) { if (p->writes_xreg[i]) { LOG_DEBUG("Saving register %d as used by program", (int)i); - saved_registers[i] = riscv_get_register(t, i); + int result = riscv_get_register(t, &saved_registers[i], i); + if (result != ERROR_OK) + return result; } } - if (p->writes_memory && (riscv_program_fence(p) != ERROR_OK)) { - LOG_ERROR("Unable to write fence"); - for(size_t i = 0; i < riscv_debug_buffer_size(p->target); ++i) - LOG_ERROR("ram[%02x]: DASM(0x%08lx) [0x%08lx]", (int)i, (long)p->debug_buffer[i], (long)p->debug_buffer[i]); - abort(); - return ERROR_FAIL; - } - if (riscv_program_ebreak(p) != ERROR_OK) { LOG_ERROR("Unable to write ebreak"); - for(size_t i = 0; i < riscv_debug_buffer_size(p->target); ++i) + for (size_t i = 0; i < riscv_debug_buffer_size(p->target); ++i) LOG_ERROR("ram[%02x]: DASM(0x%08lx) [0x%08lx]", (int)i, (long)p->debug_buffer[i], (long)p->debug_buffer[i]); - abort(); return ERROR_FAIL; } - for (unsigned i = 0; i < riscv_debug_buffer_size(p->target); ++i) { - if (i < p->instruction_count) { - LOG_DEBUG("%p: debug_buffer[%02x] = DASM(0x%08x)", p, i, p->debug_buffer[i]); - riscv_write_debug_buffer(t, i, p->debug_buffer[i]); - } - if (i >= riscv_debug_buffer_size(p->target) - p->data_count) { - LOG_DEBUG("%p: debug_buffer[%02x] = 0x%08x", p, i, p->debug_buffer[i]); - riscv_write_debug_buffer(t, i, p->debug_buffer[i]); - } - } + if (riscv_program_write(p) != ERROR_OK) + return ERROR_FAIL; if (riscv_execute_debug_buffer(t) != ERROR_OK) { LOG_ERROR("Unable to execute program %p", p); @@ -90,259 +69,56 @@ int riscv_program_exec(struct riscv_program *p, struct target *t) } for (size_t i = 0; i < riscv_debug_buffer_size(p->target); ++i) - if (i >= riscv_debug_buffer_size(p->target) - p->data_count) + if (i >= riscv_debug_buffer_size(p->target)) p->debug_buffer[i] = riscv_read_debug_buffer(t, i); - for (size_t i = GDB_REGNO_XPR0; i <= GDB_REGNO_XPR31; ++i) + for (size_t i = GDB_REGNO_ZERO; i <= GDB_REGNO_XPR31; ++i) if (p->writes_xreg[i]) riscv_set_register(t, i, saved_registers[i]); return ERROR_OK; } -riscv_addr_t riscv_program_alloc_data(struct riscv_program *p, size_t bytes) -{ - riscv_addr_t addr = - riscv_debug_buffer_addr(p->target) - + riscv_debug_buffer_size(p->target) * sizeof(p->debug_buffer[0]) - - p->data_count * sizeof(p->debug_buffer[0]) - - bytes; - while (addr % bytes != 0) addr--; - - riscv_addr_t ptop = - riscv_debug_buffer_addr(p->target) - + p->instruction_count * sizeof(p->debug_buffer[0]); - - if (addr <= ptop) { - LOG_ERROR("unable to allocate %d bytes", (int)bytes); - return RISCV_PROGRAM_ALLOC_FAIL; - } - - p->data_count = - + riscv_debug_buffer_size(p->target) - - (addr - riscv_debug_buffer_addr(p->target)) / sizeof(p->debug_buffer[0]); - return addr; -} - -riscv_addr_t riscv_program_alloc_x(struct riscv_program *p) -{ - return riscv_program_alloc_data(p, p->target_xlen / 8); -} - -riscv_addr_t riscv_program_alloc_d(struct riscv_program *p) -{ - return riscv_program_alloc_data(p, 8); -} - -riscv_addr_t riscv_program_alloc_w(struct riscv_program *p) -{ - return riscv_program_alloc_data(p, 4); -} - -riscv_addr_t riscv_program_alloc_h(struct riscv_program *p) -{ - return riscv_program_alloc_data(p, 2); -} - -riscv_addr_t riscv_program_alloc_b(struct riscv_program *p) -{ - return riscv_program_alloc_data(p, 1); -} - -riscv_insn_t riscv_program_read_ram(struct riscv_program *p, riscv_addr_t addr) -{ - if (addr < riscv_debug_buffer_addr(p->target)) - return -1; - if ((size_t)addr > riscv_debug_buffer_addr(p->target) + (riscv_debug_buffer_size(p->target) * sizeof(p->debug_buffer[0]))) - return -1; - - int off = (addr - riscv_debug_buffer_addr(p->target)) / sizeof(p->debug_buffer[0]); - return p->debug_buffer[off]; -} - -void riscv_program_write_ram(struct riscv_program *p, riscv_addr_t addr, uint64_t d) -{ - if (addr < riscv_debug_buffer_addr(p->target)) - return; - if ((size_t)addr > riscv_debug_buffer_addr(p->target) + (riscv_debug_buffer_size(p->target) * sizeof(p->debug_buffer[0]))) - return; - - int off = (addr - riscv_debug_buffer_addr(p->target)) / sizeof(p->debug_buffer[0]); - p->debug_buffer[off] = d; -} - int riscv_program_swr(struct riscv_program *p, enum gdb_regno d, enum gdb_regno b, int offset) { - p->writes_memory = 1; return riscv_program_insert(p, sw(d, b, offset)); } int riscv_program_shr(struct riscv_program *p, enum gdb_regno d, enum gdb_regno b, int offset) { - p->writes_memory = 1; return riscv_program_insert(p, sh(d, b, offset)); } int riscv_program_sbr(struct riscv_program *p, enum gdb_regno d, enum gdb_regno b, int offset) { - p->writes_memory = 1; return riscv_program_insert(p, sb(d, b, offset)); } int riscv_program_lwr(struct riscv_program *p, enum gdb_regno d, enum gdb_regno b, int offset) { - p->writes_memory = 1; return riscv_program_insert(p, lw(d, b, offset)); } int riscv_program_lhr(struct riscv_program *p, enum gdb_regno d, enum gdb_regno b, int offset) { - p->writes_memory = 1; return riscv_program_insert(p, lh(d, b, offset)); } int riscv_program_lbr(struct riscv_program *p, enum gdb_regno d, enum gdb_regno b, int offset) { - p->writes_memory = 1; return riscv_program_insert(p, lb(d, b, offset)); } -int riscv_program_lx(struct riscv_program *p, enum gdb_regno d, riscv_addr_t addr) -{ - switch (p->target_xlen) { - case 64: return riscv_program_ld(p, d, addr); - case 32: return riscv_program_lw(p, d, addr); - } - - LOG_ERROR("unknown xlen %d", p->target_xlen); - abort(); - return -1; -} - -int riscv_program_ld(struct riscv_program *p, enum gdb_regno d, riscv_addr_t addr) -{ - enum gdb_regno t = riscv_program_gah(p, addr) == 0 ? GDB_REGNO_X0 : d; - if (riscv_program_lah(p, d, addr) != ERROR_OK) - return ERROR_FAIL; - if (riscv_program_insert(p, ld(d, t, riscv_program_gal(p, addr))) != ERROR_OK) - return ERROR_FAIL; - return ERROR_OK; -} - -int riscv_program_lw(struct riscv_program *p, enum gdb_regno d, riscv_addr_t addr) -{ - enum gdb_regno t = riscv_program_gah(p, addr) == 0 ? GDB_REGNO_X0 : d; - if (riscv_program_lah(p, d, addr) != ERROR_OK) - return ERROR_FAIL; - if (riscv_program_insert(p, lw(d, t, riscv_program_gal(p, addr))) != ERROR_OK) - return ERROR_FAIL; - return ERROR_OK; -} - -int riscv_program_lh(struct riscv_program *p, enum gdb_regno d, riscv_addr_t addr) -{ - enum gdb_regno t = riscv_program_gah(p, addr) == 0 ? GDB_REGNO_X0 : d; - if (riscv_program_lah(p, d, addr) != ERROR_OK) - return ERROR_FAIL; - if (riscv_program_insert(p, lh(d, t, riscv_program_gal(p, addr))) != ERROR_OK) - return ERROR_FAIL; - return ERROR_OK; -} - -int riscv_program_lb(struct riscv_program *p, enum gdb_regno d, riscv_addr_t addr) -{ - enum gdb_regno t = riscv_program_gah(p, addr) == 0 ? GDB_REGNO_X0 : d; - if (riscv_program_lah(p, t, addr) != ERROR_OK) - return ERROR_FAIL; - if (riscv_program_insert(p, lb(d, t, riscv_program_gal(p, addr))) != ERROR_OK) - return ERROR_FAIL; - return ERROR_OK; -} - -int riscv_program_sx(struct riscv_program *p, enum gdb_regno d, riscv_addr_t addr) -{ - switch (p->target_xlen) { - case 64: return riscv_program_sd(p, d, addr); - case 32: return riscv_program_sw(p, d, addr); - } - - LOG_ERROR("unknown xlen %d", p->target_xlen); - abort(); - return -1; -} - -int riscv_program_sd(struct riscv_program *p, enum gdb_regno d, riscv_addr_t addr) -{ - enum gdb_regno t = riscv_program_gah(p, addr) == 0 - ? GDB_REGNO_X0 - : riscv_program_gettemp(p); - if (riscv_program_lah(p, t, addr) != ERROR_OK) - return ERROR_FAIL; - if (riscv_program_insert(p, sd(d, t, riscv_program_gal(p, addr))) != ERROR_OK) - return ERROR_FAIL; - riscv_program_puttemp(p, t); - p->writes_memory = true; - return ERROR_OK; -} - -int riscv_program_sw(struct riscv_program *p, enum gdb_regno d, riscv_addr_t addr) -{ - enum gdb_regno t = riscv_program_gah(p, addr) == 0 - ? GDB_REGNO_X0 - : riscv_program_gettemp(p); - if (riscv_program_lah(p, t, addr) != ERROR_OK) - return ERROR_FAIL; - if (riscv_program_insert(p, sw(d, t, riscv_program_gal(p, addr))) != ERROR_OK) - return ERROR_FAIL; - riscv_program_puttemp(p, t); - p->writes_memory = true; - return ERROR_OK; -} - -int riscv_program_sh(struct riscv_program *p, enum gdb_regno d, riscv_addr_t addr) -{ - enum gdb_regno t = riscv_program_gah(p, addr) == 0 - ? GDB_REGNO_X0 - : riscv_program_gettemp(p); - if (riscv_program_lah(p, t, addr) != ERROR_OK) - return ERROR_FAIL; - if (riscv_program_insert(p, sh(d, t, riscv_program_gal(p, addr))) != ERROR_OK) - return ERROR_FAIL; - riscv_program_puttemp(p, t); - p->writes_memory = true; - return ERROR_OK; -} - -int riscv_program_sb(struct riscv_program *p, enum gdb_regno d, riscv_addr_t addr) -{ - enum gdb_regno t = riscv_program_gah(p, addr) == 0 - ? GDB_REGNO_X0 - : riscv_program_gettemp(p); - if (riscv_program_lah(p, t, addr) != ERROR_OK) - return ERROR_FAIL; - if (riscv_program_insert(p, sb(d, t, riscv_program_gal(p, addr))) != ERROR_OK) - return ERROR_FAIL; - riscv_program_puttemp(p, t); - p->writes_memory = true; - return ERROR_OK; -} - int riscv_program_csrr(struct riscv_program *p, enum gdb_regno d, enum gdb_regno csr) { - assert(csr >= GDB_REGNO_CSR0); - return riscv_program_insert(p, csrrs(d, GDB_REGNO_X0, csr - GDB_REGNO_CSR0)); + assert(csr >= GDB_REGNO_CSR0 && csr <= GDB_REGNO_CSR4095); + return riscv_program_insert(p, csrrs(d, GDB_REGNO_ZERO, csr - GDB_REGNO_CSR0)); } int riscv_program_csrw(struct riscv_program *p, enum gdb_regno s, enum gdb_regno csr) { assert(csr >= GDB_REGNO_CSR0); - return riscv_program_insert(p, csrrw(GDB_REGNO_X0, s, csr - GDB_REGNO_CSR0)); -} - -int riscv_program_csrrw(struct riscv_program *p, enum gdb_regno d, enum gdb_regno s, enum gdb_regno csr) -{ - assert(csr >= GDB_REGNO_CSR0); - return riscv_program_insert(p, csrrw(d, s, csr - GDB_REGNO_CSR0)); + return riscv_program_insert(p, csrrw(GDB_REGNO_ZERO, s, csr - GDB_REGNO_CSR0)); } int riscv_program_fence_i(struct riscv_program *p) @@ -357,156 +133,25 @@ int riscv_program_fence(struct riscv_program *p) int riscv_program_ebreak(struct riscv_program *p) { + struct target *target = p->target; + RISCV_INFO(r); + if (p->instruction_count == riscv_debug_buffer_size(p->target) && + r->impebreak) { + return ERROR_OK; + } return riscv_program_insert(p, ebreak()); } -int riscv_program_lui(struct riscv_program *p, enum gdb_regno d, int32_t u) -{ - return riscv_program_insert(p, lui(d, u)); -} - int riscv_program_addi(struct riscv_program *p, enum gdb_regno d, enum gdb_regno s, int16_t u) { return riscv_program_insert(p, addi(d, s, u)); } -int riscv_program_fsx(struct riscv_program *p, enum gdb_regno d, riscv_addr_t addr) -{ - assert(d >= GDB_REGNO_FPR0); - assert(d <= GDB_REGNO_FPR31); - enum gdb_regno t = riscv_program_gah(p, addr) == 0 - ? GDB_REGNO_X0 - : riscv_program_gettemp(p); - if (riscv_program_lah(p, t, addr) != ERROR_OK) - return ERROR_FAIL; - uint32_t instruction; - switch (p->target->reg_cache->reg_list[GDB_REGNO_FPR0].size) { - case 64: - instruction = fsd(d - GDB_REGNO_FPR0, t, riscv_program_gal(p, addr)); - break; - case 32: - instruction = fsw(d - GDB_REGNO_FPR0, t, riscv_program_gal(p, addr)); - break; - default: - return ERROR_FAIL; - } - if (riscv_program_insert(p, instruction) != ERROR_OK) - return ERROR_FAIL; - riscv_program_puttemp(p, t); - p->writes_memory = true; - return ERROR_OK; -} - -int riscv_program_flx(struct riscv_program *p, enum gdb_regno d, riscv_addr_t addr) -{ - assert(d >= GDB_REGNO_FPR0); - assert(d <= GDB_REGNO_FPR31); - enum gdb_regno t = riscv_program_gah(p, addr) == 0 ? GDB_REGNO_X0 : d; - if (riscv_program_lah(p, t, addr) != ERROR_OK) - return ERROR_FAIL; - uint32_t instruction; - switch (p->target->reg_cache->reg_list[GDB_REGNO_FPR0].size) { - case 64: - instruction = fld(d - GDB_REGNO_FPR0, t, riscv_program_gal(p, addr)); - break; - case 32: - instruction = flw(d - GDB_REGNO_FPR0, t, riscv_program_gal(p, addr)); - break; - default: - return ERROR_FAIL; - } - if (riscv_program_insert(p, instruction) != ERROR_OK) - return ERROR_FAIL; - return ERROR_OK; -} - -int riscv_program_li(struct riscv_program *p, enum gdb_regno d, riscv_reg_t c) -{ - if (riscv_program_lui(p, d, c >> 12) != ERROR_OK) - return ERROR_FAIL; - if (riscv_program_addi(p, d, d, c & 0xFFF) != ERROR_OK) - return ERROR_FAIL; - return ERROR_OK; -} - -int riscv_program_dont_restore_register(struct riscv_program *p, enum gdb_regno r) -{ - assert(r < RISCV_REGISTER_COUNT); - p->writes_xreg[r] = 0; - return ERROR_OK; -} - -int riscv_program_do_restore_register(struct riscv_program *p, enum gdb_regno r) -{ - assert(r < RISCV_REGISTER_COUNT); - p->writes_xreg[r] = 1; - return ERROR_OK; -} - -void riscv_program_reserve_register(struct riscv_program *p, enum gdb_regno r) -{ - assert(r < RISCV_REGISTER_COUNT); - assert(p->in_use[r] == 0); - p->in_use[r] = 1; -} - -enum gdb_regno riscv_program_gettemp(struct riscv_program *p) -{ - for (size_t i = GDB_REGNO_S0; i <= GDB_REGNO_XPR31; ++i) { - if (p->in_use[i]) continue; - - riscv_program_do_restore_register(p, i); - p->in_use[i] = 1; - return i; - } - - LOG_ERROR("You've run out of temporary registers. This is impossible."); - abort(); -} - -void riscv_program_puttemp(struct riscv_program *p, enum gdb_regno r) -{ - assert(r < RISCV_REGISTER_COUNT); - p->in_use[r] = 0; -} - -/* Helper functions. */ -riscv_addr_t riscv_program_gah(struct riscv_program *p, riscv_addr_t addr) -{ - return addr >> 12; -} - -riscv_addr_t riscv_program_gal(struct riscv_program *p, riscv_addr_t addr) -{ - if (addr > 0) { - return (addr & 0x7FF); - } else { - return 0; - } -} - -int riscv_program_lah(struct riscv_program *p, enum gdb_regno d, riscv_addr_t addr) -{ - riscv_addr_t ah = riscv_program_gah(p, addr); - if (ah == 0) - return ERROR_OK; - return riscv_program_lui(p, d, ah); -} - -int riscv_program_lal(struct riscv_program *p, enum gdb_regno d, riscv_addr_t addr) -{ - riscv_addr_t al = riscv_program_gal(p, addr); - if (al == 0) - return ERROR_OK; - return riscv_program_addi(p, d, d, al); -} - int riscv_program_insert(struct riscv_program *p, riscv_insn_t i) { - if (p->instruction_count + p->data_count + 1 > riscv_debug_buffer_size(p->target)) { + if (p->instruction_count >= riscv_debug_buffer_size(p->target)) { LOG_ERROR("Unable to insert instruction:"); LOG_ERROR(" instruction_count=%d", (int)p->instruction_count); - LOG_ERROR(" data_count =%d", (int)p->data_count); LOG_ERROR(" buffer size =%d", (int)riscv_debug_buffer_size(p->target)); return ERROR_FAIL; } diff --git a/src/target/riscv/program.h b/src/target/riscv/program.h index ac1127ed9..d641be1be 100644 --- a/src/target/riscv/program.h +++ b/src/target/riscv/program.h @@ -15,23 +15,12 @@ struct riscv_program { uint32_t debug_buffer[RISCV_MAX_DEBUG_BUFFER_SIZE]; - /* The debug buffer is allocated in two directions: instructions go at - * the start, while data goes at the end. When they meet in the middle - * this blows up. */ + /* Number of 32-bit instructions in the program. */ size_t instruction_count; - size_t data_count; /* Side effects of executing this program. These must be accounted for * in order to maintain correct executing of the target system. */ bool writes_xreg[RISCV_REGISTER_COUNT]; - bool writes_memory; - - /* When a register is used it will be set in this array. */ - bool in_use[RISCV_REGISTER_COUNT]; - - /* Remembers the registers that have been saved into dscratch - * registers. These are restored */ - enum gdb_regno dscratch_saved[RISCV_DSCRATCH_COUNT]; /* XLEN on the target. */ int target_xlen; @@ -40,6 +29,9 @@ struct riscv_program { /* Initializes a program with the header. */ int riscv_program_init(struct riscv_program *p, struct target *t); +/* Write the program to the program buffer. */ +int riscv_program_write(struct riscv_program *program); + /* Executes a program, returning 0 if the program successfully executed. Note * that this may cause registers to be saved or restored, which could result to * calls to things like riscv_save_register which itself could require a @@ -60,83 +52,24 @@ int riscv_program_insert(struct riscv_program *p, riscv_insn_t i); * memory. */ int riscv_program_save_to_dscratch(struct riscv_program *p, enum gdb_regno to_save); -/* Allocates data of various sizes. Either returns the absolute physical - * address or RISCV_PROGRAM_ALLOC_FAIL on failure. */ -riscv_addr_t riscv_program_alloc_data(struct riscv_program *p, size_t bytes); -riscv_addr_t riscv_program_alloc_x(struct riscv_program *p); -riscv_addr_t riscv_program_alloc_d(struct riscv_program *p); -riscv_addr_t riscv_program_alloc_w(struct riscv_program *p); -riscv_addr_t riscv_program_alloc_h(struct riscv_program *p); -riscv_addr_t riscv_program_alloc_b(struct riscv_program *p); -#define RISCV_PROGRAM_ALLOC_FAIL ((riscv_addr_t)(-1)) - -/* Reads a word of memory from this program's internal view of the debug RAM. - * This is what you want to use to get data back from the program after it - * executes. */ -riscv_insn_t riscv_program_read_ram(struct riscv_program *p, riscv_addr_t addr); -void riscv_program_write_ram(struct riscv_program *p, riscv_addr_t a, uint64_t d); - /* Helpers to assembly various instructions. Return 0 on success. These might * assembly into a multi-instruction sequence that overwrites some other * register, but those will be properly saved and restored. */ -int riscv_program_lx(struct riscv_program *p, enum gdb_regno d, riscv_addr_t addr); -int riscv_program_ld(struct riscv_program *p, enum gdb_regno d, riscv_addr_t addr); -int riscv_program_lw(struct riscv_program *p, enum gdb_regno d, riscv_addr_t addr); -int riscv_program_lh(struct riscv_program *p, enum gdb_regno d, riscv_addr_t addr); -int riscv_program_lb(struct riscv_program *p, enum gdb_regno d, riscv_addr_t addr); - -int riscv_program_sx(struct riscv_program *p, enum gdb_regno s, riscv_addr_t addr); -int riscv_program_sd(struct riscv_program *p, enum gdb_regno s, riscv_addr_t addr); -int riscv_program_sw(struct riscv_program *p, enum gdb_regno s, riscv_addr_t addr); -int riscv_program_sh(struct riscv_program *p, enum gdb_regno s, riscv_addr_t addr); -int riscv_program_sb(struct riscv_program *p, enum gdb_regno s, riscv_addr_t addr); - -int riscv_program_lxr(struct riscv_program *p, enum gdb_regno d, enum gdb_regno a, int o); -int riscv_program_ldr(struct riscv_program *p, enum gdb_regno d, enum gdb_regno a, int o); int riscv_program_lwr(struct riscv_program *p, enum gdb_regno d, enum gdb_regno a, int o); int riscv_program_lhr(struct riscv_program *p, enum gdb_regno d, enum gdb_regno a, int o); int riscv_program_lbr(struct riscv_program *p, enum gdb_regno d, enum gdb_regno a, int o); -int riscv_program_sxr(struct riscv_program *p, enum gdb_regno s, enum gdb_regno a, int o); -int riscv_program_sdr(struct riscv_program *p, enum gdb_regno s, enum gdb_regno a, int o); int riscv_program_swr(struct riscv_program *p, enum gdb_regno s, enum gdb_regno a, int o); int riscv_program_shr(struct riscv_program *p, enum gdb_regno s, enum gdb_regno a, int o); int riscv_program_sbr(struct riscv_program *p, enum gdb_regno s, enum gdb_regno a, int o); int riscv_program_csrr(struct riscv_program *p, enum gdb_regno d, enum gdb_regno csr); int riscv_program_csrw(struct riscv_program *p, enum gdb_regno s, enum gdb_regno csr); -int riscv_program_csrrw(struct riscv_program *p, enum gdb_regno d, enum gdb_regno s, enum gdb_regno csr); int riscv_program_fence_i(struct riscv_program *p); int riscv_program_fence(struct riscv_program *p); int riscv_program_ebreak(struct riscv_program *p); -int riscv_program_lui(struct riscv_program *p, enum gdb_regno d, int32_t u); int riscv_program_addi(struct riscv_program *p, enum gdb_regno d, enum gdb_regno s, int16_t i); -int riscv_program_fsx(struct riscv_program *p, enum gdb_regno s, riscv_addr_t addr); -int riscv_program_flx(struct riscv_program *p, enum gdb_regno d, riscv_addr_t addr); - -/* Assembler macros. */ -int riscv_program_li(struct riscv_program *p, enum gdb_regno d, riscv_reg_t c); -int riscv_program_la(struct riscv_program *p, enum gdb_regno d, riscv_addr_t a); - -/* Register allocation. The user is expected to have obtained temporary - * registers using these fuctions. Additionally, there is an interface for - * reserving registers -- it's expected that this has been called as the first - * thing in the program's execution to reserve registers that can't be touched - * by the program's execution. */ -void riscv_program_reserve_register(struct riscv_program *p, enum gdb_regno r); -enum gdb_regno riscv_program_gettemp(struct riscv_program *p); -void riscv_program_puttemp(struct riscv_program *p, enum gdb_regno r); - -/* Executing a program usually causes the registers that get overwritten to be - * saved and restored. Calling this prevents the given register from actually - * being restored as a result of all activity in this program. */ -int riscv_program_dont_restore_register(struct riscv_program *p, enum gdb_regno r); -int riscv_program_do_restore_register(struct riscv_program *p, enum gdb_regno r); - -/* Addressing functions. */ -riscv_addr_t riscv_program_gah(struct riscv_program *p, riscv_addr_t addr); - #endif diff --git a/src/target/riscv/riscv-011.c b/src/target/riscv/riscv-011.c index 6e576fef9..632567f35 100644 --- a/src/target/riscv/riscv-011.c +++ b/src/target/riscv/riscv-011.c @@ -34,32 +34,32 @@ * Code structure * * At the bottom of the stack are the OpenOCD JTAG functions: - * jtag_add_[id]r_scan - * jtag_execute_query - * jtag_add_runtest + * jtag_add_[id]r_scan + * jtag_execute_query + * jtag_add_runtest * * There are a few functions to just instantly shift a register and get its * value: - * dtmcontrol_scan - * idcode_scan - * dbus_scan + * dtmcontrol_scan + * idcode_scan + * dbus_scan * * Because doing one scan and waiting for the result is slow, most functions * batch up a bunch of dbus writes and then execute them all at once. They use * the scans "class" for this: - * scans_new - * scans_delete - * scans_execute - * scans_add_... + * scans_new + * scans_delete + * scans_execute + * scans_add_... * Usually you new(), call a bunch of add functions, then execute() and look * at the results by calling scans_get...() * * Optimized functions will directly use the scans class above, but slightly * lazier code will use the cache functions that in turn use the scans * functions: - * cache_get... - * cache_set... - * cache_write + * cache_get... + * cache_set... + * cache_write * cache_set... update a local structure, which is then synced to the target * with cache_write(). Only Debug RAM words that are actually changed are sent * to the target. Afterwards use cache_get... to read results. @@ -70,7 +70,7 @@ #define DIM(x) (sizeof(x)/sizeof(*x)) -// Constants for legacy SiFive hardware breakpoints. +/* Constants for legacy SiFive hardware breakpoints. */ #define CSR_BPCONTROL_X (1<<0) #define CSR_BPCONTROL_W (1<<1) #define CSR_BPCONTROL_R (1<<2) @@ -189,25 +189,19 @@ typedef struct { struct memory_cache_line dram_cache[DRAM_CACHE_SIZE]; - /* Single buffer that contains all register names, instead of calling - * malloc for each register. Needs to be freed when reg_list is freed. */ - char *reg_names; - /* Single buffer that contains all register values. */ - void *reg_values; - - // Number of run-test/idle cycles the target requests we do after each dbus - // access. + /* Number of run-test/idle cycles the target requests we do after each dbus + * access. */ unsigned int dtmcontrol_idle; - // This value is incremented every time a dbus access comes back as "busy". - // It's used to determine how many run-test/idle cycles to feed the target - // in between accesses. + /* This value is incremented every time a dbus access comes back as "busy". + * It's used to determine how many run-test/idle cycles to feed the target + * in between accesses. */ unsigned int dbus_busy_delay; - // This value is incremented every time we read the debug interrupt as - // high. It's used to add extra run-test/idle cycles after setting debug - // interrupt high, so ideally we never have to perform a whole extra scan - // before the interrupt is cleared. + /* This value is incremented every time we read the debug interrupt as + * high. It's used to add extra run-test/idle cycles after setting debug + * interrupt high, so ideally we never have to perform a whole extra scan + * before the interrupt is cleared. */ unsigned int interrupt_high_delay; bool need_strict_step; @@ -223,7 +217,8 @@ typedef struct { static int poll_target(struct target *target, bool announce); static int riscv011_poll(struct target *target); -static int register_get(struct reg *reg); +static int get_register(struct target *target, riscv_reg_t *value, int hartid, + int regid); /*** Utility functions. ***/ @@ -255,7 +250,7 @@ static unsigned int slot_offset(const struct target *target, slot_t slot) LOG_ERROR("slot_offset called with xlen=%d, slot=%d", riscv_xlen(target), slot); assert(0); - return 0; // Silence -Werror=return-type + return 0; /* Silence -Werror=return-type */ } static uint32_t load_slot(const struct target *target, unsigned int dest, @@ -341,7 +336,7 @@ static void increase_dbus_busy_delay(struct target *target) { riscv011_info_t *info = get_info(target); info->dbus_busy_delay += info->dbus_busy_delay / 10 + 1; - LOG_INFO("dtmcontrol_idle=%d, dbus_busy_delay=%d, interrupt_high_delay=%d", + LOG_DEBUG("dtmcontrol_idle=%d, dbus_busy_delay=%d, interrupt_high_delay=%d", info->dtmcontrol_idle, info->dbus_busy_delay, info->interrupt_high_delay); @@ -352,7 +347,7 @@ static void increase_interrupt_high_delay(struct target *target) { riscv011_info_t *info = get_info(target); info->interrupt_high_delay += info->interrupt_high_delay / 10 + 1; - LOG_INFO("dtmcontrol_idle=%d, dbus_busy_delay=%d, interrupt_high_delay=%d", + LOG_DEBUG("dtmcontrol_idle=%d, dbus_busy_delay=%d, interrupt_high_delay=%d", info->dtmcontrol_idle, info->dbus_busy_delay, info->interrupt_high_delay); } @@ -374,19 +369,17 @@ static void add_dbus_scan(const struct target *target, struct scan_field *field, jtag_add_dr_scan(target->tap, 1, field, TAP_IDLE); int idle_count = info->dtmcontrol_idle + info->dbus_busy_delay; - if (data & DMCONTROL_INTERRUPT) { + if (data & DMCONTROL_INTERRUPT) idle_count += info->interrupt_high_delay; - } - if (idle_count) { + if (idle_count) jtag_add_runtest(idle_count, TAP_IDLE); - } } static void dump_field(const struct scan_field *field) { - static const char *op_string[] = {"nop", "r", "w", "?"}; - static const char *status_string[] = {"+", "?", "F", "b"}; + static const char * const op_string[] = {"nop", "r", "w", "?"}; + static const char * const status_string[] = {"+", "?", "F", "b"}; if (debug_level < LOG_LVL_DEBUG) return; @@ -437,23 +430,20 @@ static dbus_status_t dbus_scan(struct target *target, uint16_t *address_in, int idle_count = info->dtmcontrol_idle + info->dbus_busy_delay; - if (idle_count) { + if (idle_count) jtag_add_runtest(idle_count, TAP_IDLE); - } int retval = jtag_execute_queue(); if (retval != ERROR_OK) { LOG_ERROR("dbus_scan failed jtag scan"); - return retval; + return DBUS_STATUS_FAILED; } - if (data_in) { + if (data_in) *data_in = buf_get_u64(in, DBUS_DATA_START, DBUS_DATA_SIZE); - } - if (address_in) { + if (address_in) *address_in = buf_get_u32(in, DBUS_ADDRESS_START, info->addrbits); - } dump_field(&field); @@ -465,19 +455,26 @@ static uint64_t dbus_read(struct target *target, uint16_t address) uint64_t value; dbus_status_t status; uint16_t address_in; - + + /* If the previous read/write was to the same address, we will get the read data + * from the previous access. + * While somewhat nonintuitive, this is an efficient way to get the data. + */ + unsigned i = 0; do { status = dbus_scan(target, &address_in, &value, DBUS_OP_READ, address, 0); - if (status == DBUS_STATUS_BUSY) { + if (status == DBUS_STATUS_BUSY) increase_dbus_busy_delay(target); + if (status == DBUS_STATUS_FAILED) { + LOG_ERROR("dbus_read(0x%x) failed!", address); + return 0; } } while (((status == DBUS_STATUS_BUSY) || (address_in != address)) && i++ < 256); - if (status != DBUS_STATUS_SUCCESS) { + if (status != DBUS_STATUS_SUCCESS) LOG_ERROR("failed read from 0x%x; value=0x%" PRIx64 ", status=%d\n", address, value, status); - } return value; } @@ -488,21 +485,19 @@ static void dbus_write(struct target *target, uint16_t address, uint64_t value) unsigned i = 0; while (status == DBUS_STATUS_BUSY && i++ < 256) { status = dbus_scan(target, NULL, NULL, DBUS_OP_WRITE, address, value); - if (status == DBUS_STATUS_BUSY) { + if (status == DBUS_STATUS_BUSY) increase_dbus_busy_delay(target); - } } - if (status != DBUS_STATUS_SUCCESS) { + if (status != DBUS_STATUS_SUCCESS) LOG_ERROR("failed to write 0x%" PRIx64 " to 0x%x; status=%d\n", value, address, status); - } } /*** scans "class" ***/ typedef struct { - // Number of scans that space is reserved for. + /* Number of scans that space is reserved for. */ unsigned int scan_count; - // Size reserved in memory for each scan, in bytes. + /* Size reserved in memory for each scan, in bytes. */ unsigned int scan_size; unsigned int next_scan; uint8_t *in; @@ -515,7 +510,7 @@ static scans_t *scans_new(struct target *target, unsigned int scan_count) { scans_t *scans = malloc(sizeof(scans_t)); scans->scan_count = scan_count; - // This code also gets called before xlen is detected. + /* This code also gets called before xlen is detected. */ if (riscv_xlen(target)) scans->scan_size = 2 + riscv_xlen(target) / 8; else @@ -545,9 +540,8 @@ static void scans_reset(scans_t *scans) static void scans_dump(scans_t *scans) { - for (unsigned int i = 0; i < scans->next_scan; i++) { + for (unsigned int i = 0; i < scans->next_scan; i++) dump_field(&scans->field[i]); - } } static int scans_execute(scans_t *scans) @@ -685,6 +679,9 @@ static bits_t read_bits(struct target *target) return err_result; } increase_dbus_busy_delay(target); + } else if (status == DBUS_STATUS_FAILED) { + // TODO: return an actual error + return err_result; } } while (status == DBUS_STATUS_BUSY && i++ < 256); @@ -705,17 +702,16 @@ static int wait_for_debugint_clear(struct target *target, bool ignore_first) { time_t start = time(NULL); if (ignore_first) { - // Throw away the results of the first read, since they'll contain the - // result of the read that happened just before debugint was set. - // (Assuming the last scan before calling this function was one that - // sets debugint.) + /* Throw away the results of the first read, since they'll contain the + * result of the read that happened just before debugint was set. + * (Assuming the last scan before calling this function was one that + * sets debugint.) */ read_bits(target); } while (1) { bits_t bits = read_bits(target); - if (!bits.interrupt) { + if (!bits.interrupt) return ERROR_OK; - } if (time(NULL) - start > riscv_command_timeout_sec) { LOG_ERROR("Timed out waiting for debug int to clear." "Increase timeout with riscv set_command_timeout_sec."); @@ -742,7 +738,7 @@ static void cache_set32(struct target *target, unsigned int index, uint32_t data riscv011_info_t *info = get_info(target); if (info->dram_cache[index].valid && info->dram_cache[index].data == data) { - // This is already preset on the target. + /* This is already preset on the target. */ LOG_DEBUG("cache[0x%x] = 0x%08x: DASM(0x%x) (hit)", index, data, data); return; } @@ -756,9 +752,8 @@ static void cache_set(struct target *target, slot_t slot, uint64_t data) { unsigned int offset = slot_offset(target, slot); cache_set32(target, offset, data); - if (riscv_xlen(target) > 32) { + if (riscv_xlen(target) > 32) cache_set32(target, offset + 1, data >> 32); - } } static void cache_set_jump(struct target *target, unsigned int index) @@ -805,9 +800,8 @@ static void cache_clean(struct target *target) { riscv011_info_t *info = get_info(target); for (unsigned int i = 0; i < info->dramsize; i++) { - if (i >= 4) { + if (i >= 4) info->dram_cache[i].valid = false; - } info->dram_cache[i].dirty = false; } } @@ -819,9 +813,8 @@ static int cache_check(struct target *target) for (unsigned int i = 0; i < info->dramsize; i++) { if (info->dram_cache[i].valid && !info->dram_cache[i].dirty) { - if (dram_check32(target, i, info->dram_cache[i].data) != ERROR_OK) { + if (dram_check32(target, i, info->dram_cache[i].data) != ERROR_OK) error++; - } } } @@ -844,13 +837,12 @@ static int cache_write(struct target *target, unsigned int address, bool run) unsigned int last = info->dramsize; for (unsigned int i = 0; i < info->dramsize; i++) { - if (info->dram_cache[i].dirty) { + if (info->dram_cache[i].dirty) last = i; - } } if (last == info->dramsize) { - // Nothing needs to be written to RAM. + /* Nothing needs to be written to RAM. */ dbus_write(target, DMCONTROL, DMCONTROL_HALTNOT | (run ? DMCONTROL_INTERRUPT : 0)); } else { @@ -864,17 +856,18 @@ static int cache_write(struct target *target, unsigned int address, bool run) } if (run || address < CACHE_NO_READ) { - // Throw away the results of the first read, since it'll contain the - // result of the read that happened just before debugint was set. + /* Throw away the results of the first read, since it'll contain the + * result of the read that happened just before debugint was set. */ scans_add_read32(scans, address, false); - // This scan contains the results of the read the caller requested, as - // well as an interrupt bit worth looking at. + /* This scan contains the results of the read the caller requested, as + * well as an interrupt bit worth looking at. */ scans_add_read32(scans, address, false); } int retval = scans_execute(scans); if (retval != ERROR_OK) { + scans_delete(scans); LOG_ERROR("JTAG execute failed."); return retval; } @@ -888,12 +881,14 @@ static int cache_write(struct target *target, unsigned int address, bool run) break; case DBUS_STATUS_FAILED: LOG_ERROR("Debug RAM write failed. Hardware error?"); + scans_delete(scans); return ERROR_FAIL; case DBUS_STATUS_BUSY: errors++; break; default: LOG_ERROR("Got invalid bus access status: %d", status); + scans_delete(scans); return ERROR_FAIL; } } @@ -901,23 +896,22 @@ static int cache_write(struct target *target, unsigned int address, bool run) if (errors) { increase_dbus_busy_delay(target); - // Try again, using the slow careful code. - // Write all RAM, just to be extra cautious. + /* Try again, using the slow careful code. + * Write all RAM, just to be extra cautious. */ for (unsigned int i = 0; i < info->dramsize; i++) { - if (i == last && run) { + if (i == last && run) dram_write32(target, last, info->dram_cache[last].data, true); - } else { + else dram_write32(target, i, info->dram_cache[i].data, false); - } info->dram_cache[i].dirty = false; } - if (run) { + if (run) cache_clean(target); - } if (wait_for_debugint_clear(target, true) != ERROR_OK) { LOG_ERROR("Debug interrupt didn't clear."); dump_debug_ram(target); + scans_delete(scans); return ERROR_FAIL; } @@ -925,9 +919,8 @@ static int cache_write(struct target *target, unsigned int address, bool run) if (run) { cache_clean(target); } else { - for (unsigned int i = 0; i < info->dramsize; i++) { + for (unsigned int i = 0; i < info->dramsize; i++) info->dram_cache[i].dirty = false; - } } if (run || address < CACHE_NO_READ) { @@ -935,14 +928,15 @@ static int cache_write(struct target *target, unsigned int address, bool run) DBUS_DATA_START + 33, 1); if (interrupt) { increase_interrupt_high_delay(target); - // Slow path wait for it to clear. + /* Slow path wait for it to clear. */ if (wait_for_debugint_clear(target, false) != ERROR_OK) { LOG_ERROR("Debug interrupt didn't clear."); dump_debug_ram(target); + scans_delete(scans); return ERROR_FAIL; } } else { - // We read a useful value in that last scan. + /* We read a useful value in that last scan. */ unsigned int read_addr = scans_get_u32(scans, scans->next_scan-1, DBUS_ADDRESS_START, info->addrbits); if (read_addr != address) { @@ -976,9 +970,8 @@ static uint64_t cache_get(struct target *target, slot_t slot) { unsigned int offset = slot_offset(target, slot); uint64_t value = cache_get32(target, offset); - if (riscv_xlen(target) > 32) { + if (riscv_xlen(target) > 32) value |= ((uint64_t) cache_get32(target, offset + 1)) << 32; - } return value; } @@ -997,12 +990,10 @@ static int wait_for_state(struct target *target, enum target_state state) time_t start = time(NULL); while (1) { int result = riscv011_poll(target); - if (result != ERROR_OK) { + if (result != ERROR_OK) return result; - } - if (target->state == state) { + if (target->state == state) return ERROR_OK; - } if (time(NULL) - start > riscv_command_timeout_sec) { LOG_ERROR("Timed out waiting for state %d. " "Increase timeout with riscv set_command_timeout_sec.", state); @@ -1017,15 +1008,15 @@ static int read_csr(struct target *target, uint64_t *value, uint32_t csr) cache_set32(target, 0, csrr(S0, csr)); cache_set_store(target, 1, S0, SLOT0); cache_set_jump(target, 2); - if (cache_write(target, 4, true) != ERROR_OK) { + if (cache_write(target, 4, true) != ERROR_OK) return ERROR_FAIL; - } *value = cache_get(target, SLOT0); LOG_DEBUG("csr 0x%x = 0x%" PRIx64, csr, *value); uint32_t exception = cache_get32(target, info->dramsize-1); if (exception) { - LOG_WARNING("Got exception 0x%x when reading CSR 0x%x", exception, csr); + LOG_WARNING("Got exception 0x%x when reading %s", exception, + gdb_regno_name(GDB_REGNO_CSR0 + csr)); *value = ~0; return ERROR_FAIL; } @@ -1040,9 +1031,8 @@ static int write_csr(struct target *target, uint32_t csr, uint64_t value) cache_set32(target, 1, csrw(S0, csr)); cache_set_jump(target, 2); cache_set(target, SLOT0, value); - if (cache_write(target, 4, true) != ERROR_OK) { + if (cache_write(target, 4, true) != ERROR_OK) return ERROR_FAIL; - } return ERROR_OK; } @@ -1052,9 +1042,8 @@ static int write_gpr(struct target *target, unsigned int gpr, uint64_t value) cache_set_load(target, 0, gpr, SLOT0); cache_set_jump(target, 1); cache_set(target, SLOT0, value); - if (cache_write(target, 4, true) != ERROR_OK) { + if (cache_write(target, 4, true) != ERROR_OK) return ERROR_FAIL; - } return ERROR_OK; } @@ -1094,15 +1083,14 @@ static int execute_resume(struct target *target, bool step) maybe_write_tselect(target); - // TODO: check if dpc is dirty (which also is true if an exception was hit - // at any time) + /* TODO: check if dpc is dirty (which also is true if an exception was hit + * at any time) */ cache_set_load(target, 0, S0, SLOT0); cache_set32(target, 1, csrw(S0, CSR_DPC)); cache_set_jump(target, 2); cache_set(target, SLOT0, info->dpc); - if (cache_write(target, 4, true) != ERROR_OK) { + if (cache_write(target, 4, true) != ERROR_OK) return ERROR_FAIL; - } struct reg *mstatus_reg = &target->reg_cache->reg_list[GDB_REGNO_MSTATUS]; if (mstatus_reg->valid) { @@ -1112,27 +1100,25 @@ static int execute_resume(struct target *target, bool step) cache_set32(target, 1, csrw(S0, CSR_MSTATUS)); cache_set_jump(target, 2); cache_set(target, SLOT0, mstatus_user); - if (cache_write(target, 4, true) != ERROR_OK) { + if (cache_write(target, 4, true) != ERROR_OK) return ERROR_FAIL; - } } } info->dcsr |= DCSR_EBREAKM | DCSR_EBREAKH | DCSR_EBREAKS | DCSR_EBREAKU; info->dcsr &= ~DCSR_HALT; - if (step) { + if (step) info->dcsr |= DCSR_STEP; - } else { + else info->dcsr &= ~DCSR_STEP; - } dram_write32(target, 0, lw(S0, ZERO, DEBUG_RAM_START + 16), false); dram_write32(target, 1, csrw(S0, CSR_DCSR), false); dram_write32(target, 2, fence_i(), false); dram_write_jump(target, 3, false); - // Write DCSR value, set interrupt and clear haltnot. + /* Write DCSR value, set interrupt and clear haltnot. */ uint64_t dbus_value = DMCONTROL_INTERRUPT | info->dcsr; dbus_write(target, dram_address(4), dbus_value); @@ -1149,7 +1135,7 @@ static int execute_resume(struct target *target, bool step) return ERROR_OK; } -// Execute a step, and wait for reentry into Debug Mode. +/* Execute a step, and wait for reentry into Debug Mode. */ static int full_step(struct target *target, bool announce) { int result = execute_resume(target, true); @@ -1164,7 +1150,7 @@ static int full_step(struct target *target, bool announce) break; if (time(NULL) - start > riscv_command_timeout_sec) { LOG_ERROR("Timed out waiting for step to complete." - "Increase timeout with riscv set_command_timeout_sec"); + "Increase timeout with riscv set_command_timeout_sec"); return ERROR_FAIL; } } @@ -1181,30 +1167,6 @@ static int resume(struct target *target, int debug_execution, bool step) return execute_resume(target, step); } -/** Update register sizes based on xlen. */ -static void update_reg_list(struct target *target) -{ - riscv011_info_t *info = get_info(target); - if (info->reg_values) { - free(info->reg_values); - } - info->reg_values = malloc(GDB_REGNO_COUNT * riscv_xlen(target) / 4); - - for (unsigned int i = 0; i < GDB_REGNO_COUNT; i++) { - struct reg *r = &target->reg_cache->reg_list[i]; - r->value = info->reg_values + i * riscv_xlen(target) / 4; - if (r->dirty) { - LOG_ERROR("Register %d was dirty. Its value is lost.", i); - } - if (i == GDB_REGNO_PRIV) { - r->size = 8; - } else { - r->size = riscv_xlen(target); - } - r->valid = false; - } -} - static uint64_t reg_cache_get(struct target *target, unsigned int number) { struct reg *r = &target->reg_cache->reg_list[number]; @@ -1230,13 +1192,14 @@ static int update_mstatus_actual(struct target *target) { struct reg *mstatus_reg = &target->reg_cache->reg_list[GDB_REGNO_MSTATUS]; if (mstatus_reg->valid) { - // We previously made it valid. + /* We previously made it valid. */ return ERROR_OK; } - // Force reading the register. In that process mstatus_actual will be - // updated. - return register_get(&target->reg_cache->reg_list[GDB_REGNO_MSTATUS]); + /* Force reading the register. In that process mstatus_actual will be + * updated. */ + riscv_reg_t mstatus; + return get_register(target, &mstatus, 0, GDB_REGNO_MSTATUS); } /*** OpenOCD target functions. ***/ @@ -1253,14 +1216,12 @@ static int register_read(struct target *target, riscv_reg_t *value, int regnum) return ERROR_FAIL; } - if (cache_write(target, 4, true) != ERROR_OK) { + if (cache_write(target, 4, true) != ERROR_OK) return ERROR_FAIL; - } uint32_t exception = cache_get32(target, info->dramsize-1); if (exception) { - LOG_WARNING("Got exception 0x%x when reading register %d", exception, - regnum); + LOG_WARNING("Got exception 0x%x when reading %s", exception, gdb_regno_name(regnum)); *value = ~0; return ERROR_FAIL; } @@ -1268,66 +1229,13 @@ static int register_read(struct target *target, riscv_reg_t *value, int regnum) *value = cache_get(target, SLOT0); LOG_DEBUG("reg[%d]=0x%" PRIx64, regnum, *value); - if (regnum == GDB_REGNO_MSTATUS) { + if (regnum == GDB_REGNO_MSTATUS) info->mstatus_actual = *value; - } return ERROR_OK; } -static int register_get(struct reg *reg) -{ - struct target *target = (struct target *) reg->arch_info; - riscv011_info_t *info = get_info(target); - - maybe_write_tselect(target); - riscv_reg_t value = ~0; - - if (reg->number <= GDB_REGNO_XPR31) { - value = reg_cache_get(target, reg->number); - LOG_DEBUG("%s=0x%" PRIx64, reg->name, reg_cache_get(target, reg->number)); - } else if (reg->number == GDB_REGNO_PC) { - value = info->dpc; - LOG_DEBUG("%s=0x%" PRIx64 " (cached)", reg->name, info->dpc); - } else if (reg->number >= GDB_REGNO_FPR0 && reg->number <= GDB_REGNO_FPR31) { - int result = update_mstatus_actual(target); - if (result != ERROR_OK) { - return result; - } - unsigned i = 0; - if ((info->mstatus_actual & MSTATUS_FS) == 0) { - info->mstatus_actual = set_field(info->mstatus_actual, MSTATUS_FS, 1); - cache_set_load(target, i++, S0, SLOT1); - cache_set32(target, i++, csrw(S0, CSR_MSTATUS)); - cache_set(target, SLOT1, info->mstatus_actual); - } - - if (riscv_xlen(target) == 32) { - cache_set32(target, i++, fsw(reg->number - GDB_REGNO_FPR0, 0, DEBUG_RAM_START + 16)); - } else { - cache_set32(target, i++, fsd(reg->number - GDB_REGNO_FPR0, 0, DEBUG_RAM_START + 16)); - } - cache_set_jump(target, i++); - - if (cache_write(target, 4, true) != ERROR_OK) { - return ERROR_FAIL; - } - } else if (reg->number == GDB_REGNO_PRIV) { - value = get_field(info->dcsr, DCSR_PRV); - } else { - if (register_read(target, &value, reg->number) != ERROR_OK) - return ERROR_FAIL; - } - buf_set_u64(reg->value, 0, riscv_xlen(target), value); - - if (reg->number == GDB_REGNO_MSTATUS) { - reg->valid = true; - } - - return ERROR_OK; -} - -// Write the register. No caching or games. +/* Write the register. No caching or games. */ static int register_write(struct target *target, unsigned int number, uint64_t value) { @@ -1344,16 +1252,15 @@ static int register_write(struct target *target, unsigned int number, cache_set_store(target, 1, S0, SLOT_LAST); cache_set_jump(target, 2); } else if (number <= GDB_REGNO_XPR31) { - cache_set_load(target, 0, number - GDB_REGNO_XPR0, SLOT0); + cache_set_load(target, 0, number - GDB_REGNO_ZERO, SLOT0); cache_set_jump(target, 1); } else if (number == GDB_REGNO_PC) { info->dpc = value; return ERROR_OK; } else if (number >= GDB_REGNO_FPR0 && number <= GDB_REGNO_FPR31) { int result = update_mstatus_actual(target); - if (result != ERROR_OK) { + if (result != ERROR_OK) return result; - } unsigned i = 0; if ((info->mstatus_actual & MSTATUS_FS) == 0) { info->mstatus_actual = set_field(info->mstatus_actual, MSTATUS_FS, 1); @@ -1362,20 +1269,18 @@ static int register_write(struct target *target, unsigned int number, cache_set(target, SLOT1, info->mstatus_actual); } - if (riscv_xlen(target) == 32) { + if (riscv_xlen(target) == 32) cache_set32(target, i++, flw(number - GDB_REGNO_FPR0, 0, DEBUG_RAM_START + 16)); - } else { + else cache_set32(target, i++, fld(number - GDB_REGNO_FPR0, 0, DEBUG_RAM_START + 16)); - } cache_set_jump(target, i++); } else if (number >= GDB_REGNO_CSR0 && number <= GDB_REGNO_CSR4095) { cache_set_load(target, 0, S0, SLOT0); cache_set32(target, 1, csrw(S0, number - GDB_REGNO_CSR0)); cache_set_jump(target, 2); - if (number == GDB_REGNO_MSTATUS) { + if (number == GDB_REGNO_MSTATUS) info->mstatus_actual = value; - } } else if (number == GDB_REGNO_PRIV) { info->dcsr = set_field(info->dcsr, DCSR_PRV, value); return ERROR_OK; @@ -1385,56 +1290,70 @@ static int register_write(struct target *target, unsigned int number, } cache_set(target, SLOT0, value); - if (cache_write(target, info->dramsize - 1, true) != ERROR_OK) { + if (cache_write(target, info->dramsize - 1, true) != ERROR_OK) return ERROR_FAIL; - } uint32_t exception = cache_get32(target, info->dramsize-1); if (exception) { - LOG_WARNING("Got exception 0x%x when writing register %d", exception, - number); + LOG_WARNING("Got exception 0x%x when writing %s", exception, + gdb_regno_name(number)); return ERROR_FAIL; } return ERROR_OK; } -static int register_set(struct reg *reg, uint8_t *buf) -{ - struct target *target = (struct target *) reg->arch_info; - - uint64_t value = buf_get_u64(buf, 0, riscv_xlen(target)); - - LOG_DEBUG("write 0x%" PRIx64 " to %s", value, reg->name); - struct reg *r = &target->reg_cache->reg_list[reg->number]; - r->valid = true; - memcpy(r->value, buf, (r->size + 7) / 8); - - return register_write(target, reg->number, value); -} - -static struct reg_arch_type riscv_reg_arch_type = { - .get = register_get, - .set = register_set -}; - -static riscv_reg_t get_register(struct target *target, int hartid, int regid) +static int get_register(struct target *target, riscv_reg_t *value, int hartid, + int regid) { assert(hartid == 0); - riscv_reg_t value; - if (register_read(target, &value, regid) != ERROR_OK) { - // TODO: propagate errors - value = ~0; + riscv011_info_t *info = get_info(target); + + maybe_write_tselect(target); + + if (regid <= GDB_REGNO_XPR31) { + *value = reg_cache_get(target, regid); + } else if (regid == GDB_REGNO_PC) { + *value = info->dpc; + } else if (regid >= GDB_REGNO_FPR0 && regid <= GDB_REGNO_FPR31) { + int result = update_mstatus_actual(target); + if (result != ERROR_OK) + return result; + unsigned i = 0; + if ((info->mstatus_actual & MSTATUS_FS) == 0) { + info->mstatus_actual = set_field(info->mstatus_actual, MSTATUS_FS, 1); + cache_set_load(target, i++, S0, SLOT1); + cache_set32(target, i++, csrw(S0, CSR_MSTATUS)); + cache_set(target, SLOT1, info->mstatus_actual); + } + + if (riscv_xlen(target) == 32) + cache_set32(target, i++, fsw(regid - GDB_REGNO_FPR0, 0, DEBUG_RAM_START + 16)); + else + cache_set32(target, i++, fsd(regid - GDB_REGNO_FPR0, 0, DEBUG_RAM_START + 16)); + cache_set_jump(target, i++); + + if (cache_write(target, 4, true) != ERROR_OK) + return ERROR_FAIL; + } else if (regid == GDB_REGNO_PRIV) { + *value = get_field(info->dcsr, DCSR_PRV); + } else { + int result = register_read(target, value, regid); + if (result != ERROR_OK) + return result; } - return value; + + if (regid == GDB_REGNO_MSTATUS) + target->reg_cache->reg_list[regid].valid = true; + + return ERROR_OK; } -static void set_register(struct target *target, int hartid, int regid, +static int set_register(struct target *target, int hartid, int regid, uint64_t value) { assert(hartid == 0); - // TODO: propagate errors - register_write(target, regid, value); + return register_write(target, regid, value); } static int halt(struct target *target) @@ -1466,45 +1385,10 @@ static int init_target(struct command_context *cmd_ctx, generic_info->version_specific = calloc(1, sizeof(riscv011_info_t)); if (!generic_info->version_specific) return ERROR_FAIL; - riscv011_info_t *info = get_info(target); - target->reg_cache = calloc(1, sizeof(*target->reg_cache)); - target->reg_cache->name = "RISC-V registers"; - target->reg_cache->num_regs = GDB_REGNO_COUNT; - - target->reg_cache->reg_list = calloc(GDB_REGNO_COUNT, sizeof(struct reg)); - - const unsigned int max_reg_name_len = 12; - info->reg_names = calloc(1, GDB_REGNO_COUNT * max_reg_name_len); - char *reg_name = info->reg_names; - info->reg_values = NULL; - - for (unsigned int i = 0; i < GDB_REGNO_COUNT; i++) { - struct reg *r = &target->reg_cache->reg_list[i]; - r->number = i; - r->caller_save = true; - r->dirty = false; - r->valid = false; - r->exist = true; - r->type = &riscv_reg_arch_type; - r->arch_info = target; - if (i <= GDB_REGNO_XPR31) { - sprintf(reg_name, "x%d", i); - } else if (i == GDB_REGNO_PC) { - sprintf(reg_name, "pc"); - } else if (i >= GDB_REGNO_FPR0 && i <= GDB_REGNO_FPR31) { - sprintf(reg_name, "f%d", i - GDB_REGNO_FPR0); - } else if (i >= GDB_REGNO_CSR0 && i <= GDB_REGNO_CSR4095) { - sprintf(reg_name, "csr%d", i - GDB_REGNO_CSR0); - } else if (i == GDB_REGNO_PRIV) { - sprintf(reg_name, "priv"); - } - if (reg_name[0]) { - r->name = reg_name; - } - reg_name += strlen(reg_name) + 1; - assert(reg_name < info->reg_names + GDB_REGNO_COUNT * max_reg_name_len); - } + /* Assume 32-bit until we discover the real value in examine(). */ + generic_info->xlen[0] = 32; + riscv_init_registers(target); return ERROR_OK; } @@ -1586,7 +1470,7 @@ static int step(struct target *target, int current, target_addr_t address, static int examine(struct target *target) { - // Don't need to select dbus, since the first thing we do is read dtmcontrol. + /* Don't need to select dbus, since the first thing we do is read dtmcontrol. */ uint32_t dtmcontrol = dtmcontrol_scan(target, 0); LOG_DEBUG("dtmcontrol=0x%x", dtmcontrol); @@ -1610,7 +1494,7 @@ static int examine(struct target *target) info->addrbits = get_field(dtmcontrol, DTMCONTROL_ADDRBITS); info->dtmcontrol_idle = get_field(dtmcontrol, DTMCONTROL_IDLE); if (info->dtmcontrol_idle == 0) { - // Some old SiFive cores don't set idle but need it to be 1. + /* Some old SiFive cores don't set idle but need it to be 1. */ uint32_t idcode = idcode_scan(target); if (idcode == 0x10e31913) info->dtmcontrol_idle = 1; @@ -1645,29 +1529,27 @@ static int examine(struct target *target) return ERROR_FAIL; } - // Pretend this is a 32-bit system until we have found out the true value. + /* Pretend this is a 32-bit system until we have found out the true value. */ r->xlen[0] = 32; - // Figure out XLEN, and test writing all of Debug RAM while we're at it. + /* Figure out XLEN, and test writing all of Debug RAM while we're at it. */ cache_set32(target, 0, xori(S1, ZERO, -1)); - // 0xffffffff 0xffffffff:ffffffff 0xffffffff:ffffffff:ffffffff:ffffffff + /* 0xffffffff 0xffffffff:ffffffff 0xffffffff:ffffffff:ffffffff:ffffffff */ cache_set32(target, 1, srli(S1, S1, 31)); - // 0x00000001 0x00000001:ffffffff 0x00000001:ffffffff:ffffffff:ffffffff + /* 0x00000001 0x00000001:ffffffff 0x00000001:ffffffff:ffffffff:ffffffff */ cache_set32(target, 2, sw(S1, ZERO, DEBUG_RAM_START)); cache_set32(target, 3, srli(S1, S1, 31)); - // 0x00000000 0x00000000:00000003 0x00000000:00000003:ffffffff:ffffffff + /* 0x00000000 0x00000000:00000003 0x00000000:00000003:ffffffff:ffffffff */ cache_set32(target, 4, sw(S1, ZERO, DEBUG_RAM_START + 4)); cache_set_jump(target, 5); - for (unsigned i = 6; i < info->dramsize; i++) { + for (unsigned i = 6; i < info->dramsize; i++) cache_set32(target, i, i * 0x01020304); - } cache_write(target, 0, false); - // Check that we can actually read/write dram. - if (cache_check(target) != ERROR_OK) { + /* Check that we can actually read/write dram. */ + if (cache_check(target) != ERROR_OK) return ERROR_FAIL; - } cache_write(target, 0, true); cache_invalidate(target); @@ -1690,27 +1572,26 @@ static int examine(struct target *target) } LOG_DEBUG("Discovered XLEN is %d", riscv_xlen(target)); - // Update register list to match discovered XLEN. - update_reg_list(target); - if (read_csr(target, &r->misa, CSR_MISA) != ERROR_OK) { const unsigned old_csr_misa = 0xf10; LOG_WARNING("Failed to read misa at 0x%x; trying 0x%x.", CSR_MISA, old_csr_misa); if (read_csr(target, &r->misa, old_csr_misa) != ERROR_OK) { - // Maybe this is an old core that still has $misa at the old - // address. + /* Maybe this is an old core that still has $misa at the old + * address. */ LOG_ERROR("Failed to read misa at 0x%x.", old_csr_misa); return ERROR_FAIL; } } + /* Update register list to match discovered XLEN/supported extensions. */ + riscv_init_registers(target); + info->never_halted = true; int result = riscv011_poll(target); - if (result != ERROR_OK) { + if (result != ERROR_OK) return result; - } target_set_examined(target); riscv_set_current_hartid(target, 0); @@ -1728,41 +1609,40 @@ static riscv_error_t handle_halt_routine(struct target *target) scans_t *scans = scans_new(target, 256); - // Read all GPRs as fast as we can, because gdb is going to ask for them - // anyway. Reading them one at a time is much slower. + /* Read all GPRs as fast as we can, because gdb is going to ask for them + * anyway. Reading them one at a time is much slower. */ - // Write the jump back to address 1. + /* Write the jump back to address 1. */ scans_add_write_jump(scans, 1, false); for (int reg = 1; reg < 32; reg++) { - if (reg == S0 || reg == S1) { + if (reg == S0 || reg == S1) continue; - } - // Write store instruction. + /* Write store instruction. */ scans_add_write_store(scans, 0, reg, SLOT0, true); - // Read value. + /* Read value. */ scans_add_read(scans, SLOT0, false); } - // Write store of s0 at index 1. + /* Write store of s0 at index 1. */ scans_add_write_store(scans, 1, S0, SLOT0, false); - // Write jump at index 2. + /* Write jump at index 2. */ scans_add_write_jump(scans, 2, false); - // Read S1 from debug RAM + /* Read S1 from debug RAM */ scans_add_write_load(scans, 0, S0, SLOT_LAST, true); - // Read value. + /* Read value. */ scans_add_read(scans, SLOT0, false); - // Read S0 from dscratch + /* Read S0 from dscratch */ unsigned int csr[] = {CSR_DSCRATCH, CSR_DPC, CSR_DCSR}; for (unsigned int i = 0; i < DIM(csr); i++) { scans_add_write32(scans, 0, csrr(S0, csr[i]), true); scans_add_read(scans, SLOT0, false); } - // Final read to get the last value out. + /* Final read to get the last value out. */ scans_add_read32(scans, 4, false); int retval = scans_execute(scans); @@ -1776,8 +1656,8 @@ static riscv_error_t handle_halt_routine(struct target *target) unsigned result = 0; uint64_t value = 0; reg_cache_set(target, 0, 0); - // The first scan result is the result from something old we don't care - // about. + /* The first scan result is the result from something old we don't care + * about. */ for (unsigned int i = 1; i < scans->next_scan && dbus_busy == 0; i++) { dbus_status_t status = scans_get_u32(scans, i, DBUS_OP_START, DBUS_OP_SIZE); @@ -1804,43 +1684,109 @@ static riscv_error_t handle_halt_routine(struct target *target) if (address == 4 || address == 5) { unsigned int reg; switch (result) { - case 0: reg = 1; break; - case 1: reg = 2; break; - case 2: reg = 3; break; - case 3: reg = 4; break; - case 4: reg = 5; break; - case 5: reg = 6; break; - case 6: reg = 7; break; - // S0 - // S1 - case 7: reg = 10; break; - case 8: reg = 11; break; - case 9: reg = 12; break; - case 10: reg = 13; break; - case 11: reg = 14; break; - case 12: reg = 15; break; - case 13: reg = 16; break; - case 14: reg = 17; break; - case 15: reg = 18; break; - case 16: reg = 19; break; - case 17: reg = 20; break; - case 18: reg = 21; break; - case 19: reg = 22; break; - case 20: reg = 23; break; - case 21: reg = 24; break; - case 22: reg = 25; break; - case 23: reg = 26; break; - case 24: reg = 27; break; - case 25: reg = 28; break; - case 26: reg = 29; break; - case 27: reg = 30; break; - case 28: reg = 31; break; - case 29: reg = S1; break; - case 30: reg = S0; break; - case 31: reg = CSR_DPC; break; - case 32: reg = CSR_DCSR; break; + case 0: + reg = 1; + break; + case 1: + reg = 2; + break; + case 2: + reg = 3; + break; + case 3: + reg = 4; + break; + case 4: + reg = 5; + break; + case 5: + reg = 6; + break; + case 6: + reg = 7; + break; + /* S0 */ + /* S1 */ + case 7: + reg = 10; + break; + case 8: + reg = 11; + break; + case 9: + reg = 12; + break; + case 10: + reg = 13; + break; + case 11: + reg = 14; + break; + case 12: + reg = 15; + break; + case 13: + reg = 16; + break; + case 14: + reg = 17; + break; + case 15: + reg = 18; + break; + case 16: + reg = 19; + break; + case 17: + reg = 20; + break; + case 18: + reg = 21; + break; + case 19: + reg = 22; + break; + case 20: + reg = 23; + break; + case 21: + reg = 24; + break; + case 22: + reg = 25; + break; + case 23: + reg = 26; + break; + case 24: + reg = 27; + break; + case 25: + reg = 28; + break; + case 26: + reg = 29; + break; + case 27: + reg = 30; + break; + case 28: + reg = 31; + break; + case 29: + reg = S1; + break; + case 30: + reg = S0; + break; + case 31: + reg = CSR_DPC; + break; + case 32: + reg = CSR_DCSR; + break; default: - assert(0); + assert(0); } if (riscv_xlen(target) == 32) { reg_cache_set(target, reg, data & 0xffffffff); @@ -1866,18 +1812,18 @@ static riscv_error_t handle_halt_routine(struct target *target) return RE_AGAIN; } - // TODO: get rid of those 2 variables and talk to the cache directly. + /* TODO: get rid of those 2 variables and talk to the cache directly. */ info->dpc = reg_cache_get(target, CSR_DPC); info->dcsr = reg_cache_get(target, CSR_DCSR); - scans = scans_delete(scans); + scans_delete(scans); cache_invalidate(target); return RE_OK; error: - scans = scans_delete(scans); + scans_delete(scans); return RE_FAIL; } @@ -1902,8 +1848,8 @@ static int handle_halt(struct target *target, bool announce) break; case DCSR_CAUSE_HWBP: target->debug_reason = DBG_REASON_WPTANDBKPT; - // If we halted because of a data trigger, gdb doesn't know to do - // the disable-breakpoints-step-enable-breakpoints dance. + /* If we halted because of a data trigger, gdb doesn't know to do + * the disable-breakpoints-step-enable-breakpoints dance. */ info->need_strict_step = true; break; case DCSR_CAUSE_DEBUGINT: @@ -1927,9 +1873,8 @@ static int handle_halt(struct target *target, bool announce) riscv_enumerate_triggers(target); } - if (announce) { + if (announce) target_call_event_callbacks(target, TARGET_EVENT_HALTED); - } const char *cause_string[] = { "none", @@ -1939,10 +1884,10 @@ static int handle_halt(struct target *target, bool announce) "step", "halt" }; - // This is logged to the user so that gdb will show it when a user types - // 'monitor reset init'. At that time gdb appears to have the pc cached - // still so if a user manually inspects the pc it will still have the old - // value. + /* This is logged to the user so that gdb will show it when a user types + * 'monitor reset init'. At that time gdb appears to have the pc cached + * still so if a user manually inspects the pc it will still have the old + * value. */ LOG_USER("halted at 0x%" PRIx64 " due to %s", info->dpc, cause_string[cause]); return ERROR_OK; @@ -1952,12 +1897,11 @@ static int poll_target(struct target *target, bool announce) { jtag_add_ir_scan(target->tap, &select_dbus, TAP_IDLE); - // Inhibit debug logging during poll(), which isn't usually interesting and - // just fills up the screen/logs with clutter. + /* Inhibit debug logging during poll(), which isn't usually interesting and + * just fills up the screen/logs with clutter. */ int old_debug_level = debug_level; - if (debug_level >= LOG_LVL_DEBUG) { + if (debug_level >= LOG_LVL_DEBUG) debug_level = LOG_LVL_INFO; - } bits_t bits = read_bits(target); debug_level = old_debug_level; @@ -1965,11 +1909,10 @@ static int poll_target(struct target *target, bool announce) target->state = TARGET_DEBUG_RUNNING; LOG_DEBUG("debug running"); } else if (bits.haltnot && !bits.interrupt) { - if (target->state != TARGET_HALTED) { + if (target->state != TARGET_HALTED) return handle_halt(target, announce); - } } else if (!bits.haltnot && bits.interrupt) { - // Target is halting. There is no state for that, so don't change anything. + /* Target is halting. There is no state for that, so don't change anything. */ LOG_DEBUG("halting"); } else if (!bits.haltnot && !bits.interrupt) { target->state = TARGET_RUNNING; @@ -2012,28 +1955,27 @@ static int riscv011_resume(struct target *target, int current, static int assert_reset(struct target *target) { riscv011_info_t *info = get_info(target); - // TODO: Maybe what I implemented here is more like soft_reset_halt()? + /* TODO: Maybe what I implemented here is more like soft_reset_halt()? */ jtag_add_ir_scan(target->tap, &select_dbus, TAP_IDLE); - // The only assumption we can make is that the TAP was reset. + /* The only assumption we can make is that the TAP was reset. */ if (wait_for_debugint_clear(target, true) != ERROR_OK) { LOG_ERROR("Debug interrupt didn't clear."); return ERROR_FAIL; } - // Not sure what we should do when there are multiple cores. - // Here just reset the single hart we're talking to. + /* Not sure what we should do when there are multiple cores. + * Here just reset the single hart we're talking to. */ info->dcsr |= DCSR_EBREAKM | DCSR_EBREAKH | DCSR_EBREAKS | DCSR_EBREAKU | DCSR_HALT; - if (target->reset_halt) { + if (target->reset_halt) info->dcsr |= DCSR_NDRESET; - } else { + else info->dcsr |= DCSR_FULLRESET; - } dram_write32(target, 0, lw(S0, ZERO, DEBUG_RAM_START + 16), false); dram_write32(target, 1, csrw(S0, CSR_DCSR), false); - // We shouldn't actually need the jump because a reset should happen. + /* We shouldn't actually need the jump because a reset should happen. */ dram_write_jump(target, 2, false); dram_write32(target, 4, info->dcsr, true); cache_invalidate(target); @@ -2046,11 +1988,10 @@ static int assert_reset(struct target *target) static int deassert_reset(struct target *target) { jtag_add_ir_scan(target->tap, &select_dbus, TAP_IDLE); - if (target->reset_halt) { + if (target->reset_halt) return wait_for_state(target, TARGET_HALTED); - } else { + else return wait_for_state(target, TARGET_RUNNING); - } } static int read_memory(struct target *target, target_addr_t address, @@ -2091,13 +2032,13 @@ static int read_memory(struct target *target, target_addr_t address, for (unsigned int j = 0; j < batch_size; j++) { if (i + j == count) { - // Just insert a read so we can scan out the last value. + /* Just insert a read so we can scan out the last value. */ scans_add_read32(scans, 4, false); } else if (i + j >= count + 1) { - // And check for errors. + /* And check for errors. */ scans_add_read32(scans, info->dramsize-1, false); } else { - // Write the next address and set interrupt. + /* Write the next address and set interrupt. */ uint32_t offset = size * (i + j); scans_add_write32(scans, 4, address + offset, true); } @@ -2129,9 +2070,8 @@ static int read_memory(struct target *target, target_addr_t address, } uint64_t data = scans_get_u64(scans, j, DBUS_DATA_START, DBUS_DATA_SIZE); - if (data & DMCONTROL_INTERRUPT) { + if (data & DMCONTROL_INTERRUPT) execute_busy++; - } if (i + j == count + 2) { result_value = data; } else if (i + j > 1) { @@ -2154,16 +2094,14 @@ static int read_memory(struct target *target, target_addr_t address, } LOG_DEBUG("j=%d status=%d data=%09" PRIx64, j, status, data); } - if (dbus_busy) { + if (dbus_busy) increase_dbus_busy_delay(target); - } - if (execute_busy) { + if (execute_busy) increase_interrupt_high_delay(target); - } if (dbus_busy || execute_busy) { wait_for_debugint_clear(target, false); - // Retry. + /* Retry. */ LOG_INFO("Retrying memory read starting from 0x%" TARGET_PRIxADDR " with more delays", address + size * i); } else { @@ -2225,21 +2163,19 @@ static int write_memory(struct target *target, target_addr_t address, riscv011_info_t *info = get_info(target); jtag_add_ir_scan(target->tap, &select_dbus, TAP_IDLE); - // Set up the address. + /* Set up the address. */ cache_set_store(target, 0, T0, SLOT1); cache_set_load(target, 1, T0, SLOT0); cache_set_jump(target, 2); cache_set(target, SLOT0, address); - if (cache_write(target, 5, true) != ERROR_OK) { + if (cache_write(target, 5, true) != ERROR_OK) return ERROR_FAIL; - } uint64_t t0 = cache_get(target, SLOT1); LOG_DEBUG("t0 is 0x%" PRIx64, t0); - if (setup_write_memory(target, size) != ERROR_OK) { + if (setup_write_memory(target, size) != ERROR_OK) return ERROR_FAIL; - } const unsigned max_batch_size = 256; scans_t *scans = scans_new(target, max_batch_size); @@ -2252,10 +2188,10 @@ static int write_memory(struct target *target, target_addr_t address, for (unsigned int j = 0; j < batch_size; j++) { if (i + j >= count) { - // Check for an exception. + /* Check for an exception. */ scans_add_read32(scans, info->dramsize-1, false); } else { - // Write the next value and set interrupt. + /* Write the next value and set interrupt. */ uint32_t value; uint32_t offset = size * (i + j); switch (size) { @@ -2305,37 +2241,31 @@ static int write_memory(struct target *target, target_addr_t address, return ERROR_FAIL; } int interrupt = scans_get_u32(scans, j, DBUS_DATA_START + 33, 1); - if (interrupt) { + if (interrupt) execute_busy++; - } - if (i + j == count + 1) { + if (i + j == count + 1) result_value = scans_get_u32(scans, j, DBUS_DATA_START, 32); - } } - if (dbus_busy) { + if (dbus_busy) increase_dbus_busy_delay(target); - } - if (execute_busy) { + if (execute_busy) increase_interrupt_high_delay(target); - } if (dbus_busy || execute_busy) { wait_for_debugint_clear(target, false); - // Retry. - // Set t0 back to what it should have been at the beginning of this - // batch. + /* Retry. + * Set t0 back to what it should have been at the beginning of this + * batch. */ LOG_INFO("Retrying memory write starting from 0x%" TARGET_PRIxADDR " with more delays", address + size * i); cache_clean(target); - if (write_gpr(target, T0, address + size * i) != ERROR_OK) { + if (write_gpr(target, T0, address + size * i) != ERROR_OK) goto error; - } - if (setup_write_memory(target, size) != ERROR_OK) { + if (setup_write_memory(target, size) != ERROR_OK) goto error; - } } else { i += batch_size; } @@ -2353,6 +2283,7 @@ static int write_memory(struct target *target, target_addr_t address, goto error; } + scans_delete(scans); cache_clean(target); return register_write(target, T0, t0); @@ -2367,8 +2298,7 @@ static int arch_state(struct target *target) return ERROR_OK; } -struct target_type riscv011_target = -{ +struct target_type riscv011_target = { .name = "riscv", .init_target = init_target, diff --git a/src/target/riscv/riscv-013.c b/src/target/riscv/riscv-013.c index eeaacef8e..c12406a4a 100644 --- a/src/target/riscv/riscv-013.c +++ b/src/target/riscv/riscv-013.c @@ -28,33 +28,26 @@ #include "batch.h" #define DMI_DATA1 (DMI_DATA0 + 1) +#define DMI_PROGBUF1 (DMI_PROGBUF0 + 1) -static void riscv013_on_step_or_resume(struct target *target, bool step); -static void riscv013_step_or_resume_current_hart(struct target *target, bool step); -static riscv_addr_t riscv013_progbuf_addr(struct target *target); -static riscv_addr_t riscv013_progbuf_size(struct target *target); -static riscv_addr_t riscv013_data_size(struct target *target); -static riscv_addr_t riscv013_data_addr(struct target *target); -static void riscv013_set_autoexec(struct target *target, unsigned index, - bool enabled); -static int riscv013_debug_buffer_register(struct target *target, riscv_addr_t addr); +static int riscv013_on_step_or_resume(struct target *target, bool step); +static int riscv013_step_or_resume_current_hart(struct target *target, bool step); static void riscv013_clear_abstract_error(struct target *target); /* Implementations of the functions in riscv_info_t. */ -static riscv_reg_t riscv013_get_register(struct target *target, int hartid, int regid); -static void riscv013_set_register(struct target *target, int hartid, int regid, uint64_t value); +static int riscv013_get_register(struct target *target, + riscv_reg_t *value, int hid, int rid); +static int riscv013_set_register(struct target *target, int hartid, int regid, uint64_t value); static void riscv013_select_current_hart(struct target *target); -static void riscv013_halt_current_hart(struct target *target); -static void riscv013_resume_current_hart(struct target *target); -static void riscv013_step_current_hart(struct target *target); -static void riscv013_on_halt(struct target *target); -static void riscv013_on_step(struct target *target); -static void riscv013_on_resume(struct target *target); +static int riscv013_halt_current_hart(struct target *target); +static int riscv013_resume_current_hart(struct target *target); +static int riscv013_step_current_hart(struct target *target); +static int riscv013_on_halt(struct target *target); +static int riscv013_on_step(struct target *target); +static int riscv013_on_resume(struct target *target); static bool riscv013_is_halted(struct target *target); static enum riscv_halt_reason riscv013_halt_reason(struct target *target); -static void riscv013_debug_buffer_enter(struct target *target, struct riscv_program *p); -static void riscv013_debug_buffer_leave(struct target *target, struct riscv_program *p); -static void riscv013_write_debug_buffer(struct target *target, unsigned index, +static int riscv013_write_debug_buffer(struct target *target, unsigned index, riscv_insn_t d); static riscv_insn_t riscv013_read_debug_buffer(struct target *target, unsigned index); @@ -64,6 +57,13 @@ static void riscv013_fill_dmi_read_u64(struct target *target, char *buf, int a); static int riscv013_dmi_write_u64_bits(struct target *target); static void riscv013_fill_dmi_nop_u64(struct target *target, char *buf); static int riscv013_test_compliance(struct target *target); +static int register_read_direct(struct target *target, uint64_t *value, uint32_t number); +static int register_write_direct(struct target *target, unsigned number, + uint64_t value); +static int read_memory(struct target *target, target_addr_t address, + uint32_t size, uint32_t count, uint8_t *buffer); +static int write_memory(struct target *target, target_addr_t address, + uint32_t size, uint32_t count, const uint8_t *buffer); /** * Since almost everything can be accomplish by scanning the dbus register, all @@ -130,11 +130,11 @@ struct trigger { int unique_id; }; -struct memory_cache_line { - uint32_t data; - bool valid; - bool dirty; -}; +typedef enum { + YNM_MAYBE, + YNM_YES, + YNM_NO +} yes_no_maybe_t; typedef struct { /* Number of address bits in the dbus register. */ @@ -142,50 +142,61 @@ typedef struct { /* Number of abstract command data registers. */ unsigned datacount; /* Number of words in the Program Buffer. */ - unsigned progsize; - /* The value that mstatus actually has on the target right now. This is not - * the value we present to the user. That one may be stored in the - * reg_cache. */ - uint64_t mstatus_actual; + unsigned progbufsize; - /* Single buffer that contains all register names, instead of calling - * malloc for each register. Needs to be freed when reg_list is freed. */ - char *reg_names; - /* Single buffer that contains all register values. */ - void *reg_values; + yes_no_maybe_t progbuf_writable; + /* We only need the address so that we know the alignment of the buffer. */ + riscv_addr_t progbuf_address; - // Number of run-test/idle cycles the target requests we do after each dbus - // access. + /* Number of run-test/idle cycles the target requests we do after each dbus + * access. */ unsigned int dtmcontrol_idle; - // This value is incremented every time a dbus access comes back as "busy". - // It's used to determine how many run-test/idle cycles to feed the target - // in between accesses. + /* This value is incremented every time a dbus access comes back as "busy". + * It's used to determine how many run-test/idle cycles to feed the target + * in between accesses. */ unsigned int dmi_busy_delay; - // This value is increased every time we tried to execute two commands - // consecutively, and the second one failed because the previous hadn't - // completed yet. It's used to add extra run-test/idle cycles after - // starting a command, so we don't have to waste time checking for busy to - // go low. + /* This value is increased every time we tried to execute two commands + * consecutively, and the second one failed because the previous hadn't + * completed yet. It's used to add extra run-test/idle cycles after + * starting a command, so we don't have to waste time checking for busy to + * go low. */ unsigned int ac_busy_delay; bool need_strict_step; - // Some memoized values - int progbuf_size, progbuf_addr, data_addr, data_size; - bool abstract_read_csr_supported; bool abstract_write_csr_supported; bool abstract_read_fpr_supported; bool abstract_write_fpr_supported; - // When a function returns some error due to a failure indicated by the - // target in cmderr, the caller can look here to see what that error was. - // (Compare with errno.) - unsigned cmderr; + /* When a function returns some error due to a failure indicated by the + * target in cmderr, the caller can look here to see what that error was. + * (Compare with errno.) */ + uint8_t cmderr; + + /* Some fields from hartinfo. */ + uint8_t datasize; + uint8_t dataaccess; + int16_t dataaddr; + + /* The width of the hartsel field. */ + unsigned hartsellen; } riscv013_info_t; +static riscv013_info_t *get_info(const struct target *target) +{ + riscv_info_t *info = (riscv_info_t *) target->arch_info; + return (riscv013_info_t *) info->version_specific; +} + +static uint32_t hartsel_mask(const struct target *target) +{ + RISCV013_INFO(info); + return ((1L<hartsellen)-1) << DMI_DMCONTROL_HARTSEL_OFFSET; +} + static void decode_dmi(char *text, unsigned address, unsigned data) { static const struct { @@ -197,10 +208,11 @@ static void decode_dmi(char *text, unsigned address, unsigned data) { DMI_DMCONTROL, DMI_DMCONTROL_RESUMEREQ, "resumereq" }, { DMI_DMCONTROL, DMI_DMCONTROL_HARTRESET, "hartreset" }, { DMI_DMCONTROL, DMI_DMCONTROL_HASEL, "hasel" }, - { DMI_DMCONTROL, DMI_DMCONTROL_HARTSEL, "hartsel" }, + { DMI_DMCONTROL, ((1L<<10)-1) << DMI_DMCONTROL_HARTSEL_OFFSET, "hartsel" }, { DMI_DMCONTROL, DMI_DMCONTROL_NDMRESET, "ndmreset" }, { DMI_DMCONTROL, DMI_DMCONTROL_DMACTIVE, "dmactive" }, + { DMI_DMSTATUS, DMI_DMSTATUS_IMPEBREAK, "impebreak" }, { DMI_DMSTATUS, DMI_DMSTATUS_ALLRESUMEACK, "allresumeack" }, { DMI_DMSTATUS, DMI_DMSTATUS_ANYRESUMEACK, "anyresumeack" }, { DMI_DMSTATUS, DMI_DMSTATUS_ALLNONEXISTENT, "allnonexistent" }, @@ -216,7 +228,7 @@ static void decode_dmi(char *text, unsigned address, unsigned data) { DMI_DMSTATUS, DMI_DMSTATUS_DEVTREEVALID, "devtreevalid" }, { DMI_DMSTATUS, DMI_DMSTATUS_VERSION, "version" }, - { DMI_ABSTRACTCS, DMI_ABSTRACTCS_PROGSIZE, "progsize" }, + { DMI_ABSTRACTCS, DMI_ABSTRACTCS_PROGBUFSIZE, "progbufsize" }, { DMI_ABSTRACTCS, DMI_ABSTRACTCS_BUSY, "busy" }, { DMI_ABSTRACTCS, DMI_ABSTRACTCS_CMDERR, "cmderr" }, { DMI_ABSTRACTCS, DMI_ABSTRACTCS_DATACOUNT, "datacount" }, @@ -233,7 +245,7 @@ static void decode_dmi(char *text, unsigned address, unsigned data) if (i > 0) *(text++) = ' '; if (mask & (mask >> 1)) { - // If the field is more than 1 bit wide. + /* If the field is more than 1 bit wide. */ sprintf(text, "%s=%d", description[i].name, value); } else { strcpy(text, description[i].name); @@ -246,8 +258,8 @@ static void decode_dmi(char *text, unsigned address, unsigned data) static void dump_field(const struct scan_field *field) { - static const char *op_string[] = {"-", "r", "w", "?"}; - static const char *status_string[] = {"+", "?", "F", "b"}; + static const char * const op_string[] = {"-", "r", "w", "?"}; + static const char * const status_string[] = {"+", "?", "F", "b"}; if (debug_level < LOG_LVL_DEBUG) return; @@ -279,32 +291,8 @@ static void dump_field(const struct scan_field *field) } } -static riscv013_info_t *get_info(const struct target *target) -{ - riscv_info_t *info = (riscv_info_t *) target->arch_info; - return (riscv013_info_t *) info->version_specific; -} - -/*** Necessary prototypes. ***/ - -static int register_get(struct reg *reg); - /*** Utility functions. ***/ -bool supports_extension(struct target *target, char letter) -{ - RISCV_INFO(r); - unsigned num; - if (letter >= 'a' && letter <= 'z') { - num = letter - 'a'; - } else if (letter >= 'A' && letter <= 'Z') { - num = letter - 'A'; - } else { - return false; - } - return r->misa & (1 << num); -} - static void select_dmi(struct target *target) { static uint8_t ir_dmi[1] = {DTM_DMI}; @@ -353,7 +341,7 @@ static void increase_dmi_busy_delay(struct target *target) { riscv013_info_t *info = get_info(target); info->dmi_busy_delay += info->dmi_busy_delay / 10 + 1; - LOG_INFO("dtmcontrol_idle=%d, dmi_busy_delay=%d, ac_busy_delay=%d", + LOG_DEBUG("dtmcontrol_idle=%d, dmi_busy_delay=%d, ac_busy_delay=%d", info->dtmcontrol_idle, info->dmi_busy_delay, info->ac_busy_delay); @@ -365,7 +353,7 @@ static void increase_dmi_busy_delay(struct target *target) * run-test/idle cycles may be required. */ static dmi_status_t dmi_scan(struct target *target, uint16_t *address_in, - uint64_t *data_in, dmi_op_t op, uint16_t address_out, uint64_t data_out, + uint32_t *data_in, dmi_op_t op, uint16_t address_out, uint32_t data_out, bool exec) { riscv013_info_t *info = get_info(target); @@ -379,9 +367,9 @@ static dmi_status_t dmi_scan(struct target *target, uint16_t *address_in, assert(info->abits != 0); - buf_set_u64(out, DTM_DMI_OP_OFFSET, DTM_DMI_OP_LENGTH, op); - buf_set_u64(out, DTM_DMI_DATA_OFFSET, DTM_DMI_DATA_LENGTH, data_out); - buf_set_u64(out, DTM_DMI_ADDRESS_OFFSET, info->abits, address_out); + buf_set_u32(out, DTM_DMI_OP_OFFSET, DTM_DMI_OP_LENGTH, op); + buf_set_u32(out, DTM_DMI_DATA_OFFSET, DTM_DMI_DATA_LENGTH, data_out); + buf_set_u32(out, DTM_DMI_ADDRESS_OFFSET, info->abits, address_out); /* Assume dbus is already selected. */ jtag_add_dr_scan(target->tap, 1, &field, TAP_IDLE); @@ -390,9 +378,8 @@ static dmi_status_t dmi_scan(struct target *target, uint16_t *address_in, if (exec) idle_count += info->ac_busy_delay; - if (idle_count) { + if (idle_count) jtag_add_runtest(idle_count, TAP_IDLE); - } int retval = jtag_execute_queue(); if (retval != ERROR_OK) { @@ -400,20 +387,18 @@ static dmi_status_t dmi_scan(struct target *target, uint16_t *address_in, return DMI_STATUS_FAILED; } - if (data_in) { - *data_in = buf_get_u64(in, DTM_DMI_DATA_OFFSET, DTM_DMI_DATA_LENGTH); - } + if (data_in) + *data_in = buf_get_u32(in, DTM_DMI_DATA_OFFSET, DTM_DMI_DATA_LENGTH); - if (address_in) { + if (address_in) *address_in = buf_get_u32(in, DTM_DMI_ADDRESS_OFFSET, info->abits); - } dump_field(&field); return buf_get_u32(in, DTM_DMI_OP_OFFSET, DTM_DMI_OP_LENGTH); } -static uint64_t dmi_read(struct target *target, uint16_t address) +static uint32_t dmi_read(struct target *target, uint16_t address) { select_dmi(target); @@ -422,9 +407,9 @@ static uint64_t dmi_read(struct target *target, uint16_t address) unsigned i = 0; - // This first loop ensures that the read request was actually sent - // to the target. Note that if for some reason this stays busy, - // it is actually due to the previous dmi_read or dmi_write. + /* This first loop ensures that the read request was actually sent + * to the target. Note that if for some reason this stays busy, + * it is actually due to the previous dmi_read or dmi_write. */ for (i = 0; i < 256; i++) { status = dmi_scan(target, NULL, NULL, DMI_OP_READ, address, 0, false); @@ -440,13 +425,13 @@ static uint64_t dmi_read(struct target *target, uint16_t address) if (status != DMI_STATUS_SUCCESS) { LOG_ERROR("Failed read from 0x%x; status=%d", address, status); - abort(); + return ~0; } - // This second loop ensures that we got the read - // data back. Note that NOP can result in a 'busy' result as well, but - // that would be noticed on the next DMI access we do. - uint64_t value; + /* This second loop ensures that we got the read + * data back. Note that NOP can result in a 'busy' result as well, but + * that would be noticed on the next DMI access we do. */ + uint32_t value; for (i = 0; i < 256; i++) { status = dmi_scan(target, &address_in, &value, DMI_OP_NOP, address, 0, false); @@ -461,21 +446,25 @@ static uint64_t dmi_read(struct target *target, uint16_t address) } if (status != DMI_STATUS_SUCCESS) { - LOG_ERROR("Failed read (NOP) from 0x%x; value=0x%" PRIx64 ", status=%d", - address, value, status); - abort(); + if (status == DMI_STATUS_FAILED) { + LOG_ERROR("Failed read (NOP) from 0x%x; status=%d", address, status); + } else { + LOG_ERROR("Failed read (NOP) from 0x%x; value=0x%x, status=%d", + address, value, status); + } + return ~0; } return value; } -static void dmi_write(struct target *target, uint16_t address, uint64_t value) +static int dmi_write(struct target *target, uint16_t address, uint64_t value) { select_dmi(target); dmi_status_t status = DMI_STATUS_BUSY; unsigned i = 0; - // The first loop ensures that we successfully sent the write request. + /* The first loop ensures that we successfully sent the write request. */ for (i = 0; i < 256; i++) { status = dmi_scan(target, NULL, NULL, DMI_OP_WRITE, address, value, address == DMI_COMMAND); @@ -492,11 +481,12 @@ static void dmi_write(struct target *target, uint16_t address, uint64_t value) if (status != DMI_STATUS_SUCCESS) { LOG_ERROR("Failed write to 0x%x;, status=%d", address, status); - abort(); + return ERROR_FAIL; } - // The second loop isn't strictly necessary, but would ensure that - // the write is complete/ has no non-busy errors before returning from this function. + /* The second loop isn't strictly necessary, but would ensure that the + * write is complete/ has no non-busy errors before returning from this + * function. */ for (i = 0; i < 256; i++) { status = dmi_scan(target, NULL, NULL, DMI_OP_NOP, address, 0, false); @@ -511,15 +501,17 @@ static void dmi_write(struct target *target, uint16_t address, uint64_t value) } if (status != DMI_STATUS_SUCCESS) { LOG_ERROR("failed to write (NOP) 0x%" PRIx64 " to 0x%x; status=%d", value, address, status); - abort(); + return ERROR_FAIL; } + + return ERROR_OK; } static void increase_ac_busy_delay(struct target *target) { riscv013_info_t *info = get_info(target); info->ac_busy_delay += info->ac_busy_delay / 10 + 1; - LOG_INFO("dtmcontrol_idle=%d, dmi_busy_delay=%d, ac_busy_delay=%d", + LOG_DEBUG("dtmcontrol_idle=%d, dmi_busy_delay=%d, ac_busy_delay=%d", info->dtmcontrol_idle, info->dmi_busy_delay, info->ac_busy_delay); } @@ -548,9 +540,8 @@ static int wait_for_idle(struct target *target, uint32_t *abstractcs) while (1) { *abstractcs = dmi_read(target, DMI_ABSTRACTCS); - if (get_field(*abstractcs, DMI_ABSTRACTCS_BUSY) == 0) { + if (get_field(*abstractcs, DMI_ABSTRACTCS_BUSY) == 0) return ERROR_OK; - } if (time(NULL) - start > riscv_command_timeout_sec) { info->cmderr = get_field(*abstractcs, DMI_ABSTRACTCS_CMDERR); @@ -570,9 +561,9 @@ static int wait_for_idle(struct target *target, uint32_t *abstractcs) } LOG_ERROR("Timed out after %ds waiting for busy to go low (abstractcs=0x%x). " - "Increase the timeout with riscv set_command_timeout_sec.", - riscv_command_timeout_sec, - *abstractcs); + "Increase the timeout with riscv set_command_timeout_sec.", + riscv_command_timeout_sec, + *abstractcs); return ERROR_FAIL; } } @@ -585,15 +576,15 @@ static int execute_abstract_command(struct target *target, uint32_t command) dmi_write(target, DMI_COMMAND, command); { - uint32_t dmstatus = 0; - wait_for_idle(target, &dmstatus); + uint32_t abstractcs = 0; + wait_for_idle(target, &abstractcs); } uint32_t cs = dmi_read(target, DMI_ABSTRACTCS); info->cmderr = get_field(cs, DMI_ABSTRACTCS_CMDERR); if (info->cmderr != 0) { LOG_DEBUG("command 0x%x failed; abstractcs=0x%x", command, cs); - // Clear the error. + /* Clear the error. */ dmi_write(target, DMI_ABSTRACTCS, set_field(0, DMI_ABSTRACTCS_CMDERR, info->cmderr)); return ERROR_FAIL; @@ -636,11 +627,12 @@ static int write_abstract_arg(struct target *target, unsigned index, return ERROR_OK; } -static int register_read_abstract(struct target *target, uint64_t *value, - uint32_t number, unsigned size) +/** + * @size in bits + */ +static uint32_t access_register_command(uint32_t number, unsigned size, + uint32_t flags) { - RISCV013_INFO(r); - uint32_t command = set_field(0, DMI_COMMAND_CMDTYPE, 0); switch (size) { case 32: @@ -650,45 +642,58 @@ static int register_read_abstract(struct target *target, uint64_t *value, command = set_field(command, AC_ACCESS_REGISTER_SIZE, 3); break; default: - LOG_ERROR("Unsupported abstract register read size: %d", size); - return ERROR_FAIL; + assert(0); } - command = set_field(command, AC_ACCESS_REGISTER_POSTEXEC, 0); - command = set_field(command, AC_ACCESS_REGISTER_TRANSFER, 1); - command = set_field(command, AC_ACCESS_REGISTER_WRITE, 0); if (number <= GDB_REGNO_XPR31) { command = set_field(command, AC_ACCESS_REGISTER_REGNO, - 0x1000 + number - GDB_REGNO_XPR0); + 0x1000 + number - GDB_REGNO_ZERO); } else if (number >= GDB_REGNO_FPR0 && number <= GDB_REGNO_FPR31) { - if (!r->abstract_read_fpr_supported) - return ERROR_FAIL; command = set_field(command, AC_ACCESS_REGISTER_REGNO, 0x1020 + number - GDB_REGNO_FPR0); } else if (number >= GDB_REGNO_CSR0 && number <= GDB_REGNO_CSR4095) { - if (!r->abstract_read_csr_supported) - return ERROR_FAIL; command = set_field(command, AC_ACCESS_REGISTER_REGNO, number - GDB_REGNO_CSR0); } else { - return ERROR_FAIL; + assert(0); } + command |= flags; + + return command; +} + +static int register_read_abstract(struct target *target, uint64_t *value, + uint32_t number, unsigned size) +{ + RISCV013_INFO(info); + + if (number >= GDB_REGNO_FPR0 && number <= GDB_REGNO_FPR31 && + !info->abstract_read_fpr_supported) + return ERROR_FAIL; + if (number >= GDB_REGNO_CSR0 && number <= GDB_REGNO_CSR4095 && + !info->abstract_read_csr_supported) + return ERROR_FAIL; + + uint32_t command = access_register_command(number, size, + AC_ACCESS_REGISTER_TRANSFER); + int result = execute_abstract_command(target, command); if (result != ERROR_OK) { - if (r->cmderr == CMDERR_NOT_SUPPORTED) { + if (info->cmderr == CMDERR_NOT_SUPPORTED) { if (number >= GDB_REGNO_FPR0 && number <= GDB_REGNO_FPR31) { - r->abstract_read_fpr_supported = false; + info->abstract_read_fpr_supported = false; LOG_INFO("Disabling abstract command reads from FPRs."); } else if (number >= GDB_REGNO_CSR0 && number <= GDB_REGNO_CSR4095) { - r->abstract_read_csr_supported = false; + info->abstract_read_csr_supported = false; LOG_INFO("Disabling abstract command reads from CSRs."); } } return result; } - *value = read_abstract_arg(target, 0); + if (value) + *value = read_abstract_arg(target, 0); return ERROR_OK; } @@ -698,42 +703,19 @@ static int register_write_abstract(struct target *target, uint32_t number, { RISCV013_INFO(info); - uint32_t command = set_field(0, DMI_COMMAND_CMDTYPE, 0); - switch (size) { - case 32: - command = set_field(command, AC_ACCESS_REGISTER_SIZE, 2); - break; - case 64: - command = set_field(command, AC_ACCESS_REGISTER_SIZE, 3); - break; - default: - LOG_ERROR("Unsupported abstract register read size: %d", size); - return ERROR_FAIL; - } - command = set_field(command, AC_ACCESS_REGISTER_POSTEXEC, 0); - command = set_field(command, AC_ACCESS_REGISTER_TRANSFER, 1); - command = set_field(command, AC_ACCESS_REGISTER_WRITE, 1); - - if (number <= GDB_REGNO_XPR31) { - command = set_field(command, AC_ACCESS_REGISTER_REGNO, - 0x1000 + number - GDB_REGNO_XPR0); - } else if (number >= GDB_REGNO_FPR0 && number <= GDB_REGNO_FPR31) { - if (!info->abstract_read_fpr_supported) - return ERROR_FAIL; - command = set_field(command, AC_ACCESS_REGISTER_REGNO, - 0x1020 + number - GDB_REGNO_FPR0); - } else if (number >= GDB_REGNO_CSR0 && number <= GDB_REGNO_CSR4095) { - if (!info->abstract_read_csr_supported) - return ERROR_FAIL; - command = set_field(command, AC_ACCESS_REGISTER_REGNO, - number - GDB_REGNO_CSR0); - } else { + if (number >= GDB_REGNO_FPR0 && number <= GDB_REGNO_FPR31 && + !info->abstract_write_fpr_supported) return ERROR_FAIL; - } - - if (write_abstract_arg(target, 0, value) != ERROR_OK) { + if (number >= GDB_REGNO_CSR0 && number <= GDB_REGNO_CSR4095 && + !info->abstract_write_csr_supported) + return ERROR_FAIL; + + uint32_t command = access_register_command(number, size, + AC_ACCESS_REGISTER_TRANSFER | + AC_ACCESS_REGISTER_WRITE); + + if (write_abstract_arg(target, 0, value) != ERROR_OK) return ERROR_FAIL; - } int result = execute_abstract_command(target, command); if (result != ERROR_OK) { @@ -752,6 +734,200 @@ static int register_write_abstract(struct target *target, uint32_t number, return ERROR_OK; } +static int examine_progbuf(struct target *target) +{ + riscv013_info_t *info = get_info(target); + + if (info->progbuf_writable != YNM_MAYBE) + return ERROR_OK; + + /* Figure out if progbuf is writable. */ + + if (info->progbufsize < 1) { + info->progbuf_writable = YNM_NO; + LOG_INFO("No program buffer present."); + return ERROR_OK; + } + + uint64_t s0; + if (register_read_direct(target, &s0, GDB_REGNO_S0) != ERROR_OK) + return ERROR_FAIL; + + struct riscv_program program; + riscv_program_init(&program, target); + riscv_program_insert(&program, auipc(S0)); + if (riscv_program_exec(&program, target) != ERROR_OK) + return ERROR_FAIL; + + if (register_read_direct(target, &info->progbuf_address, GDB_REGNO_S0) != ERROR_OK) + return ERROR_FAIL; + + riscv_program_init(&program, target); + riscv_program_insert(&program, sw(S0, S0, 0)); + int result = riscv_program_exec(&program, target); + + if (register_write_direct(target, GDB_REGNO_S0, s0) != ERROR_OK) + return ERROR_FAIL; + + if (result != ERROR_OK) { + /* This program might have failed if the program buffer is not + * writable. */ + info->progbuf_writable = YNM_NO; + return ERROR_OK; + } + + uint32_t written = dmi_read(target, DMI_PROGBUF0); + if (written == (uint32_t) info->progbuf_address) { + LOG_INFO("progbuf is writable at 0x%" TARGET_PRIxADDR, + info->progbuf_address); + info->progbuf_writable = YNM_YES; + + } else { + LOG_INFO("progbuf is not writeable at 0x%" TARGET_PRIxADDR, + info->progbuf_address); + info->progbuf_writable = YNM_NO; + } + + return ERROR_OK; +} + +typedef enum { + SPACE_DMI_DATA, + SPACE_DMI_PROGBUF, + SPACE_DMI_RAM +} memory_space_t; + +typedef struct { + /* How can the debugger access this memory? */ + memory_space_t memory_space; + /* Memory address to access the scratch memory from the hart. */ + riscv_addr_t hart_address; + /* Memory address to access the scratch memory from the debugger. */ + riscv_addr_t debug_address; +} scratch_mem_t; + +/** + * Find some scratch memory to be used with the given program. + */ +static int scratch_find(struct target *target, + scratch_mem_t *scratch, + struct riscv_program *program, + unsigned size_bytes) +{ + riscv013_info_t *info = get_info(target); + + riscv_addr_t alignment = 1; + while (alignment < size_bytes) + alignment *= 2; + + if (info->dataaccess == 1) { + /* Sign extend dataaddr. */ + scratch->hart_address = info->dataaddr; + if (info->dataaddr & (1<<11)) + scratch->hart_address |= 0xfffffffffffff000ULL; + /* Align. */ + scratch->hart_address = (scratch->hart_address + alignment - 1) & ~(alignment - 1); + + if ((size_bytes + scratch->hart_address - info->dataaddr + 3) / 4 >= + info->datasize) { + scratch->memory_space = SPACE_DMI_DATA; + scratch->debug_address = (scratch->hart_address - info->dataaddr) / 4; + return ERROR_OK; + } + } + + if (examine_progbuf(target) != ERROR_OK) + return ERROR_FAIL; + + /* Allow for ebreak at the end of the program. */ + unsigned program_size = (program->instruction_count + 1) * 4; + scratch->hart_address = (info->progbuf_address + program_size + alignment - 1) & + ~(alignment - 1); + if ((size_bytes + scratch->hart_address - info->progbuf_address + 3) / 4 >= + info->progbufsize) { + scratch->memory_space = SPACE_DMI_PROGBUF; + scratch->debug_address = (scratch->hart_address - info->progbuf_address) / 4; + return ERROR_OK; + } + + if (riscv_use_scratch_ram) { + scratch->hart_address = (riscv_scratch_ram_address + alignment - 1) & + ~(alignment - 1); + scratch->memory_space = SPACE_DMI_RAM; + scratch->debug_address = scratch->hart_address; + return ERROR_OK; + } + + LOG_ERROR("Couldn't find %d bytes of scratch RAM to use. Please configure " + "an address with 'riscv set_scratch_ram'.", size_bytes); + return ERROR_FAIL; +} + +static int scratch_read64(struct target *target, scratch_mem_t *scratch, + uint64_t *value) +{ + switch (scratch->memory_space) { + case SPACE_DMI_DATA: + *value = dmi_read(target, DMI_DATA0 + scratch->debug_address); + *value |= ((uint64_t) dmi_read(target, DMI_DATA1 + + scratch->debug_address)) << 32; + break; + case SPACE_DMI_PROGBUF: + *value = dmi_read(target, DMI_PROGBUF0 + scratch->debug_address); + *value |= ((uint64_t) dmi_read(target, DMI_PROGBUF1 + + scratch->debug_address)) << 32; + break; + case SPACE_DMI_RAM: + { + uint8_t buffer[8]; + if (read_memory(target, scratch->debug_address, 4, 2, buffer) != ERROR_OK) + return ERROR_FAIL; + *value = buffer[0] | + (((uint64_t) buffer[1]) << 8) | + (((uint64_t) buffer[2]) << 16) | + (((uint64_t) buffer[3]) << 24) | + (((uint64_t) buffer[4]) << 32) | + (((uint64_t) buffer[5]) << 40) | + (((uint64_t) buffer[6]) << 48) | + (((uint64_t) buffer[7]) << 56); + } + break; + } + return ERROR_OK; +} + +static int scratch_write64(struct target *target, scratch_mem_t *scratch, + uint64_t value) +{ + switch (scratch->memory_space) { + case SPACE_DMI_DATA: + dmi_write(target, DMI_DATA0 + scratch->debug_address, value); + dmi_write(target, DMI_DATA1 + scratch->debug_address, value >> 32); + break; + case SPACE_DMI_PROGBUF: + dmi_write(target, DMI_PROGBUF0 + scratch->debug_address, value); + dmi_write(target, DMI_PROGBUF1 + scratch->debug_address, value >> 32); + break; + case SPACE_DMI_RAM: + { + uint8_t buffer[8] = { + value, + value >> 8, + value >> 16, + value >> 24, + value >> 32, + value >> 40, + value >> 48, + value >> 56 + }; + if (write_memory(target, scratch->debug_address, 4, 2, buffer) != ERROR_OK) + return ERROR_FAIL; + } + break; + } + return ERROR_OK; +} + static int register_write_direct(struct target *target, unsigned number, uint64_t value) { @@ -764,34 +940,54 @@ static int register_write_direct(struct target *target, unsigned number, return ERROR_OK; struct riscv_program program; - riscv_program_init(&program, target); - riscv_addr_t input = riscv_program_alloc_d(&program); - riscv_program_write_ram(&program, input + 4, value >> 32); - riscv_program_write_ram(&program, input, value); + uint64_t s0; + if (register_read_direct(target, &s0, GDB_REGNO_S0) != ERROR_OK) + return ERROR_FAIL; + + if (number >= GDB_REGNO_FPR0 && number <= GDB_REGNO_FPR31 && + riscv_supports_extension(target, 'D') && + riscv_xlen(target) < 64) { + /* There are no instructions to move all the bits from a register, so + * we need to use some scratch RAM. */ + riscv_program_insert(&program, fld(number - GDB_REGNO_FPR0, S0, 0)); + + scratch_mem_t scratch; + if (scratch_find(target, &scratch, &program, 8) != ERROR_OK) + return ERROR_FAIL; + + if (register_write_direct(target, GDB_REGNO_S0, scratch.hart_address) + != ERROR_OK) + return ERROR_FAIL; + + if (scratch_write64(target, &scratch, value) != ERROR_OK) + return ERROR_FAIL; - assert(GDB_REGNO_XPR0 == 0); - if (number <= GDB_REGNO_XPR31) { - riscv_program_lx(&program, number, input); - } else if (number >= GDB_REGNO_FPR0 && number <= GDB_REGNO_FPR31) { - riscv_program_flx(&program, number, input); - } else if (number >= GDB_REGNO_CSR0 && number <= GDB_REGNO_CSR4095) { - enum gdb_regno temp = riscv_program_gettemp(&program); - riscv_program_lx(&program, temp, input); - riscv_program_csrw(&program, temp, number); } else { - LOG_ERROR("Unsupported register (enum gdb_regno)(%d)", number); - abort(); + if (register_write_direct(target, GDB_REGNO_S0, value) != ERROR_OK) + return ERROR_FAIL; + + if (number >= GDB_REGNO_FPR0 && number <= GDB_REGNO_FPR31) { + if (riscv_supports_extension(target, 'D')) + riscv_program_insert(&program, fmv_d_x(number - GDB_REGNO_FPR0, S0)); + else + riscv_program_insert(&program, fmv_w_x(number - GDB_REGNO_FPR0, S0)); + } else if (number >= GDB_REGNO_CSR0 && number <= GDB_REGNO_CSR4095) { + riscv_program_csrw(&program, S0, number); + } else { + LOG_ERROR("Unsupported register (enum gdb_regno)(%d)", number); + return ERROR_FAIL; + } } int exec_out = riscv_program_exec(&program, target); - if (exec_out != ERROR_OK) { - riscv013_clear_abstract_error(target); - return ERROR_FAIL; - } - return ERROR_OK; + /* Restore S0. */ + if (register_write_direct(target, GDB_REGNO_S0, s0) != ERROR_OK) + return ERROR_FAIL; + + return exec_out; } /** Actually read registers from the target right now. */ @@ -801,79 +997,86 @@ static int register_read_direct(struct target *target, uint64_t *value, uint32_t riscv_xlen(target)); if (result != ERROR_OK) { + assert(number != GDB_REGNO_S0); + struct riscv_program program; riscv_program_init(&program, target); - riscv_addr_t output = riscv_program_alloc_d(&program); - riscv_program_write_ram(&program, output + 4, 0); - riscv_program_write_ram(&program, output, 0); - assert(GDB_REGNO_XPR0 == 0); - if (number <= GDB_REGNO_XPR31) { - riscv_program_sx(&program, number, output); - } else if (number >= GDB_REGNO_FPR0 && number <= GDB_REGNO_FPR31) { - riscv_program_fsx(&program, number, output); + scratch_mem_t scratch; + bool use_scratch = false; + + uint64_t s0; + if (register_read_direct(target, &s0, GDB_REGNO_S0) != ERROR_OK) + return ERROR_FAIL; + + /* Write program to move data into s0. */ + + uint64_t mstatus; + if (number >= GDB_REGNO_FPR0 && number <= GDB_REGNO_FPR31) { + if (register_read_direct(target, &mstatus, GDB_REGNO_MSTATUS) != ERROR_OK) + return ERROR_FAIL; + if ((mstatus & MSTATUS_FS) == 0) + if (register_write_direct(target, GDB_REGNO_MSTATUS, + set_field(mstatus, MSTATUS_FS, 1)) != ERROR_OK) + return ERROR_FAIL; + + if (riscv_supports_extension(target, 'D') && riscv_xlen(target) < 64) { + /* There are no instructions to move all the bits from a + * register, so we need to use some scratch RAM. */ + riscv_program_insert(&program, fsd(number - GDB_REGNO_FPR0, S0, + 0)); + + if (scratch_find(target, &scratch, &program, 8) != ERROR_OK) + return ERROR_FAIL; + use_scratch = true; + + if (register_write_direct(target, GDB_REGNO_S0, + scratch.hart_address) != ERROR_OK) + return ERROR_FAIL; + } else if (riscv_supports_extension(target, 'D')) { + riscv_program_insert(&program, fmv_x_d(S0, number - GDB_REGNO_FPR0)); + } else { + riscv_program_insert(&program, fmv_x_w(S0, number - GDB_REGNO_FPR0)); + } } else if (number >= GDB_REGNO_CSR0 && number <= GDB_REGNO_CSR4095) { - LOG_DEBUG("reading CSR index=0x%03x", number - GDB_REGNO_CSR0); - enum gdb_regno temp = riscv_program_gettemp(&program); - riscv_program_csrr(&program, temp, number); - riscv_program_sx(&program, temp, output); + riscv_program_csrr(&program, S0, number); } else { LOG_ERROR("Unsupported register (enum gdb_regno)(%d)", number); - abort(); - } - - int exec_out = riscv_program_exec(&program, target); - if (exec_out != ERROR_OK) { - riscv013_clear_abstract_error(target); return ERROR_FAIL; } - *value = 0; - *value |= ((uint64_t)(riscv_program_read_ram(&program, output + 4))) << 32; - *value |= riscv_program_read_ram(&program, output); + /* Execute program. */ + result = riscv_program_exec(&program, target); + + if (use_scratch) { + if (scratch_read64(target, &scratch, value) != ERROR_OK) + return ERROR_FAIL; + } else { + /* Read S0 */ + if (register_read_direct(target, value, GDB_REGNO_S0) != ERROR_OK) + return ERROR_FAIL; + } + + if (number >= GDB_REGNO_FPR0 && number <= GDB_REGNO_FPR31 && + (mstatus & MSTATUS_FS) == 0) + if (register_write_direct(target, GDB_REGNO_MSTATUS, mstatus) != ERROR_OK) + return ERROR_FAIL; + + /* Restore S0. */ + if (register_write_direct(target, GDB_REGNO_S0, s0) != ERROR_OK) + return ERROR_FAIL; } - LOG_DEBUG("[%d] reg[0x%x] = 0x%" PRIx64, riscv_current_hartid(target), - number, *value); - return ERROR_OK; + if (result == ERROR_OK) { + LOG_DEBUG("[%d] reg[0x%x] = 0x%" PRIx64, riscv_current_hartid(target), + number, *value); + } + + return result; } /*** OpenOCD target functions. ***/ -static int register_get(struct reg *reg) -{ - struct target *target = (struct target *) reg->arch_info; - uint64_t value = riscv_get_register(target, reg->number); - buf_set_u64(reg->value, 0, 64, value); - return ERROR_OK; -} - -static int register_write(struct target *target, unsigned int number, - uint64_t value) -{ - riscv_set_register(target, number, value); - return ERROR_OK; -} - -static int register_set(struct reg *reg, uint8_t *buf) -{ - struct target *target = (struct target *) reg->arch_info; - - uint64_t value = buf_get_u64(buf, 0, riscv_xlen(target)); - - LOG_DEBUG("write 0x%" PRIx64 " to %s", value, reg->name); - struct reg *r = &target->reg_cache->reg_list[reg->number]; - r->valid = true; - memcpy(r->value, buf, (r->size + 7) / 8); - - return register_write(target, reg->number, value); -} - -static struct reg_arch_type riscv_reg_arch_type = { - .get = register_get, - .set = register_set -}; - static int init_target(struct command_context *cmd_ctx, struct target *target) { @@ -891,8 +1094,6 @@ static int init_target(struct command_context *cmd_ctx, generic_info->on_resume = &riscv013_on_resume; generic_info->on_step = &riscv013_on_step; generic_info->halt_reason = &riscv013_halt_reason; - generic_info->debug_buffer_enter = &riscv013_debug_buffer_enter; - generic_info->debug_buffer_leave = &riscv013_debug_buffer_leave; generic_info->read_debug_buffer = &riscv013_read_debug_buffer; generic_info->write_debug_buffer = &riscv013_write_debug_buffer; generic_info->execute_debug_buffer = &riscv013_execute_debug_buffer; @@ -906,62 +1107,21 @@ static int init_target(struct command_context *cmd_ctx, return ERROR_FAIL; riscv013_info_t *info = get_info(target); - info->progbuf_size = -1; - info->progbuf_addr = -1; - info->data_size = -1; - info->data_addr = -1; + info->progbufsize = -1; info->dmi_busy_delay = 0; info->ac_busy_delay = 0; - // Assume all these abstract commands are supported until we learn - // otherwise. - // TODO: The spec allows eg. one CSR to be able to be accessed abstractly - // while another one isn't. We don't track that this closely here, but in - // the future we probably should. + /* Assume all these abstract commands are supported until we learn + * otherwise. + * TODO: The spec allows eg. one CSR to be able to be accessed abstractly + * while another one isn't. We don't track that this closely here, but in + * the future we probably should. */ info->abstract_read_csr_supported = true; info->abstract_write_csr_supported = true; info->abstract_read_fpr_supported = true; info->abstract_write_fpr_supported = true; - target->reg_cache = calloc(1, sizeof(*target->reg_cache)); - target->reg_cache->name = "RISC-V Registers"; - target->reg_cache->num_regs = GDB_REGNO_COUNT; - - target->reg_cache->reg_list = calloc(GDB_REGNO_COUNT, sizeof(struct reg)); - - const unsigned int max_reg_name_len = 12; - info->reg_names = calloc(1, GDB_REGNO_COUNT * max_reg_name_len); - char *reg_name = info->reg_names; - info->reg_values = NULL; - - for (unsigned int i = 0; i < GDB_REGNO_COUNT; i++) { - struct reg *r = &target->reg_cache->reg_list[i]; - r->number = i; - r->caller_save = true; - r->dirty = false; - r->valid = false; - r->exist = true; - r->type = &riscv_reg_arch_type; - r->arch_info = target; - if (i <= GDB_REGNO_XPR31) { - sprintf(reg_name, "x%d", i); - } else if (i == GDB_REGNO_PC) { - sprintf(reg_name, "pc"); - } else if (i >= GDB_REGNO_FPR0 && i <= GDB_REGNO_FPR31) { - sprintf(reg_name, "f%d", i - GDB_REGNO_FPR0); - } else if (i >= GDB_REGNO_CSR0 && i <= GDB_REGNO_CSR4095) { - sprintf(reg_name, "csr%d", i - GDB_REGNO_CSR0); - } else if (i == GDB_REGNO_PRIV) { - sprintf(reg_name, "priv"); - } - if (reg_name[0]) { - r->name = reg_name; - } - reg_name += strlen(reg_name) + 1; - assert(reg_name < info->reg_names + GDB_REGNO_COUNT * max_reg_name_len); - } - return ERROR_OK; } @@ -975,7 +1135,7 @@ static void deinit_target(struct target *target) static int examine(struct target *target) { - // Don't need to select dbus, since the first thing we do is read dtmcontrol. + /* Don't need to select dbus, since the first thing we do is read dtmcontrol. */ uint32_t dtmcontrol = dtmcontrol_scan(target, 0); LOG_DEBUG("dtmcontrol=0x%x", dtmcontrol); @@ -998,7 +1158,6 @@ static int examine(struct target *target) info->abits = get_field(dtmcontrol, DTM_DTMCS_ABITS); info->dtmcontrol_idle = get_field(dtmcontrol, DTM_DTMCS_IDLE); - uint32_t dmcontrol = dmi_read(target, DMI_DMCONTROL); uint32_t dmstatus = dmi_read(target, DMI_DMSTATUS); if (get_field(dmstatus, DMI_DMSTATUS_VERSION) != 2) { LOG_ERROR("OpenOCD only supports Debug Module version 2, not %d " @@ -1006,13 +1165,11 @@ static int examine(struct target *target) return ERROR_FAIL; } - // Reset the Debug Module. - dmi_write(target, DMI_DMCONTROL, 0); - dmi_write(target, DMI_DMCONTROL, DMI_DMCONTROL_DMACTIVE); - dmcontrol = dmi_read(target, DMI_DMCONTROL); - - LOG_DEBUG("dmcontrol: 0x%08x", dmcontrol); - LOG_DEBUG("dmstatus: 0x%08x", dmstatus); + /* Reset the Debug Module. */ + uint32_t max_hartsel_mask = ((1L<<10)-1) << DMI_DMCONTROL_HARTSEL_OFFSET; + dmi_write(target, DMI_DMCONTROL, max_hartsel_mask); + dmi_write(target, DMI_DMCONTROL, max_hartsel_mask | DMI_DMCONTROL_DMACTIVE); + uint32_t dmcontrol = dmi_read(target, DMI_DMCONTROL); if (!get_field(dmcontrol, DMI_DMCONTROL_DMACTIVE)) { LOG_ERROR("Debug Module did not become active. dmcontrol=0x%x", @@ -1020,159 +1177,110 @@ static int examine(struct target *target) return ERROR_FAIL; } + uint32_t hartsel = get_field(dmcontrol, max_hartsel_mask); + info->hartsellen = 0; + while (hartsel & 1) { + info->hartsellen++; + hartsel >>= 1; + } + LOG_DEBUG("hartsellen=%d", info->hartsellen); + + uint32_t hartinfo = dmi_read(target, DMI_HARTINFO); + + info->datasize = get_field(hartinfo, DMI_HARTINFO_DATASIZE); + info->dataaccess = get_field(hartinfo, DMI_HARTINFO_DATAACCESS); + info->dataaddr = get_field(hartinfo, DMI_HARTINFO_DATAADDR); + if (!get_field(dmstatus, DMI_DMSTATUS_AUTHENTICATED)) { LOG_ERROR("Authentication required by RISC-V core but not " "supported by OpenOCD. dmcontrol=0x%x", dmcontrol); return ERROR_FAIL; } - if (get_field(dmstatus, DMI_DMSTATUS_ANYUNAVAIL)) { - LOG_ERROR("The hart is unavailable."); - return ERROR_FAIL; - } - - if (get_field(dmstatus, DMI_DMSTATUS_ANYNONEXISTENT)) { - LOG_ERROR("The hart doesn't exist."); - return ERROR_FAIL; - } - - // Check that abstract data registers are accessible. + /* Check that abstract data registers are accessible. */ uint32_t abstractcs = dmi_read(target, DMI_ABSTRACTCS); info->datacount = get_field(abstractcs, DMI_ABSTRACTCS_DATACOUNT); - info->progsize = get_field(abstractcs, DMI_ABSTRACTCS_PROGSIZE); + info->progbufsize = get_field(abstractcs, DMI_ABSTRACTCS_PROGBUFSIZE); + + RISCV_INFO(r); + r->impebreak = get_field(dmstatus, DMI_DMSTATUS_IMPEBREAK); /* Before doing anything else we must first enumerate the harts. */ - RISCV_INFO(r); - int original_coreid = target->coreid; - for (int i = 0; i < RISCV_MAX_HARTS; ++i) { - /* Fake being a non-RTOS targeted to this core so we can see if - * it exists. This avoids the assertion in - * riscv_set_current_hartid() that ensures non-RTOS targets - * don't touch the harts they're not assigned to. */ - target->coreid = i; - r->hart_count = i + 1; - riscv_set_current_hartid(target, i); - uint32_t s = dmi_read(target, DMI_DMSTATUS); - if (get_field(s, DMI_DMSTATUS_ANYNONEXISTENT)) { - r->hart_count--; - break; - } - } - target->coreid = original_coreid; - - LOG_DEBUG("Enumerated %d harts", r->hart_count); - - /* Halt every hart so we can probe them. */ - riscv_halt_all_harts(target); - - /* Find the address of the program buffer, which must be done without - * knowing anything about the target. */ - for (int i = 0; i < riscv_count_harts(target); ++i) { - if (!riscv_hart_enabled(target, i)) + /* Don't call any riscv_* functions until after we've counted the number of + * cores and initialized registers. */ + for (int i = 0; i < MIN(RISCV_MAX_HARTS, 1 << info->hartsellen); ++i) { + if (!riscv_rtos_enabled(target) && i != target->coreid) continue; - riscv_set_current_hartid(target, i); + r->current_hartid = i; + riscv013_select_current_hart(target); + + uint32_t s = dmi_read(target, DMI_DMSTATUS); + if (get_field(s, DMI_DMSTATUS_ANYNONEXISTENT)) + break; + r->hart_count = i + 1; + + if (!riscv_is_halted(target)) { + if (riscv013_halt_current_hart(target) != ERROR_OK) { + LOG_ERROR("Fatal: Hart %d failed to halt during examine()", i); + return ERROR_FAIL; + } + } /* Without knowing anything else we can at least mess with the * program buffer. */ - r->debug_buffer_size[i] = riscv013_progbuf_size(target); + r->debug_buffer_size[i] = info->progbufsize; - /* Guess this is a 32-bit system, we're probing it. */ - r->xlen[i] = 32; - - /* First find the low 32 bits of the program buffer. This is - * used to check for alignment. */ - struct riscv_program program32; - riscv_program_init(&program32, target); - riscv_program_csrrw(&program32, GDB_REGNO_S0, GDB_REGNO_S0, GDB_REGNO_DSCRATCH); - riscv_program_insert(&program32, auipc(GDB_REGNO_S0)); - riscv_program_insert(&program32, sw(GDB_REGNO_S0, GDB_REGNO_S0, -4)); - riscv_program_csrrw(&program32, GDB_REGNO_S0, GDB_REGNO_S0, GDB_REGNO_DSCRATCH); - riscv_program_fence(&program32); - riscv_program_exec(&program32, target); - - riscv_addr_t progbuf_addr = dmi_read(target, DMI_PROGBUF0) - 4; - if (get_field(dmi_read(target, DMI_ABSTRACTCS), DMI_ABSTRACTCS_CMDERR) != 0) { - LOG_ERROR("Unable to find the address of the program buffer on hart %d", i); - r->xlen[i] = -1; - continue; - } - r->debug_buffer_addr[i] = progbuf_addr; - - /* Check to see if the core can execute 64 bit instructions. - * In order to make this work we first need to */ - int offset = (progbuf_addr % 8 == 0) ? -4 : 0; - - /* This program uses a temporary register. If the core can not - * execute 64 bit instruction, the original value of temporary - * register (s0) will not be restored due to an exception. - * So we have to save it and restore manually in that case. - * If the core can execute 64 bit instruction, the saved value - * is wrong, because it was read with 32 bit lw instruction, - * but the value of s0 will be restored by the reverse swap - * of s0 and dscratch registers. */ - uint64_t s0 = riscv_get_register(target, GDB_REGNO_S0); - - struct riscv_program program64; - riscv_program_init(&program64, target); - riscv_program_csrrw(&program64, GDB_REGNO_S0, GDB_REGNO_S0, GDB_REGNO_DSCRATCH); - riscv_program_insert(&program64, auipc(GDB_REGNO_S0)); - riscv_program_insert(&program64, sd(GDB_REGNO_S0, GDB_REGNO_S0, offset)); - riscv_program_csrrw(&program64, GDB_REGNO_S0, GDB_REGNO_S0, GDB_REGNO_DSCRATCH); - riscv_program_fence(&program64); - int result = riscv_program_exec(&program64, target); - - if (result == ERROR_OK) { - r->debug_buffer_addr[i] = - (dmi_read(target, DMI_PROGBUF0 + (8 + offset) / 4) << 32) - + dmi_read(target, DMI_PROGBUF0 + (4 + offset) / 4) - - 4; + int result = register_read_abstract(target, NULL, GDB_REGNO_S0, 64); + if (result == ERROR_OK) r->xlen[i] = 64; - } else { - riscv_set_register(target, GDB_REGNO_S0, s0); + else + r->xlen[i] = 32; + + if (register_read_direct(target, &r->misa, GDB_REGNO_MISA)) { + LOG_ERROR("Fatal: Failed to read MISA from hart %d.", i); + return ERROR_FAIL; } + /* Now init registers based on what we discovered. */ + if (riscv_init_registers(target) != ERROR_OK) + return ERROR_FAIL; + /* Display this as early as possible to help people who are using * really slow simulators. */ - LOG_DEBUG(" hart %d: XLEN=%d, program buffer at 0x%" PRIx64, i, - r->xlen[i], r->debug_buffer_addr[i]); + LOG_DEBUG(" hart %d: XLEN=%d, misa=0x%" PRIx64, i, r->xlen[i], + r->misa); + } - if (riscv_program_gah(&program64, r->debug_buffer_addr[i])) { - LOG_ERROR("This implementation will not work with hart %d with debug_buffer_addr of 0x%lx", i, - (long)r->debug_buffer_addr[i]); - abort(); - } + LOG_DEBUG("Enumerated %d harts", r->hart_count); - /* Check to see if we can use the data words as an extended - * program buffer or not. */ - if (r->debug_buffer_addr[i] + (4 * r->debug_buffer_size[i]) == riscv013_data_addr(target)) { - r->debug_buffer_size[i] += riscv013_data_size(target); - LOG_DEBUG("extending the debug buffer using data words, total size %d", r->debug_buffer_size[i]); - } + if (r->hart_count == 0) { + LOG_ERROR("No harts found!"); + return ERROR_FAIL; } /* Then we check the number of triggers availiable to each hart. */ riscv_enumerate_triggers(target); /* Resumes all the harts, so the debugger can later pause them. */ + /* TODO: Only do this if the harts were halted to start with. */ riscv_resume_all_harts(target); target->state = TARGET_RUNNING; + target_set_examined(target); - if (target->rtos) { + if (target->rtos) riscv_update_threads(target->rtos); - } - // Some regression suites rely on seeing 'Examined RISC-V core' to know - // when they can connect with gdb/telnet. - // We will need to update those suites if we want to change that text. + /* Some regression suites rely on seeing 'Examined RISC-V core' to know + * when they can connect with gdb/telnet. + * We will need to update those suites if we want to change that text. */ LOG_INFO("Examined RISC-V core; found %d harts", riscv_count_harts(target)); for (int i = 0; i < riscv_count_harts(target); ++i) { if (riscv_hart_enabled(target, i)) { - LOG_INFO(" hart %d: XLEN=%d, program buffer at 0x%" PRIx64 - ", %d triggers", i, r->xlen[i], r->debug_buffer_addr[i], + LOG_INFO(" hart %d: XLEN=%d, %d triggers", i, r->xlen[i], r->trigger_count[i]); } else { LOG_INFO(" hart %d: currently disabled", i); @@ -1190,40 +1298,40 @@ static int assert_reset(struct target *target) uint32_t control_base = set_field(0, DMI_DMCONTROL_DMACTIVE, 1); if (target->rtos) { - // There's only one target, and OpenOCD thinks each hart is a thread. - // We must reset them all. + /* There's only one target, and OpenOCD thinks each hart is a thread. + * We must reset them all. */ - // TODO: Try to use hasel in dmcontrol + /* TODO: Try to use hasel in dmcontrol */ - // Set haltreq/resumereq for each hart. + /* Set haltreq/resumereq for each hart. */ uint32_t control = control_base; for (int i = 0; i < riscv_count_harts(target); ++i) { if (!riscv_hart_enabled(target, i)) continue; - control = set_field(control_base, DMI_DMCONTROL_HARTSEL, i); + control = set_field(control_base, hartsel_mask(target), i); control = set_field(control, DMI_DMCONTROL_HALTREQ, target->reset_halt ? 1 : 0); dmi_write(target, DMI_DMCONTROL, control); } - // Assert ndmreset + /* Assert ndmreset */ control = set_field(control, DMI_DMCONTROL_NDMRESET, 1); dmi_write(target, DMI_DMCONTROL, control); } else { - // Reset just this hart. - uint32_t control = set_field(control_base, DMI_DMCONTROL_HARTSEL, + /* Reset just this hart. */ + uint32_t control = set_field(control_base, hartsel_mask(target), r->current_hartid); control = set_field(control, DMI_DMCONTROL_HALTREQ, target->reset_halt ? 1 : 0); control = set_field(control, DMI_DMCONTROL_HARTRESET, 1); dmi_write(target, DMI_DMCONTROL, control); - // Read back to check if hartreset is supported. + /* Read back to check if hartreset is supported. */ uint32_t rb = dmi_read(target, DMI_DMCONTROL); if (!get_field(rb, DMI_DMCONTROL_HARTRESET)) { - // Use ndmreset instead. That will reset the entire device, but - // that's probably what OpenOCD wants anyway. + /* Use ndmreset instead. That will reset the entire device, but + * that's probably what OpenOCD wants anyway. */ control = set_field(control, DMI_DMCONTROL_HARTRESET, 0); control = set_field(control, DMI_DMCONTROL_NDMRESET, 1); dmi_write(target, DMI_DMCONTROL, control); @@ -1243,10 +1351,10 @@ static int deassert_reset(struct target *target) LOG_DEBUG("%d", r->current_hartid); - // Clear the reset, but make sure haltreq is still set + /* Clear the reset, but make sure haltreq is still set */ uint32_t control = 0; control = set_field(control, DMI_DMCONTROL_HALTREQ, target->reset_halt ? 1 : 0); - control = set_field(control, DMI_DMCONTROL_HARTSEL, r->current_hartid); + control = set_field(control, hartsel_mask(target), r->current_hartid); control = set_field(control, DMI_DMCONTROL_DMACTIVE, 1); dmi_write(target, DMI_DMCONTROL, control); @@ -1295,6 +1403,9 @@ static int deassert_reset(struct target *target) return ERROR_OK; } +/** + * @size in bytes + */ static void write_to_buf(uint8_t *buffer, uint64_t value, unsigned size) { switch (size) { @@ -1316,6 +1427,30 @@ static void write_to_buf(uint8_t *buffer, uint64_t value, unsigned size) } } +static int execute_fence(struct target *target) +{ + struct riscv_program program; + riscv_program_init(&program, target); + riscv_program_fence(&program); + int result = riscv_program_exec(&program, target); + if (result != ERROR_OK) + LOG_ERROR("Unable to execute fence"); + return result; +} + +static void log_memory_access(target_addr_t address, uint64_t value, + unsigned size_bytes, bool read) +{ + if (debug_level < LOG_LVL_DEBUG) + return; + + char fmt[80]; + sprintf(fmt, "M[0x%" TARGET_PRIxADDR "] %ss 0x%%0%d" PRIx64, + address, read ? "read" : "write", size_bytes * 2); + value &= (((uint64_t) 0x1) << (size_bytes * 8)) - 1; + LOG_DEBUG(fmt, value); +} + /** * Read the requested memory, taking care to execute every read exactly once, * even if cmderr=busy is encountered. @@ -1325,26 +1460,28 @@ static int read_memory(struct target *target, target_addr_t address, { RISCV013_INFO(info); + int result = ERROR_OK; + LOG_DEBUG("reading %d words of %d bytes from 0x%" TARGET_PRIxADDR, count, size, address); select_dmi(target); - /* This program uses two temporary registers. A word of data and the - * associated address are stored at some location in memory. The - * program loads the word from that address and then increments the - * address. The debugger is expected to pull the memory word-by-word - * from the chip with AUTOEXEC set in order to trigger program - * execution on every word. */ - uint64_t s0 = riscv_get_register(target, GDB_REGNO_S0); - uint64_t s1 = riscv_get_register(target, GDB_REGNO_S1); + /* s0 holds the next address to write to + * s1 holds the next data value to write + */ + uint64_t s0, s1; + if (register_read_direct(target, &s0, GDB_REGNO_S0) != ERROR_OK) + return ERROR_FAIL; + if (register_read_direct(target, &s1, GDB_REGNO_S1) != ERROR_OK) + return ERROR_FAIL; + if (execute_fence(target) != ERROR_OK) + return ERROR_FAIL; + + /* Write the program (load, increment) */ struct riscv_program program; riscv_program_init(&program, target); - riscv_addr_t r_data = riscv_program_alloc_w(&program); - riscv_addr_t r_addr = riscv_program_alloc_x(&program); - riscv_program_fence(&program); - riscv_program_lx(&program, GDB_REGNO_S0, r_addr); switch (size) { case 1: riscv_program_lbr(&program, GDB_REGNO_S1, GDB_REGNO_S0, 0); @@ -1360,70 +1497,53 @@ static int read_memory(struct target *target, target_addr_t address, return ERROR_FAIL; } riscv_program_addi(&program, GDB_REGNO_S0, GDB_REGNO_S0, size); - riscv_program_sw(&program, GDB_REGNO_S1, r_data); - riscv_program_sx(&program, GDB_REGNO_S0, r_addr); - /* The first round through the program's execution we use the regular - * program execution mechanism. */ - switch (riscv_xlen(target)) { - case 64: - riscv_program_write_ram(&program, r_addr + 4, ((riscv_addr_t) address) >> 32); - case 32: - riscv_program_write_ram(&program, r_addr, (riscv_addr_t) address); - break; - default: - LOG_ERROR("unknown XLEN %d", riscv_xlen(target)); + if (riscv_program_ebreak(&program) != ERROR_OK) return ERROR_FAIL; - } + riscv_program_write(&program); - if (riscv_program_exec(&program, target) != ERROR_OK) { - uint32_t acs = dmi_read(target, DMI_ABSTRACTCS); - LOG_ERROR("failed to execute program, abstractcs=0x%08x", acs); - riscv013_clear_abstract_error(target); - riscv_set_register(target, GDB_REGNO_S0, s0); - riscv_set_register(target, GDB_REGNO_S1, s1); - LOG_ERROR(" exiting with ERROR_FAIL"); - return ERROR_FAIL; - } + /* Write address to S0, and execute buffer. */ + result = register_write_direct(target, GDB_REGNO_S0, address); + if (result != ERROR_OK) + goto error; + uint32_t command = access_register_command(GDB_REGNO_S1, riscv_xlen(target), + AC_ACCESS_REGISTER_TRANSFER | + AC_ACCESS_REGISTER_POSTEXEC); + result = execute_abstract_command(target, command); + if (result != ERROR_OK) + goto error; - // Program has been executed once. d_addr contains address+size, and d_data - // contains *address. + /* First read has just triggered. Result is in s1. */ - /* The rest of this program is designed to be fast so it reads various - * DMI registers directly. */ - int d_data = (r_data - riscv_debug_buffer_addr(target)) / 4; - int d_addr = (r_addr - riscv_debug_buffer_addr(target)) / 4; + dmi_write(target, DMI_ABSTRACTAUTO, + 1 << DMI_ABSTRACTAUTO_AUTOEXECDATA_OFFSET); - riscv013_set_autoexec(target, d_data, 1); - - /* Copying memory might fail because we're going too quickly, in which - * case we need to back off a bit and try again. There's two - * termination conditions to this loop: a non-BUSY error message, or - * the data was all copied. */ - riscv_addr_t cur_addr = riscv_read_debug_buffer_x(target, d_addr); + /* read_addr is the next address that the hart will read from, which is the + * value in s0. */ + riscv_addr_t read_addr = address + size; + /* The next address that we need to receive data for. */ + riscv_addr_t receive_addr = address; riscv_addr_t fin_addr = address + (count * size); - LOG_DEBUG("reading until final address 0x%" PRIx64, fin_addr); - while (cur_addr < fin_addr) { - // Invariant: - // d_data contains *addr - // d_addr contains addr + size + unsigned skip = 1; + while (read_addr < fin_addr) { + LOG_DEBUG("read_addr=0x%" PRIx64 ", receive_addr=0x%" PRIx64 + ", fin_addr=0x%" PRIx64, read_addr, receive_addr, fin_addr); + /* The pipeline looks like this: + * memory -> s1 -> dm_data0 -> debugger + * It advances every time the debugger reads dmdata0. + * So at any time the debugger has just read mem[s0 - 3*size], + * dm_data0 contains mem[s0 - 2*size] + * s1 contains mem[s0-size] */ - unsigned start = (cur_addr - address) / size; - LOG_DEBUG("creating burst to read address 0x%" TARGET_PRIxADDR - " up to 0x%" TARGET_PRIxADDR "; start=0x%d", cur_addr, fin_addr, start); - assert(cur_addr >= address && cur_addr < fin_addr); - struct riscv_batch *batch = riscv_batch_alloc( - target, - 32, - info->dmi_busy_delay + info->ac_busy_delay); + LOG_DEBUG("creating burst to read from 0x%" TARGET_PRIxADDR + " up to 0x%" TARGET_PRIxADDR, read_addr, fin_addr); + assert(read_addr >= address && read_addr < fin_addr); + struct riscv_batch *batch = riscv_batch_alloc(target, 32, + info->dmi_busy_delay + info->ac_busy_delay); size_t reads = 0; - for (riscv_addr_t addr = cur_addr; addr < fin_addr; addr += size) { - size_t const index = - riscv_batch_add_dmi_read( - batch, - riscv013_debug_buffer_register(target, r_data)); - assert(index == reads); + for (riscv_addr_t addr = read_addr; addr < fin_addr; addr += size) { + riscv_batch_add_dmi_read(batch, DMI_DATA0); reads++; if (riscv_batch_full(batch)) @@ -1432,73 +1552,143 @@ static int read_memory(struct target *target, target_addr_t address, riscv_batch_run(batch); - // Wait for the target to finish performing the last abstract command, - // and update our copy of cmderr. + /* Wait for the target to finish performing the last abstract command, + * and update our copy of cmderr. */ uint32_t abstractcs = dmi_read(target, DMI_ABSTRACTCS); while (get_field(abstractcs, DMI_ABSTRACTCS_BUSY)) abstractcs = dmi_read(target, DMI_ABSTRACTCS); info->cmderr = get_field(abstractcs, DMI_ABSTRACTCS_CMDERR); + unsigned cmderr = info->cmderr; + riscv_addr_t next_read_addr; + uint32_t dmi_data0 = -1; switch (info->cmderr) { - case CMDERR_NONE: - LOG_DEBUG("successful (partial?) memory read"); - break; - case CMDERR_BUSY: - LOG_DEBUG("memory read resulted in busy response"); - increase_ac_busy_delay(target); - riscv013_clear_abstract_error(target); - break; - default: - LOG_ERROR("error when reading memory, abstractcs=0x%08lx", (long)abstractcs); - riscv013_set_autoexec(target, d_data, 0); - riscv_set_register(target, GDB_REGNO_S0, s0); - riscv_set_register(target, GDB_REGNO_S1, s1); - riscv013_clear_abstract_error(target); - riscv_batch_free(batch); - return ERROR_FAIL; + case CMDERR_NONE: + LOG_DEBUG("successful (partial?) memory read"); + next_read_addr = read_addr + reads * size; + break; + case CMDERR_BUSY: + LOG_DEBUG("memory read resulted in busy response"); + + /* + * If you want to exercise this code path, apply the following patch to spike: +--- a/riscv/debug_module.cc ++++ b/riscv/debug_module.cc +@@ -1,3 +1,5 @@ ++#include ++ + #include + + #include "debug_module.h" +@@ -398,6 +400,15 @@ bool debug_module_t::perform_abstract_command() + // Since the next instruction is what we will use, just use nother NOP + // to get there. + write32(debug_abstract, 1, addi(ZERO, ZERO, 0)); ++ ++ if (abstractauto.autoexecdata && ++ program_buffer[0] == 0x83 && ++ program_buffer[1] == 0x24 && ++ program_buffer[2] == 0x04 && ++ program_buffer[3] == 0 && ++ rand() < RAND_MAX / 10) { ++ usleep(1000000); ++ } + } else { + write32(debug_abstract, 1, ebreak()); + } + */ + increase_ac_busy_delay(target); + riscv013_clear_abstract_error(target); + + dmi_write(target, DMI_ABSTRACTAUTO, 0); + + /* This is definitely a good version of the value that we + * attempted to read when we discovered that the target was + * busy. */ + dmi_data0 = dmi_read(target, DMI_DATA0); + + /* Clobbers DMI_DATA0. */ + result = register_read_direct(target, &next_read_addr, + GDB_REGNO_S0); + if (result != ERROR_OK) { + riscv_batch_free(batch); + goto error; + } + /* Restore the command, and execute it. + * Now DMI_DATA0 contains the next value just as it would if no + * error had occurred. */ + dmi_write(target, DMI_COMMAND, command); + + dmi_write(target, DMI_ABSTRACTAUTO, + 1 << DMI_ABSTRACTAUTO_AUTOEXECDATA_OFFSET); + break; + default: + LOG_ERROR("error when reading memory, abstractcs=0x%08lx", (long)abstractcs); + riscv013_clear_abstract_error(target); + riscv_batch_free(batch); + result = ERROR_FAIL; + goto error; } - // Figure out how far we managed to read. - riscv_addr_t next_addr = riscv_read_debug_buffer_x(target, d_addr); - LOG_DEBUG("Batch read [0x%" TARGET_PRIxADDR ", 0x%" TARGET_PRIxADDR - "); reads=%d", cur_addr, next_addr, (unsigned) reads); - assert(next_addr >= address && next_addr <= fin_addr); - assert(info->cmderr != CMDERR_NONE || - next_addr == cur_addr + reads * size); + /* Now read whatever we got out of the batch. */ + for (size_t i = 0; i < reads; i++) { + if (read_addr >= next_read_addr) + break; - // Now read whatever we got out of the batch. - unsigned rereads = 0; - for (riscv_addr_t addr = cur_addr - size; addr < next_addr - size; - addr += size) { - riscv_addr_t offset = addr - address; + read_addr += size; - uint64_t dmi_out = riscv_batch_get_dmi_read(batch, rereads); + if (skip > 0) { + skip--; + continue; + } + + riscv_addr_t offset = receive_addr - address; + uint64_t dmi_out = riscv_batch_get_dmi_read(batch, i); uint32_t value = get_field(dmi_out, DTM_DMI_DATA); write_to_buf(buffer + offset, value, size); + log_memory_access(receive_addr, value, size, true); - rereads++; - - LOG_DEBUG("M[0x%" TARGET_PRIxADDR "] reads 0x%08x", addr, value); + receive_addr += size; } riscv_batch_free(batch); - cur_addr = next_addr; + if (cmderr == CMDERR_BUSY) { + riscv_addr_t offset = receive_addr - address; + write_to_buf(buffer + offset, dmi_data0, size); + log_memory_access(receive_addr, dmi_data0, size, true); + read_addr += size; + receive_addr += size; + } } - riscv013_set_autoexec(target, d_data, 0); + dmi_write(target, DMI_ABSTRACTAUTO, 0); - // Read the last word. + if (count > 1) { + /* Read the penultimate word. */ + uint64_t value = dmi_read(target, DMI_DATA0); + write_to_buf(buffer + receive_addr - address, value, size); + log_memory_access(receive_addr, value, size, true); + receive_addr += size; + } - // Access debug buffer without executing a program. This - // address logic was taken from program.c. - uint32_t value = riscv013_read_debug_buffer(target, d_data); - riscv_addr_t addr = cur_addr - size; - write_to_buf(buffer + addr - address, value, size); - LOG_DEBUG("M[0x%" TARGET_PRIxADDR "] reads 0x%08x", addr, value); + /* Read the last word. */ + uint64_t value; + result = register_read_direct(target, &value, GDB_REGNO_S1); + if (result != ERROR_OK) + goto error; + write_to_buf(buffer + receive_addr - address, value, size); + log_memory_access(receive_addr, value, size, true); riscv_set_register(target, GDB_REGNO_S0, s0); riscv_set_register(target, GDB_REGNO_S1, s1); return ERROR_OK; + +error: + dmi_write(target, DMI_ABSTRACTAUTO, 0); + + riscv_set_register(target, GDB_REGNO_S0, s0); + riscv_set_register(target, GDB_REGNO_S1, s1); + return result; } static int write_memory(struct target *target, target_addr_t address, @@ -1510,22 +1700,20 @@ static int write_memory(struct target *target, target_addr_t address, select_dmi(target); - /* This program uses two temporary registers. A word of data and the - * associated address are stored at some location in memory. The - * program stores the word to that address and then increments the - * address. The debugger is expected to feed the memory word-by-word - * into the chip with AUTOEXEC set in order to trigger program - * execution on every word. */ - uint64_t s0 = riscv_get_register(target, GDB_REGNO_S0); - uint64_t s1 = riscv_get_register(target, GDB_REGNO_S1); + /* s0 holds the next address to write to + * s1 holds the next data value to write + */ + int result = ERROR_OK; + uint64_t s0, s1; + if (register_read_direct(target, &s0, GDB_REGNO_S0) != ERROR_OK) + return ERROR_FAIL; + if (register_read_direct(target, &s1, GDB_REGNO_S1) != ERROR_OK) + return ERROR_FAIL; + + /* Write the program (store, increment) */ struct riscv_program program; riscv_program_init(&program, target); - riscv_addr_t r_data = riscv_program_alloc_w(&program); - riscv_addr_t r_addr = riscv_program_alloc_x(&program); - riscv_program_fence(&program); - riscv_program_lx(&program, GDB_REGNO_S0, r_addr); - riscv_program_lw(&program, GDB_REGNO_S1, r_data); switch (size) { case 1: @@ -1539,87 +1727,37 @@ static int write_memory(struct target *target, target_addr_t address, break; default: LOG_ERROR("Unsupported size: %d", size); - return ERROR_FAIL; + result = ERROR_FAIL; + goto error; } riscv_program_addi(&program, GDB_REGNO_S0, GDB_REGNO_S0, size); - riscv_program_sx(&program, GDB_REGNO_S0, r_addr); - /* The first round through the program's execution we use the regular - * program execution mechanism. */ - uint32_t value; - switch (size) { - case 1: - value = buffer[0]; - break; - case 2: - value = buffer[0] - | ((uint32_t) buffer[1] << 8); - break; - case 4: - value = buffer[0] - | ((uint32_t) buffer[1] << 8) - | ((uint32_t) buffer[2] << 16) - | ((uint32_t) buffer[3] << 24); - break; - default: - LOG_ERROR("unsupported access size: %d", size); - return ERROR_FAIL; - } + result = riscv_program_ebreak(&program); + if (result != ERROR_OK) + goto error; + riscv_program_write(&program); - switch (riscv_xlen(target)) { - case 64: - riscv_program_write_ram(&program, r_addr + 4, (uint64_t)address >> 32); - case 32: - riscv_program_write_ram(&program, r_addr, address); - break; - default: - LOG_ERROR("unknown XLEN %d", riscv_xlen(target)); - return ERROR_FAIL; - } - riscv_program_write_ram(&program, r_data, value); - - LOG_DEBUG("M[0x%08lx] writes 0x%08x", (long)address, value); - - if (riscv_program_exec(&program, target) != ERROR_OK) { - uint32_t acs = dmi_read(target, DMI_ABSTRACTCS); - LOG_ERROR("failed to execute program, abstractcs=0x%08x", acs); - riscv013_clear_abstract_error(target); - riscv_set_register(target, GDB_REGNO_S0, s0); - riscv_set_register(target, GDB_REGNO_S1, s1); - LOG_ERROR(" exiting with ERROR_FAIL"); - return ERROR_FAIL; - } - - /* The rest of this program is designed to be fast so it reads various - * DMI registers directly. */ - int d_data = (r_data - riscv_debug_buffer_addr(target)) / 4; - int d_addr = (r_addr - riscv_debug_buffer_addr(target)) / 4; - - riscv013_set_autoexec(target, d_data, 1); - - /* Copying memory might fail because we're going too quickly, in which - * case we need to back off a bit and try again. There's two - * termination conditions to this loop: a non-BUSY error message, or - * the data was all copied. */ - riscv_addr_t cur_addr = 0xbadbeef; + riscv_addr_t cur_addr = address; riscv_addr_t fin_addr = address + (count * size); + bool setup_needed = true; LOG_DEBUG("writing until final address 0x%016" PRIx64, fin_addr); - while ((cur_addr = riscv_read_debug_buffer_x(target, d_addr)) < fin_addr) { + while (cur_addr < fin_addr) { LOG_DEBUG("transferring burst starting at address 0x%016" PRIx64, cur_addr); - riscv_addr_t start = (cur_addr - address) / size; - assert (cur_addr > address); - struct riscv_batch *batch = riscv_batch_alloc( - target, - 32, - info->dmi_busy_delay + info->ac_busy_delay); - for (riscv_addr_t i = start; i < count; ++i) { - riscv_addr_t offset = size*i; - riscv_addr_t t_addr = address + offset; + struct riscv_batch *batch = riscv_batch_alloc( + target, + 32, + info->dmi_busy_delay + info->ac_busy_delay); + + /* To write another word, we put it in S1 and execute the program. */ + unsigned start = (cur_addr - address) / size; + for (unsigned i = start; i < count; ++i) { + unsigned offset = size*i; const uint8_t *t_buffer = buffer + offset; + uint32_t value; switch (size) { case 1: value = t_buffer[0]; @@ -1636,54 +1774,98 @@ static int write_memory(struct target *target, target_addr_t address, break; default: LOG_ERROR("unsupported access size: %d", size); - return ERROR_FAIL; + riscv_batch_free(batch); + result = ERROR_FAIL; + goto error; } - LOG_DEBUG("M[0x%08lx] writes 0x%08x", (long)t_addr, value); + log_memory_access(address + offset, value, size, false); + cur_addr += size; - riscv_batch_add_dmi_write( - batch, - riscv013_debug_buffer_register(target, r_data), - value); - if (riscv_batch_full(batch)) - break; + if (setup_needed) { + result = register_write_direct(target, GDB_REGNO_S0, + address + offset); + if (result != ERROR_OK) { + riscv_batch_free(batch); + goto error; + } + + /* Write value. */ + dmi_write(target, DMI_DATA0, value); + + /* Write and execute command that moves value into S1 and + * executes program buffer. */ + uint32_t command = access_register_command(GDB_REGNO_S1, 32, + AC_ACCESS_REGISTER_POSTEXEC | + AC_ACCESS_REGISTER_TRANSFER | + AC_ACCESS_REGISTER_WRITE); + result = execute_abstract_command(target, command); + if (result != ERROR_OK) { + riscv_batch_free(batch); + goto error; + } + + /* Turn on autoexec */ + dmi_write(target, DMI_ABSTRACTAUTO, + 1 << DMI_ABSTRACTAUTO_AUTOEXECDATA_OFFSET); + + setup_needed = false; + } else { + riscv_batch_add_dmi_write(batch, DMI_DATA0, value); + if (riscv_batch_full(batch)) + break; + } } - riscv_batch_run(batch); + result = riscv_batch_run(batch); riscv_batch_free(batch); + if (result != ERROR_OK) + goto error; - // Note that if the scan resulted in a Busy DMI response, it - // is this read to abstractcs that will cause the dmi_busy_delay - // to be incremented if necessary. The loop condition above - // catches the case where no writes went through at all. + /* Note that if the scan resulted in a Busy DMI response, it + * is this read to abstractcs that will cause the dmi_busy_delay + * to be incremented if necessary. */ uint32_t abstractcs = dmi_read(target, DMI_ABSTRACTCS); while (get_field(abstractcs, DMI_ABSTRACTCS_BUSY)) abstractcs = dmi_read(target, DMI_ABSTRACTCS); info->cmderr = get_field(abstractcs, DMI_ABSTRACTCS_CMDERR); switch (info->cmderr) { - case CMDERR_NONE: - LOG_DEBUG("successful (partial?) memory write"); - break; - case CMDERR_BUSY: - LOG_DEBUG("memory write resulted in busy response"); - riscv013_clear_abstract_error(target); - increase_ac_busy_delay(target); - break; - default: - LOG_ERROR("error when writing memory, abstractcs=0x%08lx", (long)abstractcs); - riscv013_set_autoexec(target, d_data, 0); - riscv013_clear_abstract_error(target); - riscv_set_register(target, GDB_REGNO_S0, s0); - riscv_set_register(target, GDB_REGNO_S1, s1); - return ERROR_FAIL; + case CMDERR_NONE: + LOG_DEBUG("successful (partial?) memory write"); + break; + case CMDERR_BUSY: + LOG_DEBUG("memory write resulted in busy response"); + riscv013_clear_abstract_error(target); + increase_ac_busy_delay(target); + + dmi_write(target, DMI_ABSTRACTAUTO, 0); + result = register_read_direct(target, &cur_addr, GDB_REGNO_S0); + if (result != ERROR_OK) + goto error; + setup_needed = true; + break; + + default: + LOG_ERROR("error when writing memory, abstractcs=0x%08lx", (long)abstractcs); + riscv013_clear_abstract_error(target); + result = ERROR_FAIL; + goto error; } } - riscv013_set_autoexec(target, d_data, 0); - riscv_set_register(target, GDB_REGNO_S0, s0); - riscv_set_register(target, GDB_REGNO_S1, s1); - return ERROR_OK; +error: + dmi_write(target, DMI_ABSTRACTAUTO, 0); + + if (register_write_direct(target, GDB_REGNO_S1, s1) != ERROR_OK) + return ERROR_FAIL; + if (register_write_direct(target, GDB_REGNO_S0, s0) != ERROR_OK) + return ERROR_FAIL; + + if (execute_fence(target) != ERROR_OK) + return ERROR_FAIL; + + return result; } static int arch_state(struct target *target) @@ -1691,8 +1873,7 @@ static int arch_state(struct target *target) return ERROR_OK; } -struct target_type riscv013_target = -{ +struct target_type riscv013_target = { .name = "riscv", .init_target = init_target, @@ -1714,39 +1895,35 @@ struct target_type riscv013_target = }; /*** 0.13-specific implementations of various RISC-V helper functions. ***/ -static riscv_reg_t riscv013_get_register(struct target *target, int hid, int rid) +static int riscv013_get_register(struct target *target, + riscv_reg_t *value, int hid, int rid) { LOG_DEBUG("reading register %s on hart %d", gdb_regno_name(rid), hid); riscv_set_current_hartid(target, hid); - uint64_t out; - riscv013_info_t *info = get_info(target); - + int result = ERROR_OK; if (rid <= GDB_REGNO_XPR31) { - register_read_direct(target, &out, rid); + result = register_read_direct(target, value, rid); } else if (rid == GDB_REGNO_PC) { - register_read_direct(target, &out, GDB_REGNO_DPC); - LOG_DEBUG("read PC from DPC: 0x%016" PRIx64, out); + result = register_read_direct(target, value, GDB_REGNO_DPC); + LOG_DEBUG("read PC from DPC: 0x%016" PRIx64, *value); } else if (rid == GDB_REGNO_PRIV) { uint64_t dcsr; - register_read_direct(target, &dcsr, GDB_REGNO_DCSR); - buf_set_u64((unsigned char *)&out, 0, 8, get_field(dcsr, CSR_DCSR_PRV)); + result = register_read_direct(target, &dcsr, GDB_REGNO_DCSR); + buf_set_u64((unsigned char *)value, 0, 8, get_field(dcsr, CSR_DCSR_PRV)); } else { - int result = register_read_direct(target, &out, rid); + result = register_read_direct(target, value, rid); if (result != ERROR_OK) { - LOG_ERROR("Unable to read register %d", rid); - out = -1; + LOG_ERROR("Unable to read %s", gdb_regno_name(rid)); + *value = -1; } - - if (rid == GDB_REGNO_MSTATUS) - info->mstatus_actual = out; } - return out; + return result; } -static void riscv013_set_register(struct target *target, int hid, int rid, uint64_t value) +static int riscv013_set_register(struct target *target, int hid, int rid, uint64_t value) { LOG_DEBUG("writing 0x%" PRIx64 " to register %s on hart %d", value, gdb_regno_name(rid), hid); @@ -1754,22 +1931,28 @@ static void riscv013_set_register(struct target *target, int hid, int rid, uint6 riscv_set_current_hartid(target, hid); if (rid <= GDB_REGNO_XPR31) { - register_write_direct(target, rid, value); + return register_write_direct(target, rid, value); } else if (rid == GDB_REGNO_PC) { LOG_DEBUG("writing PC to DPC: 0x%016" PRIx64, value); register_write_direct(target, GDB_REGNO_DPC, value); uint64_t actual_value; register_read_direct(target, &actual_value, GDB_REGNO_DPC); LOG_DEBUG(" actual DPC written: 0x%016" PRIx64, actual_value); - assert(value == actual_value); + if (value != actual_value) { + LOG_ERROR("Written PC (0x%" PRIx64 ") does not match read back " + "value (0x%" PRIx64 ")", value, actual_value); + return ERROR_FAIL; + } } else if (rid == GDB_REGNO_PRIV) { uint64_t dcsr; register_read_direct(target, &dcsr, GDB_REGNO_DCSR); dcsr = set_field(dcsr, CSR_DCSR_PRV, value); - register_write_direct(target, GDB_REGNO_DCSR, dcsr); + return register_write_direct(target, GDB_REGNO_DCSR, dcsr); } else { - register_write_direct(target, rid, value); + return register_write_direct(target, rid, value); } + + return ERROR_OK; } static void riscv013_select_current_hart(struct target *target) @@ -1777,15 +1960,16 @@ static void riscv013_select_current_hart(struct target *target) RISCV_INFO(r); uint64_t dmcontrol = dmi_read(target, DMI_DMCONTROL); - dmcontrol = set_field(dmcontrol, DMI_DMCONTROL_HARTSEL, r->current_hartid); + dmcontrol = set_field(dmcontrol, hartsel_mask(target), r->current_hartid); dmi_write(target, DMI_DMCONTROL, dmcontrol); } -static void riscv013_halt_current_hart(struct target *target) +static int riscv013_halt_current_hart(struct target *target) { RISCV_INFO(r); LOG_DEBUG("halting hart %d", r->current_hartid); - assert(!riscv_is_halted(target)); + if (riscv_is_halted(target)) + LOG_ERROR("Hart %d is already halted!", r->current_hartid); /* Issue the halt command, and then wait for the current hart to halt. */ uint32_t dmcontrol = dmi_read(target, DMI_DMCONTROL); @@ -1802,35 +1986,38 @@ static void riscv013_halt_current_hart(struct target *target) LOG_ERROR("unable to halt hart %d", r->current_hartid); LOG_ERROR(" dmcontrol=0x%08x", dmcontrol); LOG_ERROR(" dmstatus =0x%08x", dmstatus); - abort(); + return ERROR_FAIL; } dmcontrol &= ~DMI_DMCONTROL_HALTREQ; dmi_write(target, DMI_DMCONTROL, dmcontrol); + + return ERROR_OK; } -static void riscv013_resume_current_hart(struct target *target) +static int riscv013_resume_current_hart(struct target *target) { return riscv013_step_or_resume_current_hart(target, false); } -static void riscv013_step_current_hart(struct target *target) +static int riscv013_step_current_hart(struct target *target) { return riscv013_step_or_resume_current_hart(target, true); } -static void riscv013_on_resume(struct target *target) +static int riscv013_on_resume(struct target *target) { return riscv013_on_step_or_resume(target, false); } -static void riscv013_on_step(struct target *target) +static int riscv013_on_step(struct target *target) { return riscv013_on_step_or_resume(target, true); } -static void riscv013_on_halt(struct target *target) +static int riscv013_on_halt(struct target *target) { + return ERROR_OK; } static bool riscv013_is_halted(struct target *target) @@ -1845,7 +2032,11 @@ static bool riscv013_is_halted(struct target *target) static enum riscv_halt_reason riscv013_halt_reason(struct target *target) { - uint64_t dcsr = riscv_get_register(target, GDB_REGNO_DCSR); + riscv_reg_t dcsr; + int result = register_read_direct(target, &dcsr, GDB_REGNO_DCSR); + if (result != ERROR_OK) + return RISCV_HALT_UNKNOWN; + switch (get_field(dcsr, CSR_DCSR_CAUSE)) { case CSR_DCSR_CAUSE_SWBP: case CSR_DCSR_CAUSE_TRIGGER: @@ -1859,28 +2050,16 @@ static enum riscv_halt_reason riscv013_halt_reason(struct target *target) LOG_ERROR("Unknown DCSR cause field: %x", (int)get_field(dcsr, CSR_DCSR_CAUSE)); LOG_ERROR(" dcsr=0x%016lx", (long)dcsr); - abort(); + return RISCV_HALT_UNKNOWN; } -void riscv013_debug_buffer_enter(struct target *target, struct riscv_program *program) +int riscv013_write_debug_buffer(struct target *target, unsigned index, riscv_insn_t data) { -} - -void riscv013_debug_buffer_leave(struct target *target, struct riscv_program *program) -{ -} - -void riscv013_write_debug_buffer(struct target *target, unsigned index, riscv_insn_t data) -{ - if (index >= riscv013_progbuf_size(target)) - return dmi_write(target, DMI_DATA0 + index - riscv013_progbuf_size(target), data); return dmi_write(target, DMI_PROGBUF0 + index, data); } riscv_insn_t riscv013_read_debug_buffer(struct target *target, unsigned index) { - if (index >= riscv013_progbuf_size(target)) - return dmi_read(target, DMI_DATA0 + index - riscv013_progbuf_size(target)); return dmi_read(target, DMI_PROGBUF0 + index); } @@ -1927,7 +2106,7 @@ int riscv013_dmi_write_u64_bits(struct target *target) /* Helper Functions. */ -static void riscv013_on_step_or_resume(struct target *target, bool step) +static int riscv013_on_step_or_resume(struct target *target, bool step) { struct riscv_program program; riscv_program_init(&program, target); @@ -1936,26 +2115,31 @@ static void riscv013_on_step_or_resume(struct target *target, bool step) LOG_ERROR("Unable to execute fence.i"); /* We want to twiddle some bits in the debug CSR so debugging works. */ - uint64_t dcsr = riscv_get_register(target, GDB_REGNO_DCSR); + riscv_reg_t dcsr; + int result = register_read_direct(target, &dcsr, GDB_REGNO_DCSR); + if (result != ERROR_OK) + return result; dcsr = set_field(dcsr, CSR_DCSR_STEP, step); dcsr = set_field(dcsr, CSR_DCSR_EBREAKM, 1); - dcsr = set_field(dcsr, CSR_DCSR_EBREAKH, 1); dcsr = set_field(dcsr, CSR_DCSR_EBREAKS, 1); dcsr = set_field(dcsr, CSR_DCSR_EBREAKU, 1); - riscv_set_register(target, GDB_REGNO_DCSR, dcsr); + return riscv_set_register(target, GDB_REGNO_DCSR, dcsr); } -static void riscv013_step_or_resume_current_hart(struct target *target, bool step) +static int riscv013_step_or_resume_current_hart(struct target *target, bool step) { RISCV_INFO(r); LOG_DEBUG("resuming hart %d (for step?=%d)", r->current_hartid, step); - assert(riscv_is_halted(target)); + if (!riscv_is_halted(target)) { + LOG_ERROR("Hart %d is not halted!", r->current_hartid); + return ERROR_FAIL; + } struct riscv_program program; riscv_program_init(&program, target); riscv_program_fence_i(&program); if (riscv_program_exec(&program, target) != ERROR_OK) - abort(); + return ERROR_FAIL; /* Issue the resume command, and then wait for the current hart to resume. */ uint32_t dmcontrol = dmi_read(target, DMI_DMCONTROL); @@ -1972,7 +2156,7 @@ static void riscv013_step_or_resume_current_hart(struct target *target, bool ste dmcontrol = set_field(dmcontrol, DMI_DMCONTROL_RESUMEREQ, 0); dmi_write(target, DMI_DMCONTROL, dmcontrol); - return; + return ERROR_OK; } uint32_t dmstatus = dmi_read(target, DMI_DMSTATUS); @@ -1984,81 +2168,15 @@ static void riscv013_step_or_resume_current_hart(struct target *target, bool ste if (step) { LOG_ERROR(" was stepping, halting"); riscv013_halt_current_hart(target); - return; + return ERROR_OK; } - abort(); -} - -riscv_addr_t riscv013_progbuf_addr(struct target *target) -{ - RISCV013_INFO(info); - assert(info->progbuf_addr != -1); - return info->progbuf_addr; -} - -riscv_addr_t riscv013_progbuf_size(struct target *target) -{ - RISCV013_INFO(info); - if (info->progbuf_size == -1) { - uint32_t acs = dmi_read(target, DMI_ABSTRACTCS); - info->progbuf_size = get_field(acs, DMI_ABSTRACTCS_PROGSIZE); - } - return info->progbuf_size; -} - -riscv_addr_t riscv013_data_size(struct target *target) -{ - RISCV013_INFO(info); - if (info->data_size == -1) { - uint32_t acs = dmi_read(target, DMI_HARTINFO); - info->data_size = get_field(acs, DMI_HARTINFO_DATASIZE); - } - return info->data_size; -} - -riscv_addr_t riscv013_data_addr(struct target *target) -{ - RISCV013_INFO(info); - if (info->data_addr == -1) { - uint32_t acs = dmi_read(target, DMI_HARTINFO); - info->data_addr = get_field(acs, DMI_HARTINFO_DATAACCESS) ? get_field(acs, DMI_HARTINFO_DATAADDR) : 0; - } - return info->data_addr; -} - -void riscv013_set_autoexec(struct target *target, unsigned index, bool enabled) -{ - if (index >= riscv013_progbuf_size(target)) { - LOG_DEBUG("setting bit %d in AUTOEXECDATA to %d", index, enabled); - uint32_t aa = dmi_read(target, DMI_ABSTRACTAUTO); - uint32_t aa_aed = get_field(aa, DMI_ABSTRACTAUTO_AUTOEXECDATA); - aa_aed &= ~(1 << (index - riscv013_progbuf_size(target))); - aa_aed |= (enabled << (index - riscv013_progbuf_size(target))); - aa = set_field(aa, DMI_ABSTRACTAUTO_AUTOEXECDATA, aa_aed); - dmi_write(target, DMI_ABSTRACTAUTO, aa); - } else { - LOG_DEBUG("setting bit %d in AUTOEXECPROGBUF to %d", index, enabled); - uint32_t aa = dmi_read(target, DMI_ABSTRACTAUTO); - uint32_t aa_aed = get_field(aa, DMI_ABSTRACTAUTO_AUTOEXECPROGBUF); - aa_aed &= ~(1 << index); - aa_aed |= (enabled << index); - aa = set_field(aa, DMI_ABSTRACTAUTO_AUTOEXECPROGBUF, aa_aed); - dmi_write(target, DMI_ABSTRACTAUTO, aa); - } -} - -int riscv013_debug_buffer_register(struct target *target, riscv_addr_t addr) -{ - if (addr >= riscv013_data_addr(target)) - return DMI_DATA0 + (addr - riscv013_data_addr(target)) / 4; - else - return DMI_PROGBUF0 + (addr - riscv013_progbuf_addr(target)) / 4; + return ERROR_FAIL; } void riscv013_clear_abstract_error(struct target *target) { - // Wait for busy to go away. + /* Wait for busy to go away. */ time_t start = time(NULL); uint32_t abstractcs = dmi_read(target, DMI_ABSTRACTCS); while (get_field(abstractcs, DMI_ABSTRACTCS_BUSY)) { @@ -2073,7 +2191,7 @@ void riscv013_clear_abstract_error(struct target *target) break; } } - // Clear the error status. + /* Clear the error status. */ dmi_write(target, DMI_ABSTRACTCS, abstractcs & DMI_ABSTRACTCS_CMDERR); } diff --git a/src/target/riscv/riscv.c b/src/target/riscv/riscv.c index 01fd8bfb7..f6ad20e95 100644 --- a/src/target/riscv/riscv.c +++ b/src/target/riscv/riscv.c @@ -65,7 +65,7 @@ #define DIM(x) (sizeof(x)/sizeof(*x)) -// Constants for legacy SiFive hardware breakpoints. +/* Constants for legacy SiFive hardware breakpoints. */ #define CSR_BPCONTROL_X (1<<0) #define CSR_BPCONTROL_W (1<<1) #define CSR_BPCONTROL_R (1<<2) @@ -151,21 +151,6 @@ typedef enum slot { #define DBUS_ADDRESS_UNKNOWN 0xffff -// gdb's register list is defined in riscv_gdb_reg_names gdb/riscv-tdep.c in -// its source tree. We must interpret the numbers the same here. -enum { - REG_XPR0 = 0, - REG_XPR31 = 31, - REG_PC = 32, - REG_FPR0 = 33, - REG_FPR31 = 64, - REG_CSR0 = 65, - REG_MSTATUS = CSR_MSTATUS + REG_CSR0, - REG_CSR4095 = 4160, - REG_PRIV = 4161, - REG_COUNT -}; - #define MAX_HWBPS 16 #define DRAM_CACHE_SIZE 16 @@ -200,6 +185,17 @@ int riscv_command_timeout_sec = DEFAULT_COMMAND_TIMEOUT_SEC; /* Wall-clock timeout after reset. Settable via RISC-V Target commands.*/ int riscv_reset_timeout_sec = DEFAULT_RESET_TIMEOUT_SEC; +bool riscv_use_scratch_ram; +uint64_t riscv_scratch_ram_address; + +/* In addition to the ones in the standard spec, we'll also expose additional + * CSRs in this list. + * The list is either NULL, or a series of ranges (inclusive), terminated with + * 1,0. */ +struct { + uint16_t low, high; +} *expose_csr; + static uint32_t dtmcontrol_scan(struct target *target, uint32_t out) { struct scan_field field; @@ -288,7 +284,7 @@ static void trigger_from_breakpoint(struct trigger *trigger, trigger->read = false; trigger->write = false; trigger->execute = true; - // unique_id is unique across both breakpoints and watchpoints. + /* unique_id is unique across both breakpoints and watchpoints. */ trigger->unique_id = breakpoint->unique_id; } @@ -308,7 +304,7 @@ static int maybe_add_trigger_t1(struct target *target, unsigned hartid, const uint32_t bpcontrol_bpaction = 0xff << 11; if (tdata1 & (bpcontrol_r | bpcontrol_w | bpcontrol_x)) { - // Trigger is already in use, presumably by user code. + /* Trigger is already in use, presumably by user code. */ return ERROR_TARGET_RESOURCE_NOT_AVAILABLE; } @@ -319,13 +315,15 @@ static int maybe_add_trigger_t1(struct target *target, unsigned hartid, tdata1 = set_field(tdata1, bpcontrol_s, !!(r->misa & (1 << ('S' - 'A')))); tdata1 = set_field(tdata1, bpcontrol_h, !!(r->misa & (1 << ('H' - 'A')))); tdata1 |= bpcontrol_m; - tdata1 = set_field(tdata1, bpcontrol_bpmatch, 0); // exact match - tdata1 = set_field(tdata1, bpcontrol_bpaction, 0); // cause bp exception + tdata1 = set_field(tdata1, bpcontrol_bpmatch, 0); /* exact match */ + tdata1 = set_field(tdata1, bpcontrol_bpaction, 0); /* cause bp exception */ riscv_set_register_on_hart(target, hartid, GDB_REGNO_TDATA1, tdata1); - riscv_reg_t tdata1_rb = riscv_get_register_on_hart(target, hartid, - GDB_REGNO_TDATA1); + riscv_reg_t tdata1_rb; + if (riscv_get_register_on_hart(target, &tdata1_rb, hartid, + GDB_REGNO_TDATA1) != ERROR_OK) + return ERROR_FAIL; LOG_DEBUG("tdata1=0x%" PRIx64, tdata1_rb); if (tdata1 != tdata1_rb) { @@ -346,13 +344,13 @@ static int maybe_add_trigger_t2(struct target *target, unsigned hartid, { RISCV_INFO(r); - // tselect is already set + /* tselect is already set */ if (tdata1 & (MCONTROL_EXECUTE | MCONTROL_STORE | MCONTROL_LOAD)) { - // Trigger is already in use, presumably by user code. + /* Trigger is already in use, presumably by user code. */ return ERROR_TARGET_RESOURCE_NOT_AVAILABLE; } - // address/data match trigger + /* address/data match trigger */ tdata1 |= MCONTROL_DMODE(riscv_xlen(target)); tdata1 = set_field(tdata1, MCONTROL_ACTION, MCONTROL_ACTION_DEBUG_MODE); @@ -374,7 +372,10 @@ static int maybe_add_trigger_t2(struct target *target, unsigned hartid, riscv_set_register_on_hart(target, hartid, GDB_REGNO_TDATA1, tdata1); - uint64_t tdata1_rb = riscv_get_register_on_hart(target, hartid, GDB_REGNO_TDATA1); + uint64_t tdata1_rb; + int result = riscv_get_register_on_hart(target, &tdata1_rb, hartid, GDB_REGNO_TDATA1); + if (result != ERROR_OK) + return result; LOG_DEBUG("tdata1=0x%" PRIx64, tdata1_rb); if (tdata1 != tdata1_rb) { @@ -394,12 +395,12 @@ static int add_trigger(struct target *target, struct trigger *trigger) { RISCV_INFO(r); - // In RTOS mode, we need to set the same trigger in the same slot on every - // hart, to keep up the illusion that each hart is a thread running on the - // same core. + /* In RTOS mode, we need to set the same trigger in the same slot on every + * hart, to keep up the illusion that each hart is a thread running on the + * same core. */ - // Otherwise, we just set the trigger on the one hart this target deals - // with. + /* Otherwise, we just set the trigger on the one hart this target deals + * with. */ riscv_reg_t tselect[RISCV_MAX_HARTS]; @@ -409,30 +410,33 @@ static int add_trigger(struct target *target, struct trigger *trigger) continue; if (first_hart < 0) first_hart = hartid; - tselect[hartid] = riscv_get_register_on_hart(target, hartid, - GDB_REGNO_TSELECT); + int result = riscv_get_register_on_hart(target, &tselect[hartid], + hartid, GDB_REGNO_TSELECT); + if (result != ERROR_OK) + return result; } assert(first_hart >= 0); unsigned int i; for (i = 0; i < r->trigger_count[first_hart]; i++) { - if (r->trigger_unique_id[i] != -1) { + if (r->trigger_unique_id[i] != -1) continue; - } riscv_set_register_on_hart(target, first_hart, GDB_REGNO_TSELECT, i); - uint64_t tdata1 = riscv_get_register_on_hart(target, first_hart, GDB_REGNO_TDATA1); + uint64_t tdata1; + int result = riscv_get_register_on_hart(target, &tdata1, first_hart, + GDB_REGNO_TDATA1); + if (result != ERROR_OK) + return result; int type = get_field(tdata1, MCONTROL_TYPE(riscv_xlen(target))); - int result = ERROR_OK; + result = ERROR_OK; for (int hartid = first_hart; hartid < riscv_count_harts(target); ++hartid) { - LOG_DEBUG(">>> hartid=%d", hartid); if (!riscv_hart_enabled(target, hartid)) continue; - if (hartid > first_hart) { + if (hartid > first_hart) riscv_set_register_on_hart(target, hartid, GDB_REGNO_TSELECT, i); - } switch (type) { case 1: result = maybe_add_trigger_t1(target, hartid, trigger, tdata1); @@ -445,14 +449,12 @@ static int add_trigger(struct target *target, struct trigger *trigger) continue; } - if (result != ERROR_OK) { + if (result != ERROR_OK) continue; - } } - if (result != ERROR_OK) { + if (result != ERROR_OK) continue; - } LOG_DEBUG("Using trigger %d (type %d) for bp %d", i, type, trigger->unique_id); @@ -486,11 +488,10 @@ int riscv_add_breakpoint(struct target *target, struct breakpoint *breakpoint) } int retval; - if (breakpoint->length == 4) { + if (breakpoint->length == 4) retval = target_write_u32(target, breakpoint->address, ebreak()); - } else { + else retval = target_write_u16(target, breakpoint->address, ebreak_c()); - } if (retval != ERROR_OK) { LOG_ERROR("Failed to write %d-byte breakpoint instruction at 0x%" TARGET_PRIxADDR, breakpoint->length, breakpoint->address); @@ -501,9 +502,8 @@ int riscv_add_breakpoint(struct target *target, struct breakpoint *breakpoint) struct trigger trigger; trigger_from_breakpoint(&trigger, breakpoint); int result = add_trigger(target, &trigger); - if (result != ERROR_OK) { + if (result != ERROR_OK) return result; - } } else { LOG_INFO("OpenOCD only supports hardware and software breakpoints."); @@ -532,9 +532,8 @@ static int remove_trigger(struct target *target, struct trigger *trigger) unsigned int i; for (i = 0; i < r->trigger_count[first_hart]; i++) { - if (r->trigger_unique_id[i] == trigger->unique_id) { + if (r->trigger_unique_id[i] == trigger->unique_id) break; - } } if (i >= r->trigger_count[first_hart]) { LOG_ERROR("Couldn't find the hardware resources used by hardware " @@ -545,7 +544,10 @@ static int remove_trigger(struct target *target, struct trigger *trigger) for (int hartid = first_hart; hartid < riscv_count_harts(target); ++hartid) { if (!riscv_hart_enabled(target, hartid)) continue; - riscv_reg_t tselect = riscv_get_register_on_hart(target, hartid, GDB_REGNO_TSELECT); + riscv_reg_t tselect; + int result = riscv_get_register_on_hart(target, &tselect, hartid, GDB_REGNO_TSELECT); + if (result != ERROR_OK) + return result; riscv_set_register_on_hart(target, hartid, GDB_REGNO_TSELECT, i); riscv_set_register_on_hart(target, hartid, GDB_REGNO_TDATA1, 0); riscv_set_register_on_hart(target, hartid, GDB_REGNO_TSELECT, tselect); @@ -570,9 +572,8 @@ int riscv_remove_breakpoint(struct target *target, struct trigger trigger; trigger_from_breakpoint(&trigger, breakpoint); int result = remove_trigger(target, &trigger); - if (result != ERROR_OK) { + if (result != ERROR_OK) return result; - } } else { LOG_INFO("OpenOCD only supports hardware and software breakpoints."); @@ -594,7 +595,7 @@ static void trigger_from_watchpoint(struct trigger *trigger, trigger->read = (watchpoint->rw == WPT_READ || watchpoint->rw == WPT_ACCESS); trigger->write = (watchpoint->rw == WPT_WRITE || watchpoint->rw == WPT_ACCESS); trigger->execute = false; - // unique_id is unique across both breakpoints and watchpoints. + /* unique_id is unique across both breakpoints and watchpoints. */ trigger->unique_id = watchpoint->unique_id; } @@ -604,9 +605,8 @@ int riscv_add_watchpoint(struct target *target, struct watchpoint *watchpoint) trigger_from_watchpoint(&trigger, watchpoint); int result = add_trigger(target, &trigger); - if (result != ERROR_OK) { + if (result != ERROR_OK) return result; - } watchpoint->set = true; return ERROR_OK; @@ -619,9 +619,8 @@ int riscv_remove_watchpoint(struct target *target, trigger_from_watchpoint(&trigger, watchpoint); int result = remove_trigger(target, &trigger); - if (result != ERROR_OK) { + if (result != ERROR_OK) return result; - } watchpoint->set = false; return ERROR_OK; @@ -656,7 +655,7 @@ static int riscv_examine(struct target *target) return ERROR_OK; } - // Don't need to select dbus, since the first thing we do is read dtmcontrol. + /* Don't need to select dbus, since the first thing we do is read dtmcontrol. */ riscv_info_t *info = (riscv_info_t *) target->arch_info; uint32_t dtmcontrol = dtmcontrol_scan(target, 0); @@ -735,9 +734,19 @@ static int old_or_new_riscv_resume( return riscv_openocd_resume(target, current, address, handle_breakpoints, debug_execution); } +static void riscv_select_current_hart(struct target *target) +{ + RISCV_INFO(r); + if (r->rtos_hartid != -1 && riscv_rtos_enabled(target)) + riscv_set_current_hartid(target, r->rtos_hartid); + else + riscv_set_current_hartid(target, target->coreid); +} + static int riscv_read_memory(struct target *target, target_addr_t address, uint32_t size, uint32_t count, uint8_t *buffer) { + riscv_select_current_hart(target); struct target_type *tt = get_target_type(target); return tt->read_memory(target, address, size, count, buffer); } @@ -745,6 +754,7 @@ static int riscv_read_memory(struct target *target, target_addr_t address, static int riscv_write_memory(struct target *target, target_addr_t address, uint32_t size, uint32_t count, const uint8_t *buffer) { + riscv_select_current_hart(target); struct target_type *tt = get_target_type(target); return tt->write_memory(target, address, size, count, buffer); } @@ -757,17 +767,19 @@ static int riscv_get_gdb_reg_list(struct target *target, LOG_DEBUG("reg_class=%d", reg_class); LOG_DEBUG("rtos_hartid=%d current_hartid=%d", r->rtos_hartid, r->current_hartid); - if (r->rtos_hartid != -1 && riscv_rtos_enabled(target)) - riscv_set_current_hartid(target, r->rtos_hartid); - else - riscv_set_current_hartid(target, target->coreid); + if (!target->reg_cache) { + LOG_ERROR("Target not initialized. Return ERROR_FAIL."); + return ERROR_FAIL; + } + + riscv_select_current_hart(target); switch (reg_class) { case REG_CLASS_GENERAL: *reg_list_size = 32; break; case REG_CLASS_ALL: - *reg_list_size = REG_COUNT; + *reg_list_size = GDB_REGNO_COUNT; break; default: LOG_ERROR("Unsupported reg_class: %d", reg_class); @@ -775,17 +787,12 @@ static int riscv_get_gdb_reg_list(struct target *target, } *reg_list = calloc(*reg_list_size, sizeof(struct reg *)); - if (!*reg_list) { + if (!*reg_list) return ERROR_FAIL; - } - - if (!target->reg_cache) { - LOG_ERROR("Target not initialized. Return ERROR_FAIL."); - return ERROR_FAIL; - } - + for (int i = 0; i < *reg_list_size; i++) { - assert(target->reg_cache->reg_list[i].size > 0); + assert(!target->reg_cache->reg_list[i].valid || + target->reg_cache->reg_list[i].size > 0); (*reg_list)[i] = &target->reg_cache->reg_list[i]; } @@ -798,7 +805,7 @@ static int riscv_arch_state(struct target *target) return tt->arch_state(target); } -// Algorithm must end with a software breakpoint instruction. +/* Algorithm must end with a software breakpoint instruction. */ static int riscv_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, @@ -816,11 +823,10 @@ static int riscv_run_algorithm(struct target *target, int num_mem_params, return ERROR_TARGET_NOT_HALTED; } - /// Save registers + /* Save registers */ struct reg *reg_pc = register_get_by_name(target->reg_cache, "pc", 1); - if (!reg_pc || reg_pc->type->get(reg_pc) != ERROR_OK) { + if (!reg_pc || reg_pc->type->get(reg_pc) != ERROR_OK) return ERROR_FAIL; - } uint64_t saved_pc = buf_get_u64(reg_pc->value, 0, reg_pc->size); uint64_t saved_regs[32]; @@ -838,30 +844,31 @@ static int riscv_run_algorithm(struct target *target, int num_mem_params, return ERROR_FAIL; } - if (r->number > REG_XPR31) { + if (r->number > GDB_REGNO_XPR31) { LOG_ERROR("Only GPRs can be use as argument registers."); return ERROR_FAIL; } - if (r->type->get(r) != ERROR_OK) { + if (r->type->get(r) != ERROR_OK) return ERROR_FAIL; - } saved_regs[r->number] = buf_get_u64(r->value, 0, r->size); - if (r->type->set(r, reg_params[i].value) != ERROR_OK) { + if (r->type->set(r, reg_params[i].value) != ERROR_OK) return ERROR_FAIL; - } } - // Disable Interrupts before attempting to run the algorithm. + /* Disable Interrupts before attempting to run the algorithm. */ uint64_t current_mstatus; uint8_t mstatus_bytes[8]; LOG_DEBUG("Disabling Interrupts"); - char mstatus_name[20]; - sprintf(mstatus_name, "csr%d", CSR_MSTATUS); struct reg *reg_mstatus = register_get_by_name(target->reg_cache, - mstatus_name, 1); + "mstatus", 1); + if (!reg_mstatus) { + LOG_ERROR("Couldn't find mstatus!"); + return ERROR_FAIL; + } + reg_mstatus->type->get(reg_mstatus); current_mstatus = buf_get_u64(reg_mstatus->value, 0, reg_mstatus->size); uint64_t ie_mask = MSTATUS_MIE | MSTATUS_HIE | MSTATUS_SIE | MSTATUS_UIE; @@ -870,11 +877,10 @@ static int riscv_run_algorithm(struct target *target, int num_mem_params, reg_mstatus->type->set(reg_mstatus, mstatus_bytes); - /// Run algorithm + /* Run algorithm */ LOG_DEBUG("resume at 0x%" TARGET_PRIxADDR, entry_point); - if (oldriscv_resume(target, 0, entry_point, 0, 0) != ERROR_OK) { + if (oldriscv_resume(target, 0, entry_point, 0, 0) != ERROR_OK) return ERROR_FAIL; - } int64_t start = timeval_ms(); while (target->state != TARGET_HALTED) { @@ -890,14 +896,12 @@ static int riscv_run_algorithm(struct target *target, int num_mem_params, } int result = old_or_new_riscv_poll(target); - if (result != ERROR_OK) { + if (result != ERROR_OK) return result; - } } - if (reg_pc->type->get(reg_pc) != ERROR_OK) { + if (reg_pc->type->get(reg_pc) != ERROR_OK) return ERROR_FAIL; - } uint64_t final_pc = buf_get_u64(reg_pc->value, 0, reg_pc->size); if (final_pc != exit_point) { LOG_ERROR("PC ended up at 0x%" PRIx64 " instead of 0x%" @@ -905,37 +909,35 @@ static int riscv_run_algorithm(struct target *target, int num_mem_params, return ERROR_FAIL; } - // Restore Interrupts + /* Restore Interrupts */ LOG_DEBUG("Restoring Interrupts"); buf_set_u64(mstatus_bytes, 0, info->xlen[0], current_mstatus); reg_mstatus->type->set(reg_mstatus, mstatus_bytes); - /// Restore registers + /* Restore registers */ uint8_t buf[8]; buf_set_u64(buf, 0, info->xlen[0], saved_pc); - if (reg_pc->type->set(reg_pc, buf) != ERROR_OK) { + if (reg_pc->type->set(reg_pc, buf) != ERROR_OK) return ERROR_FAIL; - } for (int i = 0; i < num_reg_params; i++) { LOG_DEBUG("restore %s", reg_params[i].reg_name); struct reg *r = register_get_by_name(target->reg_cache, reg_params[i].reg_name, 0); buf_set_u64(buf, 0, info->xlen[0], saved_regs[r->number]); - if (r->type->set(r, buf) != ERROR_OK) { + if (r->type->set(r, buf) != ERROR_OK) return ERROR_FAIL; - } } return ERROR_OK; } -/* Should run code on the target to perform CRC of +/* Should run code on the target to perform CRC of memory. Not yet implemented. */ static int riscv_checksum_memory(struct target *target, target_addr_t address, uint32_t count, - uint32_t* checksum) + uint32_t *checksum) { *checksum = 0xFFFFFFFF; return ERROR_TARGET_RESOURCE_NOT_AVAILABLE; @@ -946,10 +948,10 @@ block holds all-ones (because this is generally called on NOR flash which is 1 when "blank") Not yet implemented. */ -int riscv_blank_check_memory(struct target * target, +int riscv_blank_check_memory(struct target *target, target_addr_t address, uint32_t count, - uint32_t * blank, + uint32_t *blank, uint8_t erased_value) { *blank = 0; @@ -1031,8 +1033,11 @@ int riscv_openocd_poll(struct target *target) case RISCV_HALT_SINGLESTEP: target->debug_reason = DBG_REASON_SINGLESTEP; break; + case RISCV_HALT_UNKNOWN: + target->debug_reason = DBG_REASON_UNDEFINED; + break; } - + if (riscv_rtos_enabled(target)) { target->rtos->current_threadid = triggered_hart + 1; target->rtos->current_thread = triggered_hart + 1; @@ -1076,9 +1081,8 @@ int riscv_openocd_resume( ) { LOG_DEBUG("resuming all harts"); - if (!current) { + if (!current) riscv_set_register(target, GDB_REGNO_PC, address); - } int out = riscv_resume_all_harts(target); if (out != ERROR_OK) { @@ -1100,9 +1104,8 @@ int riscv_openocd_step( ) { LOG_DEBUG("stepping rtos hart"); - if (!current) { + if (!current) riscv_set_register(target, GDB_REGNO_PC, address); - } int out = riscv_step_rtos_hart(target); if (out != ERROR_OK) { @@ -1120,14 +1123,14 @@ int riscv_openocd_step( } /* Command Handlers */ -COMMAND_HANDLER(riscv_set_command_timeout_sec) { - +COMMAND_HANDLER(riscv_set_command_timeout_sec) +{ if (CMD_ARGC != 1) { LOG_ERROR("Command takes exactly 1 parameter"); return ERROR_COMMAND_SYNTAX_ERROR; } int timeout = atoi(CMD_ARGV[0]); - if (timeout <= 0){ + if (timeout <= 0) { LOG_ERROR("%s is not a valid integer argument for command.", CMD_ARGV[0]); return ERROR_FAIL; } @@ -1137,14 +1140,14 @@ COMMAND_HANDLER(riscv_set_command_timeout_sec) { return ERROR_OK; } -COMMAND_HANDLER(riscv_set_reset_timeout_sec) { - +COMMAND_HANDLER(riscv_set_reset_timeout_sec) +{ if (CMD_ARGC != 1) { - LOG_ERROR("Command takes exactly 1 parameter"); - return ERROR_COMMAND_SYNTAX_ERROR; + LOG_ERROR("Command takes exactly 1 parameter"); + return ERROR_COMMAND_SYNTAX_ERROR; } int timeout = atoi(CMD_ARGV[0]); - if (timeout <= 0){ + if (timeout <= 0) { LOG_ERROR("%s is not a valid integer argument for command.", CMD_ARGV[0]); return ERROR_FAIL; } @@ -1173,6 +1176,111 @@ COMMAND_HANDLER(riscv_test_compliance) { } +COMMAND_HANDLER(riscv_set_scratch_ram) +{ + if (CMD_ARGC != 1) { + LOG_ERROR("Command takes exactly 1 parameter"); + return ERROR_COMMAND_SYNTAX_ERROR; + } + if (!strcmp(CMD_ARGV[0], "none")) { + riscv_use_scratch_ram = false; + return ERROR_OK; + } + + long long unsigned int address; + int result = sscanf(CMD_ARGV[0], "%llx", &address); + if (result != (int) strlen(CMD_ARGV[0])) { + LOG_ERROR("%s is not a valid address for command.", CMD_ARGV[0]); + riscv_use_scratch_ram = false; + return ERROR_FAIL; + } + + riscv_scratch_ram_address = address; + riscv_use_scratch_ram = true; + return ERROR_OK; +} + +void parse_error(const char *string, char c, unsigned position) +{ + char buf[position+2]; + for (unsigned i = 0; i < position; i++) + buf[i] = ' '; + buf[position] = '^'; + buf[position + 1] = 0; + + LOG_ERROR("Parse error at character %c in:", c); + LOG_ERROR("%s", string); + LOG_ERROR("%s", buf); +} + +COMMAND_HANDLER(riscv_set_expose_csrs) +{ + if (CMD_ARGC != 1) { + LOG_ERROR("Command takes exactly 1 parameter"); + return ERROR_COMMAND_SYNTAX_ERROR; + } + + for (unsigned pass = 0; pass < 2; pass++) { + unsigned range = 0; + unsigned low = 0; + bool parse_low = true; + unsigned high = 0; + for (unsigned i = 0; i == 0 || CMD_ARGV[0][i-1]; i++) { + char c = CMD_ARGV[0][i]; + if (isspace(c)) { + /* Ignore whitespace. */ + continue; + } + + if (parse_low) { + if (isdigit(c)) { + low *= 10; + low += c - '0'; + } else if (c == '-') { + parse_low = false; + } else if (c == ',' || c == 0) { + if (pass == 1) { + expose_csr[range].low = low; + expose_csr[range].high = low; + } + low = 0; + range++; + } else { + parse_error(CMD_ARGV[0], c, i); + return ERROR_COMMAND_SYNTAX_ERROR; + } + + } else { + if (isdigit(c)) { + high *= 10; + high += c - '0'; + } else if (c == ',' || c == 0) { + parse_low = true; + if (pass == 1) { + expose_csr[range].low = low; + expose_csr[range].high = high; + } + low = 0; + high = 0; + range++; + } else { + parse_error(CMD_ARGV[0], c, i); + return ERROR_COMMAND_SYNTAX_ERROR; + } + } + } + + if (pass == 0) { + if (expose_csr) + free(expose_csr); + expose_csr = calloc(range + 2, sizeof(*expose_csr)); + } else { + expose_csr[range].low = 1; + expose_csr[range].high = 0; + } + } + return ERROR_OK; +} static const struct command_registration riscv_exec_command_handlers[] = { { @@ -1188,14 +1296,30 @@ static const struct command_registration riscv_exec_command_handlers[] = { .mode = COMMAND_ANY, .usage = "riscv set_command_timeout_sec [sec]", .help = "Set the wall-clock timeout (in seconds) for individual commands" - }, - { + }, + { .name = "set_reset_timeout_sec", .handler = riscv_set_reset_timeout_sec, .mode = COMMAND_ANY, .usage = "riscv set_reset_timeout_sec [sec]", .help = "Set the wall-clock timeout (in seconds) after reset is deasserted" }, + { + .name = "set_scratch_ram", + .handler = riscv_set_scratch_ram, + .mode = COMMAND_ANY, + .usage = "riscv set_scratch_ram none|[address]", + .help = "Set address of 16 bytes of scratch RAM the debugger can use, or 'none'." + }, + { + .name = "expose_csrs", + .handler = riscv_set_expose_csrs, + .mode = COMMAND_ANY, + .usage = "riscv expose_csrs n0[-m0][,n0[-m0]]...", + .help = "Configure a list of inclusive ranges for CSRs to expose in " + "addition to the standard ones. This must be executed before " + "`init`." + }, COMMAND_REGISTRATION_DONE }; @@ -1210,8 +1334,7 @@ const struct command_registration riscv_command_handlers[] = { COMMAND_REGISTRATION_DONE }; -struct target_type riscv_target = -{ +struct target_type riscv_target = { .name = "riscv", .init_target = riscv_init_target, @@ -1262,7 +1385,6 @@ void riscv_info_init(struct target *target, riscv_info_t *r) for (size_t h = 0; h < RISCV_MAX_HARTS; ++h) { r->xlen[h] = -1; - r->debug_buffer_addr[h] = -1; for (size_t e = 0; e < RISCV_MAX_REGISTERS; ++e) r->valid_saved_registers[h][e] = false; @@ -1291,8 +1413,7 @@ int riscv_halt_one_hart(struct target *target, int hartid) return ERROR_OK; } - r->halt_current_hart(target); - return ERROR_OK; + return r->halt_current_hart(target); } int riscv_resume_all_harts(struct target *target) @@ -1319,8 +1440,7 @@ int riscv_resume_one_hart(struct target *target, int hartid) } r->on_resume(target); - r->resume_current_hart(target); - return ERROR_OK; + return r->resume_current_hart(target); } int riscv_step_rtos_hart(struct target *target) @@ -1337,16 +1457,36 @@ int riscv_step_rtos_hart(struct target *target) riscv_set_current_hartid(target, hartid); LOG_DEBUG("stepping hart %d", hartid); - assert(riscv_is_halted(target)); + if (!riscv_is_halted(target)) { + LOG_ERROR("Hart isn't halted before single step!"); + return ERROR_FAIL; + } riscv_invalidate_register_cache(target); r->on_step(target); - r->step_current_hart(target); + if (r->step_current_hart(target) != ERROR_OK) + return ERROR_FAIL; riscv_invalidate_register_cache(target); r->on_halt(target); - assert(riscv_is_halted(target)); + if (!riscv_is_halted(target)) { + LOG_ERROR("Hart was not halted after single step!"); + return ERROR_FAIL; + } return ERROR_OK; } +bool riscv_supports_extension(struct target *target, char letter) +{ + RISCV_INFO(r); + unsigned num; + if (letter >= 'a' && letter <= 'z') + num = letter - 'a'; + else if (letter >= 'A' && letter <= 'Z') + num = letter - 'A'; + else + return false; + return r->misa & (1 << num); +} + int riscv_xlen(const struct target *target) { return riscv_xlen_of_hart(target, riscv_current_hartid(target)); @@ -1384,7 +1524,7 @@ void riscv_set_current_hartid(struct target *target, int hartid) /* Avoid invalidating the register cache all the time. */ if (r->registers_initialized && (!riscv_rtos_enabled(target) || (previous_hartid == hartid)) - && target->reg_cache->reg_list[GDB_REGNO_XPR0].size == (unsigned)riscv_xlen(target) + && target->reg_cache->reg_list[GDB_REGNO_ZERO].size == (unsigned)riscv_xlen(target) && (!riscv_rtos_enabled(target) || (r->rtos_hartid != -1))) { return; } else @@ -1397,22 +1537,10 @@ void riscv_invalidate_register_cache(struct target *target) { RISCV_INFO(r); - /* Update the register list's widths. */ register_cache_invalidate(target->reg_cache); for (size_t i = 0; i < GDB_REGNO_COUNT; ++i) { struct reg *reg = &target->reg_cache->reg_list[i]; - - reg->value = &r->reg_cache_values[i]; reg->valid = false; - - switch (i) { - case GDB_REGNO_PRIV: - reg->size = 8; - break; - default: - reg->size = riscv_xlen(target); - break; - } } r->registers_initialized = true; @@ -1439,9 +1567,11 @@ void riscv_set_rtos_hartid(struct target *target, int hartid) int riscv_count_harts(struct target *target) { - if (target == NULL) return 1; + if (target == NULL) + return 1; RISCV_INFO(r); - if (r == NULL) return 1; + if (r == NULL) + return 1; return r->hart_count; } @@ -1450,12 +1580,12 @@ bool riscv_has_register(struct target *target, int hartid, int regid) return 1; } -void riscv_set_register(struct target *target, enum gdb_regno r, riscv_reg_t v) +int riscv_set_register(struct target *target, enum gdb_regno r, riscv_reg_t v) { return riscv_set_register_on_hart(target, riscv_current_hartid(target), r, v); } -void riscv_set_register_on_hart(struct target *target, int hartid, +int riscv_set_register_on_hart(struct target *target, int hartid, enum gdb_regno regid, uint64_t value) { RISCV_INFO(r); @@ -1464,17 +1594,20 @@ void riscv_set_register_on_hart(struct target *target, int hartid, return r->set_register(target, hartid, regid, value); } -riscv_reg_t riscv_get_register(struct target *target, enum gdb_regno r) +int riscv_get_register(struct target *target, riscv_reg_t *value, + enum gdb_regno r) { - return riscv_get_register_on_hart(target, riscv_current_hartid(target), r); + return riscv_get_register_on_hart(target, value, + riscv_current_hartid(target), r); } -uint64_t riscv_get_register_on_hart(struct target *target, int hartid, enum gdb_regno regid) +int riscv_get_register_on_hart(struct target *target, riscv_reg_t *value, + int hartid, enum gdb_regno regid) { RISCV_INFO(r); - uint64_t value = r->get_register(target, hartid, regid); - LOG_DEBUG("[%d] %s: %" PRIx64, hartid, gdb_regno_name(regid), value); - return value; + int result = r->get_register(target, value, hartid, regid); + LOG_DEBUG("[%d] %s: %" PRIx64, hartid, gdb_regno_name(regid), *value); + return result; } bool riscv_is_halted(struct target *target) @@ -1488,7 +1621,10 @@ enum riscv_halt_reason riscv_halt_reason(struct target *target, int hartid) { RISCV_INFO(r); riscv_set_current_hartid(target, hartid); - assert(riscv_is_halted(target)); + if (!riscv_is_halted(target)) { + LOG_ERROR("Hart is not halted!"); + return RISCV_HALT_UNKNOWN; + } return r->halt_reason(target); } @@ -1510,28 +1646,6 @@ size_t riscv_debug_buffer_size(struct target *target) return r->debug_buffer_size[riscv_current_hartid(target)]; } -riscv_addr_t riscv_debug_buffer_addr(struct target *target) -{ - RISCV_INFO(r); - riscv_addr_t out = r->debug_buffer_addr[riscv_current_hartid(target)]; - assert((out & 3) == 0); - return out; -} - -int riscv_debug_buffer_enter(struct target *target, struct riscv_program *program) -{ - RISCV_INFO(r); - r->debug_buffer_enter(target, program); - return ERROR_OK; -} - -int riscv_debug_buffer_leave(struct target *target, struct riscv_program *program) -{ - RISCV_INFO(r); - r->debug_buffer_leave(target, program); - return ERROR_OK; -} - int riscv_write_debug_buffer(struct target *target, int index, riscv_insn_t insn) { RISCV_INFO(r); @@ -1545,22 +1659,6 @@ riscv_insn_t riscv_read_debug_buffer(struct target *target, int index) return r->read_debug_buffer(target, index); } -riscv_addr_t riscv_read_debug_buffer_x(struct target *target, int index) -{ - riscv_addr_t out = 0; - switch (riscv_xlen(target)) { - case 64: - out |= (uint64_t)riscv_read_debug_buffer(target, index + 1) << 32; - case 32: - out |= riscv_read_debug_buffer(target, index + 0) << 0; - break; - default: - LOG_ERROR("unsupported XLEN %d", riscv_xlen(target)); - abort(); - } - return out; -} - int riscv_execute_debug_buffer(struct target *target) { RISCV_INFO(r); @@ -1615,33 +1713,42 @@ int riscv_enumerate_triggers(struct target *target) if (!riscv_hart_enabled(target, hartid)) continue; - riscv_reg_t tselect = riscv_get_register_on_hart(target, hartid, + riscv_reg_t tselect; + int result = riscv_get_register_on_hart(target, &tselect, hartid, GDB_REGNO_TSELECT); + if (result != ERROR_OK) + return result; for (unsigned t = 0; t < RISCV_MAX_TRIGGERS; ++t) { r->trigger_count[hartid] = t; riscv_set_register_on_hart(target, hartid, GDB_REGNO_TSELECT, t); - uint64_t tselect_rb = riscv_get_register_on_hart(target, hartid, + uint64_t tselect_rb; + result = riscv_get_register_on_hart(target, &tselect_rb, hartid, GDB_REGNO_TSELECT); - // Mask off the top bit, which is used as tdrmode in old - // implementations. + if (result != ERROR_OK) + return result; + /* Mask off the top bit, which is used as tdrmode in old + * implementations. */ tselect_rb &= ~(1ULL << (riscv_xlen(target)-1)); if (tselect_rb != t) break; - uint64_t tdata1 = riscv_get_register_on_hart(target, hartid, + uint64_t tdata1; + result = riscv_get_register_on_hart(target, &tdata1, hartid, GDB_REGNO_TDATA1); + if (result != ERROR_OK) + return result; + int type = get_field(tdata1, MCONTROL_TYPE(riscv_xlen(target))); switch (type) { case 1: - // On these older cores we don't support software using - // triggers. + /* On these older cores we don't support software using + * triggers. */ riscv_set_register_on_hart(target, hartid, GDB_REGNO_TDATA1, 0); break; case 2: - if (tdata1 & MCONTROL_DMODE(riscv_xlen(target))) { + if (tdata1 & MCONTROL_DMODE(riscv_xlen(target))) riscv_set_register_on_hart(target, hartid, GDB_REGNO_TDATA1, 0); - } break; } } @@ -1692,15 +1799,409 @@ const char *gdb_regno_name(enum gdb_regno regno) case GDB_REGNO_PRIV: return "priv"; default: - if (regno <= GDB_REGNO_XPR31) { - sprintf(buf, "x%d", regno - GDB_REGNO_XPR0); - } else if (regno >= GDB_REGNO_CSR0 && regno <= GDB_REGNO_CSR4095) { + if (regno <= GDB_REGNO_XPR31) + sprintf(buf, "x%d", regno - GDB_REGNO_ZERO); + else if (regno >= GDB_REGNO_CSR0 && regno <= GDB_REGNO_CSR4095) sprintf(buf, "csr%d", regno - GDB_REGNO_CSR0); - } else if (regno >= GDB_REGNO_FPR0 && regno <= GDB_REGNO_FPR31) { + else if (regno >= GDB_REGNO_FPR0 && regno <= GDB_REGNO_FPR31) sprintf(buf, "f%d", regno - GDB_REGNO_FPR0); - } else { + else sprintf(buf, "gdb_regno_%d", regno); - } return buf; } } + +static int register_get(struct reg *reg) +{ + struct target *target = (struct target *) reg->arch_info; + uint64_t value; + int result = riscv_get_register(target, &value, reg->number); + if (result != ERROR_OK) + return result; + buf_set_u64(reg->value, 0, reg->size, value); + return ERROR_OK; +} + +static int register_set(struct reg *reg, uint8_t *buf) +{ + struct target *target = (struct target *) reg->arch_info; + + uint64_t value = buf_get_u64(buf, 0, reg->size); + + LOG_DEBUG("write 0x%" PRIx64 " to %s", value, reg->name); + struct reg *r = &target->reg_cache->reg_list[reg->number]; + r->valid = true; + memcpy(r->value, buf, (r->size + 7) / 8); + + riscv_set_register(target, reg->number, value); + return ERROR_OK; +} + +static struct reg_arch_type riscv_reg_arch_type = { + .get = register_get, + .set = register_set +}; + +struct csr_info { + unsigned number; + const char *name; +}; + +static int cmp_csr_info(const void *p1, const void *p2) +{ + return (int) (((struct csr_info *)p1)->number) - (int) (((struct csr_info *)p2)->number); +} + +int riscv_init_registers(struct target *target) +{ + RISCV_INFO(info); + + if (target->reg_cache) { + if (target->reg_cache->reg_list) + free(target->reg_cache->reg_list); + free(target->reg_cache); + } + + target->reg_cache = calloc(1, sizeof(*target->reg_cache)); + target->reg_cache->name = "RISC-V Registers"; + target->reg_cache->num_regs = GDB_REGNO_COUNT; + + target->reg_cache->reg_list = calloc(GDB_REGNO_COUNT, sizeof(struct reg)); + + const unsigned int max_reg_name_len = 12; + if (info->reg_names) + free(info->reg_names); + info->reg_names = calloc(1, GDB_REGNO_COUNT * max_reg_name_len); + char *reg_name = info->reg_names; + + static struct reg_feature feature_cpu = { + .name = "org.gnu.gdb.riscv.cpu" + }; + static struct reg_feature feature_fpu = { + .name = "org.gnu.gdb.riscv.fpu" + }; + static struct reg_feature feature_csr = { + .name = "org.gnu.gdb.riscv.csr" + }; + static struct reg_feature feature_virtual = { + .name = "org.gnu.gdb.riscv.virtual" + }; + + static struct reg_data_type type_ieee_single = { + .type = REG_TYPE_IEEE_SINGLE, + .id = "ieee_single" + }; + static struct reg_data_type type_ieee_double = { + .type = REG_TYPE_IEEE_DOUBLE, + .id = "ieee_double" + }; + struct csr_info csr_info[] = { +#define DECLARE_CSR(name, number) { number, #name }, +#include "encoding.h" +#undef DECLARE_CSR + }; + /* encoding.h does not contain the registers in sorted order. */ + qsort(csr_info, DIM(csr_info), sizeof(*csr_info), cmp_csr_info); + unsigned csr_info_index = 0; + + /* When gdb request register N, gdb_get_register_packet() assumes that this + * is register at index N in reg_list. So if there are certain registers + * that don't exist, we need to leave holes in the list (or renumber, but + * it would be nice not to have yet another set of numbers to translate + * between). */ + for (uint32_t number = 0; number < GDB_REGNO_COUNT; number++) { + struct reg *r = &target->reg_cache->reg_list[number]; + r->caller_save = true; + r->dirty = false; + r->valid = false; + r->exist = true; + r->type = &riscv_reg_arch_type; + r->arch_info = target; + r->number = number; + r->size = riscv_xlen(target); + /* r->size is set in riscv_invalidate_register_cache, maybe because the + * target is in theory allowed to change XLEN on us. But I expect a lot + * of other things to break in that case as well. */ + if (number <= GDB_REGNO_XPR31) { + switch (number) { + case GDB_REGNO_ZERO: + r->name = "zero"; + break; + case GDB_REGNO_RA: + r->name = "ra"; + break; + case GDB_REGNO_SP: + r->name = "sp"; + break; + case GDB_REGNO_GP: + r->name = "gp"; + break; + case GDB_REGNO_TP: + r->name = "tp"; + break; + case GDB_REGNO_T0: + r->name = "t0"; + break; + case GDB_REGNO_T1: + r->name = "t1"; + break; + case GDB_REGNO_T2: + r->name = "t2"; + break; + case GDB_REGNO_FP: + r->name = "fp"; + break; + case GDB_REGNO_S1: + r->name = "s1"; + break; + case GDB_REGNO_A0: + r->name = "a0"; + break; + case GDB_REGNO_A1: + r->name = "a1"; + break; + case GDB_REGNO_A2: + r->name = "a2"; + break; + case GDB_REGNO_A3: + r->name = "a3"; + break; + case GDB_REGNO_A4: + r->name = "a4"; + break; + case GDB_REGNO_A5: + r->name = "a5"; + break; + case GDB_REGNO_A6: + r->name = "a6"; + break; + case GDB_REGNO_A7: + r->name = "a7"; + break; + case GDB_REGNO_S2: + r->name = "s2"; + break; + case GDB_REGNO_S3: + r->name = "s3"; + break; + case GDB_REGNO_S4: + r->name = "s4"; + break; + case GDB_REGNO_S5: + r->name = "s5"; + break; + case GDB_REGNO_S6: + r->name = "s6"; + break; + case GDB_REGNO_S7: + r->name = "s7"; + break; + case GDB_REGNO_S8: + r->name = "s8"; + break; + case GDB_REGNO_S9: + r->name = "s9"; + break; + case GDB_REGNO_S10: + r->name = "s10"; + break; + case GDB_REGNO_S11: + r->name = "s11"; + break; + case GDB_REGNO_T3: + r->name = "t3"; + break; + case GDB_REGNO_T4: + r->name = "t4"; + break; + case GDB_REGNO_T5: + r->name = "t5"; + break; + case GDB_REGNO_T6: + r->name = "t6"; + break; + } + r->group = "general"; + r->feature = &feature_cpu; + } else if (number == GDB_REGNO_PC) { + sprintf(reg_name, "pc"); + r->group = "general"; + r->feature = &feature_cpu; + } else if (number >= GDB_REGNO_FPR0 && number <= GDB_REGNO_FPR31) { + if (riscv_supports_extension(target, 'D')) { + r->reg_data_type = &type_ieee_double; + r->size = 64; + } else if (riscv_supports_extension(target, 'F')) { + r->reg_data_type = &type_ieee_single; + r->size = 32; + } else { + r->exist = false; + } + switch (number) { + case GDB_REGNO_FT0: + r->name = "ft0"; + break; + case GDB_REGNO_FT1: + r->name = "ft1"; + break; + case GDB_REGNO_FT2: + r->name = "ft2"; + break; + case GDB_REGNO_FT3: + r->name = "ft3"; + break; + case GDB_REGNO_FT4: + r->name = "ft4"; + break; + case GDB_REGNO_FT5: + r->name = "ft5"; + break; + case GDB_REGNO_FT6: + r->name = "ft6"; + break; + case GDB_REGNO_FT7: + r->name = "ft7"; + break; + case GDB_REGNO_FS0: + r->name = "fs0"; + break; + case GDB_REGNO_FS1: + r->name = "fs1"; + break; + case GDB_REGNO_FA0: + r->name = "fa0"; + break; + case GDB_REGNO_FA1: + r->name = "fa1"; + break; + case GDB_REGNO_FA2: + r->name = "fa2"; + break; + case GDB_REGNO_FA3: + r->name = "fa3"; + break; + case GDB_REGNO_FA4: + r->name = "fa4"; + break; + case GDB_REGNO_FA5: + r->name = "fa5"; + break; + case GDB_REGNO_FA6: + r->name = "fa6"; + break; + case GDB_REGNO_FA7: + r->name = "fa7"; + break; + case GDB_REGNO_FS2: + r->name = "fs2"; + break; + case GDB_REGNO_FS3: + r->name = "fs3"; + break; + case GDB_REGNO_FS4: + r->name = "fs4"; + break; + case GDB_REGNO_FS5: + r->name = "fs5"; + break; + case GDB_REGNO_FS6: + r->name = "fs6"; + break; + case GDB_REGNO_FS7: + r->name = "fs7"; + break; + case GDB_REGNO_FS8: + r->name = "fs8"; + break; + case GDB_REGNO_FS9: + r->name = "fs9"; + break; + case GDB_REGNO_FS10: + r->name = "fs10"; + break; + case GDB_REGNO_FS11: + r->name = "fs11"; + break; + case GDB_REGNO_FT8: + r->name = "ft8"; + break; + case GDB_REGNO_FT9: + r->name = "ft9"; + break; + case GDB_REGNO_FT10: + r->name = "ft10"; + break; + case GDB_REGNO_FT11: + r->name = "ft11"; + break; + } + r->group = "float"; + r->feature = &feature_fpu; + } else if (number >= GDB_REGNO_CSR0 && number <= GDB_REGNO_CSR4095) { + r->group = "csr"; + r->feature = &feature_csr; + unsigned csr_number = number - GDB_REGNO_CSR0; + + while (csr_info[csr_info_index].number < csr_number && + csr_info_index < DIM(csr_info) - 1) { + csr_info_index++; + } + if (csr_info[csr_info_index].number == csr_number) { + r->name = csr_info[csr_info_index].name; + } else { + sprintf(reg_name, "csr%d", csr_number); + /* Assume unnamed registers don't exist, unless we have some + * configuration that tells us otherwise. That's important + * because eg. Eclipse crashes if a target has too many + * registers, and apparently has no way of only showing a + * subset of registers in any case. */ + r->exist = false; + } + + switch (csr_number) { + case CSR_FFLAGS: + case CSR_FRM: + case CSR_FCSR: + r->exist = riscv_supports_extension(target, 'F'); + r->group = "float"; + r->feature = &feature_fpu; + break; + case CSR_SSTATUS: + case CSR_STVEC: + case CSR_SIP: + case CSR_SIE: + case CSR_SCOUNTEREN: + case CSR_SSCRATCH: + case CSR_SEPC: + case CSR_SCAUSE: + case CSR_STVAL: + case CSR_SATP: + r->exist = riscv_supports_extension(target, 'S'); + break; + } + + if (!r->exist && expose_csr) { + for (unsigned i = 0; expose_csr[i].low <= expose_csr[i].high; i++) { + if (csr_number >= expose_csr[i].low && csr_number <= expose_csr[i].high) { + LOG_INFO("Exposing additional CSR %d", csr_number); + r->exist = true; + break; + } + } + } + + } else if (number == GDB_REGNO_PRIV) { + sprintf(reg_name, "priv"); + r->group = "general"; + r->feature = &feature_virtual; + r->size = 8; + } + if (reg_name[0]) + r->name = reg_name; + reg_name += strlen(reg_name) + 1; + assert(reg_name < info->reg_names + GDB_REGNO_COUNT * max_reg_name_len); + r->value = &info->reg_cache_values[number]; + } + + return ERROR_OK; +} diff --git a/src/target/riscv/riscv.h b/src/target/riscv/riscv.h index 93bbd77a7..2ff087f9e 100644 --- a/src/target/riscv/riscv.h +++ b/src/target/riscv/riscv.h @@ -30,6 +30,7 @@ enum riscv_halt_reason { RISCV_HALT_INTERRUPT, RISCV_HALT_BREAKPOINT, RISCV_HALT_SINGLESTEP, + RISCV_HALT_UNKNOWN }; typedef struct { @@ -59,7 +60,11 @@ typedef struct { /* The register cache points into here. */ uint64_t reg_cache_values[RISCV_MAX_REGISTERS]; - + + /* Single buffer that contains all register names, instead of calling + * malloc for each register. Needs to be freed when reg_list is freed. */ + char *reg_names; + /* It's possible that each core has a different supported ISA set. */ int xlen[RISCV_MAX_HARTS]; @@ -72,32 +77,31 @@ typedef struct { * target controls, while otherwise only a single hart is controlled. */ int trigger_unique_id[RISCV_MAX_HWBPS]; - /* The address of the debug RAM buffer. */ - riscv_addr_t debug_buffer_addr[RISCV_MAX_HARTS]; - /* The number of entries in the debug buffer. */ int debug_buffer_size[RISCV_MAX_HARTS]; /* This avoids invalidating the register cache too often. */ bool registers_initialized; + /* This hart contains an implicit ebreak at the end of the program buffer. */ + bool impebreak; + /* Helper functions that target the various RISC-V debug spec * implementations. */ - riscv_reg_t (*get_register)(struct target *, int hartid, int regid); - void (*set_register)(struct target *, int hartid, int regid, + int (*get_register)(struct target *target, + riscv_reg_t *value, int hid, int rid); + int (*set_register)(struct target *, int hartid, int regid, uint64_t value); void (*select_current_hart)(struct target *); bool (*is_halted)(struct target *target); - void (*halt_current_hart)(struct target *); - void (*resume_current_hart)(struct target *target); - void (*step_current_hart)(struct target *target); - void (*on_halt)(struct target *target); - void (*on_resume)(struct target *target); - void (*on_step)(struct target *target); + int (*halt_current_hart)(struct target *); + int (*resume_current_hart)(struct target *target); + int (*step_current_hart)(struct target *target); + int (*on_halt)(struct target *target); + int (*on_resume)(struct target *target); + int (*on_step)(struct target *target); enum riscv_halt_reason (*halt_reason)(struct target *target); - void (*debug_buffer_enter)(struct target *target, struct riscv_program *program); - void (*debug_buffer_leave)(struct target *target, struct riscv_program *program); - void (*write_debug_buffer)(struct target *target, unsigned index, + int (*write_debug_buffer)(struct target *target, unsigned index, riscv_insn_t d); riscv_insn_t (*read_debug_buffer)(struct target *target, unsigned index); int (*execute_debug_buffer)(struct target *target); @@ -115,6 +119,9 @@ extern int riscv_command_timeout_sec; /* Wall-clock timeout after reset. Settable via RISC-V Target commands.*/ extern int riscv_reset_timeout_sec; +extern bool riscv_use_scratch_ram; +extern uint64_t riscv_scratch_ram_address; + /* Everything needs the RISC-V specific info structure, so here's a nice macro * that provides that. */ static inline riscv_info_t *riscv_info(const struct target *target) __attribute__((unused)); @@ -138,7 +145,7 @@ int riscv_openocd_resume( struct target *target, int current, target_addr_t address, - int handle_breakpoints, + int handle_breakpoints, int debug_execution ); @@ -169,6 +176,8 @@ int riscv_resume_one_hart(struct target *target, int hartid); * then the only hart. */ int riscv_step_rtos_hart(struct target *target); +bool riscv_supports_extension(struct target *target, char letter); + /* Returns XLEN for the given (or current) hart. */ int riscv_xlen(const struct target *target); int riscv_xlen_of_hart(const struct target *target, int hartid); @@ -197,10 +206,12 @@ bool riscv_has_register(struct target *target, int hartid, int regid); /* Returns the value of the given register on the given hart. 32-bit registers * are zero extended to 64 bits. */ -void riscv_set_register(struct target *target, enum gdb_regno i, riscv_reg_t v); -void riscv_set_register_on_hart(struct target *target, int hid, enum gdb_regno rid, uint64_t v); -riscv_reg_t riscv_get_register(struct target *target, enum gdb_regno i); -riscv_reg_t riscv_get_register_on_hart(struct target *target, int hid, enum gdb_regno rid); +int riscv_set_register(struct target *target, enum gdb_regno i, riscv_reg_t v); +int riscv_set_register_on_hart(struct target *target, int hid, enum gdb_regno rid, uint64_t v); +int riscv_get_register(struct target *target, riscv_reg_t *value, + enum gdb_regno r); +int riscv_get_register_on_hart(struct target *target, riscv_reg_t *value, + int hartid, enum gdb_regno regid); /* Checks the state of the current hart -- "is_halted" checks the actual * on-device register. */ @@ -215,15 +226,9 @@ int riscv_count_triggers_of_hart(struct target *target, int hartid); /* These helper functions let the generic program interface get target-specific * information. */ size_t riscv_debug_buffer_size(struct target *target); -riscv_addr_t riscv_debug_buffer_addr(struct target *target); - -int riscv_debug_buffer_enter(struct target *target, struct riscv_program *program); -int riscv_debug_buffer_leave(struct target *target, struct riscv_program *program); riscv_insn_t riscv_read_debug_buffer(struct target *target, int index); -riscv_addr_t riscv_read_debug_buffer_x(struct target *target, int index); int riscv_write_debug_buffer(struct target *target, int index, riscv_insn_t insn); -int riscv_write_debug_buffer_x(struct target *target, int index, riscv_addr_t data); int riscv_execute_debug_buffer(struct target *target); void riscv_fill_dmi_nop_u64(struct target *target, char *buf); @@ -246,4 +251,6 @@ int riscv_add_watchpoint(struct target *target, struct watchpoint *watchpoint); int riscv_remove_watchpoint(struct target *target, struct watchpoint *watchpoint); +int riscv_init_registers(struct target *target); + #endif diff --git a/src/target/stm8.c b/src/target/stm8.c new file mode 100644 index 000000000..262497b0d --- /dev/null +++ b/src/target/stm8.c @@ -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 . +*/ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#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; +} + +/* + Disables interrupts. + If interrupts are enabled they are masked and the cc register + is saved. + + 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
*/ + 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
*/ + 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 *)¤t_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(®_params[0], "a", 32, PARAM_IN_OUT); + buf_set_u32(reg_params[0].value, 0, 32, erased_value); + + init_reg_param(®_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(®_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 index 000000000..39fac3e32 --- /dev/null +++ b/src/target/stm8.h @@ -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 . +*/ + +#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 */ diff --git a/src/target/target.c b/src/target/target.c index adedd4709..12457fa57 100644 --- a/src/target/target.c +++ b/src/target/target.c @@ -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; extern struct target_type riscv_target; static struct target_type *target_types[] = { @@ -137,6 +138,7 @@ static struct target_type *target_types[] = { &or1k_target, &quark_x10xx_target, &quark_d20xx_target, + &stm8_target, &riscv_target, #if BUILD_TARGET64 &aarch64_target, @@ -204,10 +206,6 @@ static const Jim_Nvp nvp_target_event[] = { { .value = TARGET_EVENT_RESET_ASSERT_POST, .name = "reset-assert-post" }, { .value = TARGET_EVENT_RESET_DEASSERT_PRE, .name = "reset-deassert-pre" }, { .value = TARGET_EVENT_RESET_DEASSERT_POST, .name = "reset-deassert-post" }, - { .value = TARGET_EVENT_RESET_HALT_PRE, .name = "reset-halt-pre" }, - { .value = TARGET_EVENT_RESET_HALT_POST, .name = "reset-halt-post" }, - { .value = TARGET_EVENT_RESET_WAIT_PRE, .name = "reset-wait-pre" }, - { .value = TARGET_EVENT_RESET_WAIT_POST, .name = "reset-wait-post" }, { .value = TARGET_EVENT_RESET_INIT, .name = "reset-init" }, { .value = TARGET_EVENT_RESET_END, .name = "reset-end" }, @@ -2742,21 +2740,23 @@ COMMAND_HANDLER(handle_reg_command) i < cache->num_regs; i++, reg++, count++) { /* only print cached values if they are valid */ - if (reg->valid) { - value = buf_to_str(reg->value, - reg->size, 16); - command_print(CMD_CTX, - "(%i) %s (/%" PRIu32 "): 0x%s%s", - count, reg->name, - reg->size, value, - reg->dirty + if (reg->exist) { + if (reg->valid) { + value = buf_to_str(reg->value, + reg->size, 16); + command_print(CMD_CTX, + "(%i) %s (/%" PRIu32 "): 0x%s%s", + count, reg->name, + reg->size, value, + reg->dirty ? " (dirty)" : ""); - free(value); - } else { - command_print(CMD_CTX, "(%i) %s (/%" PRIu32 ")", - count, reg->name, - reg->size) ; + free(value); + } else { + command_print(CMD_CTX, "(%i) %s (/%" PRIu32 ")", + count, reg->name, + reg->size) ; + } } } cache = cache->next; @@ -2830,8 +2830,8 @@ COMMAND_HANDLER(handle_reg_command) retval = reg->type->set(reg, buf); if (retval != ERROR_OK) { - LOG_DEBUG("Couldn't set register %s.", reg->name); - free (buf); + LOG_DEBUG("Couldn't set register %s.", reg->name); + free(buf); return retval; } @@ -3697,7 +3697,7 @@ COMMAND_HANDLER(handle_bp_command) addr = 0; return handle_bp_command_set(CMD_CTX, addr, asid, length, hw); } - + /* fallthrough */ case 4: hw = BKPT_HARD; COMMAND_PARSE_ADDRESS(CMD_ARGV[0], addr); diff --git a/src/target/target.h b/src/target/target.h index 53f9e2614..0096cae10 100644 --- a/src/target/target.h +++ b/src/target/target.h @@ -253,10 +253,6 @@ enum target_event { TARGET_EVENT_RESET_ASSERT_POST, TARGET_EVENT_RESET_DEASSERT_PRE, TARGET_EVENT_RESET_DEASSERT_POST, - TARGET_EVENT_RESET_HALT_PRE, - TARGET_EVENT_RESET_HALT_POST, - TARGET_EVENT_RESET_WAIT_PRE, - TARGET_EVENT_RESET_WAIT_POST, TARGET_EVENT_RESET_INIT, TARGET_EVENT_RESET_END, diff --git a/tcl/board/gti/espressobin.cfg b/tcl/board/gti/espressobin.cfg new file mode 100644 index 000000000..20d0452fd --- /dev/null +++ b/tcl/board/gti/espressobin.cfg @@ -0,0 +1,7 @@ +# config for ESPRESSObin from +# Globalscale Technologies Inc. + +# srst is isolated through missing resistor +reset_config trst_only + +source [find target/marvell/88f3720.cfg] diff --git a/tcl/board/openrd.cfg b/tcl/board/openrd.cfg index 1051c25d9..db3cb0326 100644 --- a/tcl/board/openrd.cfg +++ b/tcl/board/openrd.cfg @@ -3,6 +3,8 @@ source [find interface/ftdi/openrd.cfg] source [find target/feroceon.cfg] +adapter_khz 2000 + $_TARGETNAME configure \ -work-area-phys 0x10000000 \ -work-area-size 65536 \ diff --git a/tcl/board/sifive-e31arty.cfg b/tcl/board/sifive-e31arty.cfg new file mode 100644 index 000000000..ec10b27c3 --- /dev/null +++ b/tcl/board/sifive-e31arty.cfg @@ -0,0 +1,22 @@ +# +# Be sure you include the speed and interface before this file +# Example: +# -c "adapter_khz 5000" -f "interface/ftdi/olimex-arm-usb-tiny-h.cfg" -f "board/sifive-e31arty.cfg" + +set _CHIPNAME riscv +jtag newtap $_CHIPNAME cpu -irlen 5 -expected-id 0x20000001 + +set _TARGETNAME $_CHIPNAME.cpu + +target create $_TARGETNAME.0 riscv -chain-position $_TARGETNAME +$_TARGETNAME.0 configure -work-area-phys 0x80000000 -work-area-size 10000 -work-area-backup 1 + +flash bank spi0 fespi 0x40000000 0 0 0 $_TARGETNAME.0 0x20004000 +init +if {[ info exists pulse_srst]} { + ftdi_set_signal nSRST 0 + ftdi_set_signal nSRST z +} +halt +flash protect 0 64 last off +echo "Ready for Remote Connections" diff --git a/tcl/board/sifive-e51arty.cfg b/tcl/board/sifive-e51arty.cfg new file mode 100644 index 000000000..ffd83a058 --- /dev/null +++ b/tcl/board/sifive-e51arty.cfg @@ -0,0 +1,22 @@ +# +# Be sure you include the speed and interface before this file +# Example: +# -c "adapter_khz 5000" -f "interface/ftdi/olimex-arm-usb-tiny-h.cfg" -f "board/sifive-e51arty.cfg" + +set _CHIPNAME riscv +jtag newtap $_CHIPNAME cpu -irlen 5 -expected-id 0x20000001 + +set _TARGETNAME $_CHIPNAME.cpu + +target create $_TARGETNAME.0 riscv -chain-position $_TARGETNAME +$_TARGETNAME.0 configure -work-area-phys 0x80000000 -work-area-size 10000 -work-area-backup 1 + +flash bank spi0 fespi 0x40000000 0 0 0 $_TARGETNAME.0 0x20004000 +init +if {[ info exists pulse_srst]} { + ftdi_set_signal nSRST 0 + ftdi_set_signal nSRST z +} +halt +flash protect 0 64 last off +echo "Ready for Remote Connections" diff --git a/tcl/board/sifive-hifive1.cfg b/tcl/board/sifive-hifive1.cfg new file mode 100644 index 000000000..9bc66701c --- /dev/null +++ b/tcl/board/sifive-hifive1.cfg @@ -0,0 +1,34 @@ +adapter_khz 10000 + +interface ftdi +ftdi_device_desc "Dual RS232-HS" +ftdi_vid_pid 0x0403 0x6010 + +ftdi_layout_init 0x0008 0x001b +ftdi_layout_signal nSRST -oe 0x0020 -data 0x0020 + +#Reset Stretcher logic on FE310 is ~1 second long +#This doesn't apply if you use +# ftdi_set_signal, but still good to document +#adapter_nsrst_delay 1500 + +set _CHIPNAME riscv +jtag newtap $_CHIPNAME cpu -irlen 5 -expected-id 0x10e31913 + +set _TARGETNAME $_CHIPNAME.cpu +target create $_TARGETNAME riscv -chain-position $_TARGETNAME +$_TARGETNAME configure -work-area-phys 0x80000000 -work-area-size 10000 -work-area-backup 1 + +flash bank onboard_spi_flash fespi 0x20000000 0 0 0 $_TARGETNAME +init +#reset -- This type of reset is not implemented yet +if {[ info exists pulse_srst]} { + ftdi_set_signal nSRST 0 + ftdi_set_signal nSRST z + #Wait for the reset stretcher + #It will work without this, but + #will incur lots of delays for later commands. + sleep 1500 +} +halt +flash protect 0 64 last off diff --git a/tcl/board/st_nucleo_f0.cfg b/tcl/board/st_nucleo_f0.cfg index e9fda19a6..e6a03bba8 100644 --- a/tcl/board/st_nucleo_f0.cfg +++ b/tcl/board/st_nucleo_f0.cfg @@ -6,7 +6,7 @@ # STM32F091RC # http://www.st.com/web/catalog/tools/FM116/SC959/SS1532/LN1847/PF260944 -source [find interface/stlink-v2-1.cfg] +source [find interface/stlink.cfg] transport select hla_swd diff --git a/tcl/board/st_nucleo_f103rb.cfg b/tcl/board/st_nucleo_f103rb.cfg index 71a92f704..e1990dcf4 100644 --- a/tcl/board/st_nucleo_f103rb.cfg +++ b/tcl/board/st_nucleo_f103rb.cfg @@ -1,7 +1,7 @@ # This is an ST NUCLEO F103RB board with a single STM32F103RBT6 chip. # http://www.st.com/web/catalog/tools/FM116/SC959/SS1532/LN1847/PF259875 -source [find interface/stlink-v2-1.cfg] +source [find interface/stlink.cfg] transport select hla_swd diff --git a/tcl/board/st_nucleo_f3.cfg b/tcl/board/st_nucleo_f3.cfg index 9dffdcbbd..fec612b39 100644 --- a/tcl/board/st_nucleo_f3.cfg +++ b/tcl/board/st_nucleo_f3.cfg @@ -1,7 +1,7 @@ # This is an ST NUCLEO F334R8 board with a single STM32F334R8T6 chip. # http://www.st.com/web/catalog/tools/FM116/SC959/SS1532/LN1847/PF260004 -source [find interface/stlink-v2-1.cfg] +source [find interface/stlink.cfg] transport select hla_swd diff --git a/tcl/board/st_nucleo_f4.cfg b/tcl/board/st_nucleo_f4.cfg index b5a78c1c0..11f6f8778 100644 --- a/tcl/board/st_nucleo_f4.cfg +++ b/tcl/board/st_nucleo_f4.cfg @@ -4,7 +4,7 @@ # STM32F411RET6 # http://www.st.com/web/catalog/tools/FM116/SC959/SS1532/LN1847/PF260320 -source [find interface/stlink-v2-1.cfg] +source [find interface/stlink.cfg] transport select hla_swd diff --git a/tcl/board/st_nucleo_h743zi.cfg b/tcl/board/st_nucleo_h743zi.cfg new file mode 100644 index 000000000..baedeb6d4 --- /dev/null +++ b/tcl/board/st_nucleo_h743zi.cfg @@ -0,0 +1,10 @@ +# This is an ST NUCLEO-H743ZI board with single STM32H743ZI chip. +# http://www.st.com/en/evaluation-tools/nucleo-h743zi.html + +source [find interface/stlink-v2-1.cfg] + +transport select hla_swd + +source [find target/stm32h7x_dual_bank.cfg] + +reset_config srst_only diff --git a/tcl/board/st_nucleo_l1.cfg b/tcl/board/st_nucleo_l1.cfg index 56e275627..d97eb7c17 100644 --- a/tcl/board/st_nucleo_l1.cfg +++ b/tcl/board/st_nucleo_l1.cfg @@ -1,7 +1,7 @@ # This is an ST NUCLEO L152RE board with a single STM32L152RET6 chip. # http://www.st.com/web/catalog/tools/FM116/SC959/SS1532/LN1847/PF260002 -source [find interface/stlink-v2-1.cfg] +source [find interface/stlink.cfg] transport select hla_swd diff --git a/tcl/board/st_nucleo_l476rg.cfg b/tcl/board/st_nucleo_l476rg.cfg index 2baa34e76..4426c3bc9 100644 --- a/tcl/board/st_nucleo_l476rg.cfg +++ b/tcl/board/st_nucleo_l476rg.cfg @@ -1,7 +1,7 @@ # This is a ST NUCLEO L476RG board with a single STM32L476RGT6 chip. # http://www.st.com/web/catalog/tools/FM116/SC959/SS1532/LN1847/PF261636 -source [find interface/stlink-v2-1.cfg] +source [find interface/stlink.cfg] transport select hla_swd diff --git a/tcl/board/stm320518_eval_stlink.cfg b/tcl/board/stm320518_eval_stlink.cfg index ce074cbf2..a7fef0765 100644 --- a/tcl/board/stm320518_eval_stlink.cfg +++ b/tcl/board/stm320518_eval_stlink.cfg @@ -4,7 +4,7 @@ # # This is for using the onboard STLINK/V2 -source [find interface/stlink-v2.cfg] +source [find interface/stlink.cfg] transport select hla_swd diff --git a/tcl/board/stm3220g_eval_stlink.cfg b/tcl/board/stm3220g_eval_stlink.cfg index 43a4df986..b58e42fe5 100644 --- a/tcl/board/stm3220g_eval_stlink.cfg +++ b/tcl/board/stm3220g_eval_stlink.cfg @@ -4,7 +4,7 @@ # # This is for using the onboard STLINK/V2 -source [find interface/stlink-v2.cfg] +source [find interface/stlink.cfg] transport select hla_swd diff --git a/tcl/board/stm3241g_eval_stlink.cfg b/tcl/board/stm3241g_eval_stlink.cfg index 9c7ad5d95..b1c54a2c6 100644 --- a/tcl/board/stm3241g_eval_stlink.cfg +++ b/tcl/board/stm3241g_eval_stlink.cfg @@ -4,7 +4,7 @@ # # This is for using the onboard STLINK/V2 -source [find interface/stlink-v2.cfg] +source [find interface/stlink.cfg] transport select hla_swd diff --git a/tcl/board/stm32429i_eval_stlink.cfg b/tcl/board/stm32429i_eval_stlink.cfg index 2b51cea67..010d37198 100644 --- a/tcl/board/stm32429i_eval_stlink.cfg +++ b/tcl/board/stm32429i_eval_stlink.cfg @@ -4,7 +4,7 @@ # # This is for using the onboard STLINK/V2 -source [find interface/stlink-v2.cfg] +source [find interface/stlink.cfg] transport select hla_swd diff --git a/tcl/board/stm32439i_eval_stlink.cfg b/tcl/board/stm32439i_eval_stlink.cfg index 5995fb1d2..b722ce67c 100644 --- a/tcl/board/stm32439i_eval_stlink.cfg +++ b/tcl/board/stm32439i_eval_stlink.cfg @@ -4,7 +4,7 @@ # # This is for using the onboard STLINK/V2 -source [find interface/stlink-v2.cfg] +source [find interface/stlink.cfg] transport select hla_swd diff --git a/tcl/board/stm32f0discovery.cfg b/tcl/board/stm32f0discovery.cfg index bae9a69ba..e2b5e3844 100644 --- a/tcl/board/stm32f0discovery.cfg +++ b/tcl/board/stm32f0discovery.cfg @@ -1,7 +1,7 @@ # This is an STM32F0 discovery board with a single STM32F051R8T6 chip. # http://www.st.com/internet/evalboard/product/253215.jsp -source [find interface/stlink-v2.cfg] +source [find interface/stlink.cfg] transport select hla_swd diff --git a/tcl/board/stm32f3discovery.cfg b/tcl/board/stm32f3discovery.cfg index 5a17b4c99..9bb62f5f2 100644 --- a/tcl/board/stm32f3discovery.cfg +++ b/tcl/board/stm32f3discovery.cfg @@ -1,7 +1,7 @@ # This is an STM32F3 discovery board with a single STM32F303VCT6 chip. # http://www.st.com/internet/evalboard/product/254044.jsp -source [find interface/stlink-v2.cfg] +source [find interface/stlink.cfg] transport select hla_swd diff --git a/tcl/board/stm32f429disc1.cfg b/tcl/board/stm32f429disc1.cfg index 9d3cdd757..c0bcebae4 100644 --- a/tcl/board/stm32f429disc1.cfg +++ b/tcl/board/stm32f429disc1.cfg @@ -3,7 +3,7 @@ # http://www.st.com/web/catalog/tools/FM116/SC959/SS1532/PF259090 # -source [find interface/stlink-v2-1.cfg] +source [find interface/stlink.cfg] transport select hla_swd diff --git a/tcl/board/stm32f429discovery.cfg b/tcl/board/stm32f429discovery.cfg index e06d2a51c..7aef09d4f 100644 --- a/tcl/board/stm32f429discovery.cfg +++ b/tcl/board/stm32f429discovery.cfg @@ -3,7 +3,7 @@ # http://www.st.com/web/catalog/tools/FM116/SC959/SS1532/PF259090 # -source [find interface/stlink-v2.cfg] +source [find interface/stlink.cfg] transport select hla_swd diff --git a/tcl/board/stm32f469discovery.cfg b/tcl/board/stm32f469discovery.cfg index 63b13638a..a9559a756 100644 --- a/tcl/board/stm32f469discovery.cfg +++ b/tcl/board/stm32f469discovery.cfg @@ -3,7 +3,7 @@ # http://www.st.com/web/catalog/tools/FM116/CL1620/SC959/SS1532/LN1848/PF262395 # -source [find interface/stlink-v2-1.cfg] +source [find interface/stlink.cfg] transport select hla_swd diff --git a/tcl/board/stm32f4discovery.cfg b/tcl/board/stm32f4discovery.cfg index 963e0f959..60b7f42b5 100644 --- a/tcl/board/stm32f4discovery.cfg +++ b/tcl/board/stm32f4discovery.cfg @@ -1,7 +1,7 @@ # This is an STM32F4 discovery board with a single STM32F407VGT6 chip. # http://www.st.com/internet/evalboard/product/252419.jsp -source [find interface/stlink-v2.cfg] +source [find interface/stlink.cfg] transport select hla_swd diff --git a/tcl/board/stm32f7discovery.cfg b/tcl/board/stm32f7discovery.cfg index 085340f30..7d1bc9665 100755 --- a/tcl/board/stm32f7discovery.cfg +++ b/tcl/board/stm32f7discovery.cfg @@ -2,7 +2,7 @@ # http://www.st.com/web/catalog/tools/FM116/SC959/SS1532/LN1848/PF261641 # This is for using the onboard STLINK/V2-1 -source [find interface/stlink-v2-1.cfg] +source [find interface/stlink.cfg] transport select hla_swd diff --git a/tcl/board/stm32h7x3i_eval.cfg b/tcl/board/stm32h7x3i_eval.cfg new file mode 100644 index 000000000..2949ded32 --- /dev/null +++ b/tcl/board/stm32h7x3i_eval.cfg @@ -0,0 +1,13 @@ +# STM32H7[4|5]3I-EVAL: this is for the H7 eval boards. +# This is an ST EVAL-H743XI board with single STM32H743XI chip. +# http://www.st.com/en/evaluation-tools/stm32h743i-eval.html +# This is an ST EVAL-H753XI board with single STM32H753XI chip. +# http://www.st.com/en/evaluation-tools/stm32h753i-eval.html + +source [find interface/stlink-v2-1.cfg] + +transport select hla_swd + +source [find target/stm32h7x_dual_bank.cfg] + +reset_config srst_only diff --git a/tcl/board/stm32l0discovery.cfg b/tcl/board/stm32l0discovery.cfg index a03506224..aabbf8170 100644 --- a/tcl/board/stm32l0discovery.cfg +++ b/tcl/board/stm32l0discovery.cfg @@ -1,7 +1,7 @@ # This is an STM32L053 discovery board with a single STM32L053 chip. # http://www.st.com/web/en/catalog/tools/PF260319 -source [find interface/stlink-v2-1.cfg] +source [find interface/stlink.cfg] transport select hla_swd diff --git a/tcl/board/stm32l4discovery.cfg b/tcl/board/stm32l4discovery.cfg index eb1933116..8b79841ed 100644 --- a/tcl/board/stm32l4discovery.cfg +++ b/tcl/board/stm32l4discovery.cfg @@ -4,7 +4,7 @@ # an stlink-v2-1 interface. # This is for STM32L4 boards that are connected via stlink-v2-1. -source [find interface/stlink-v2-1.cfg] +source [find interface/stlink.cfg] transport select hla_swd diff --git a/tcl/board/stm32ldiscovery.cfg b/tcl/board/stm32ldiscovery.cfg index 8678d290b..3e397cba4 100644 --- a/tcl/board/stm32ldiscovery.cfg +++ b/tcl/board/stm32ldiscovery.cfg @@ -1,7 +1,7 @@ # This is an STM32L discovery board with a single STM32L152RBT6 chip. # http://www.st.com/internet/evalboard/product/250990.jsp -source [find interface/stlink-v2.cfg] +source [find interface/stlink.cfg] transport select hla_swd diff --git a/tcl/board/stm32vldiscovery.cfg b/tcl/board/stm32vldiscovery.cfg index 970b5101e..60805b32c 100644 --- a/tcl/board/stm32vldiscovery.cfg +++ b/tcl/board/stm32vldiscovery.cfg @@ -1,7 +1,7 @@ # This is an STM32VL discovery board with a single STM32F100RB chip. # http://www.st.com/internet/evalboard/product/250863.jsp -source [find interface/stlink-v1.cfg] +source [find interface/stlink.cfg] transport select hla_swd diff --git a/tcl/board/tocoding_poplar.cfg b/tcl/board/tocoding_poplar.cfg new file mode 100644 index 000000000..fd6615605 --- /dev/null +++ b/tcl/board/tocoding_poplar.cfg @@ -0,0 +1,28 @@ +# +# board configuration for Tocoding Poplar +# + +# board does not feature anything but JTAG +transport select jtag + +adapter_khz 10000 + +# SRST-only reset configuration +reset_config srst_only srst_push_pull + +source [find tcl/target/hi3798.cfg] + +# halt the cores when gdb attaches +${_TARGETNAME}0 configure -event gdb-attach "halt" + +# make sure the default target is the boot core +targets ${_TARGETNAME}0 + +proc core_up { args } { + global _TARGETNAME + + # examine remaining cores + foreach _core [set args] { + ${_TARGETNAME}$_core arp_examine + } +} diff --git a/tcl/board/tp-link_tl-mr3020.cfg b/tcl/board/tp-link_tl-mr3020.cfg index b7d8d5b61..7e040b325 100644 --- a/tcl/board/tp-link_tl-mr3020.cfg +++ b/tcl/board/tp-link_tl-mr3020.cfg @@ -42,3 +42,5 @@ $_TARGETNAME configure -event reset-init { set ram_boot_address 0xa0000000 $_TARGETNAME configure -work-area-phys 0xa1FFE000 -work-area-size 0x1000 + +flash bank flash0 ath79 0 0 0 0 $_TARGETNAME cs0 diff --git a/tcl/board/tp-link_wdr4300.cfg b/tcl/board/tp-link_wdr4300.cfg new file mode 100644 index 000000000..c31791620 --- /dev/null +++ b/tcl/board/tp-link_wdr4300.cfg @@ -0,0 +1,160 @@ +source [find target/atheros_ar9344.cfg] + +reset_config trst_only separate + +proc ar9344_40mhz_pll_init {} { + # QCA_PLL_SRIF_CPU_DPLL2_REG + mww 0xb81161C4 0x13210f00 + # QCA_PLL_SRIF_CPU_DPLL3_REG + mww 0xb81161C8 0x03000000 + # QCA_PLL_SRIF_DDR_DPLL2_REG + mww 0xb8116244 0x13210f00 + # QCA_PLL_SRIF_DDR_DPLL3_REG + mww 0xb8116248 0x03000000 + # QCA_PLL_SRIF_BB_DPLL_BASE_REG + mww 0xb8116188 0x03000000 + + # QCA_PLL_CPU_DDR_CLK_CTRL_REG + mww 0xb8050008 0x0130001C + mww 0xb8050008 0x0130001C + mww 0xb8050008 0x0130001C + + # QCA_PLL_CPU_PLL_CFG_REG + mww 0xb8050000 0x40021380 + # QCA_PLL_DDR_PLL_CFG_REG + mww 0xb8050004 0x40815800 + # QCA_PLL_CPU_DDR_CLK_CTRL_REG + mww 0xb8050008 0x0130801C + + # QCA_PLL_SRIF_CPU_DPLL2_REG + mww 0xb81161C4 0x10810F00 + mww 0xb81161C0 0x41C00000 + # QCA_PLL_SRIF_CPU_DPLL2_REG + mww 0xb81161C4 0xD0810F00 + # QCA_PLL_SRIF_CPU_DPLL3_REG + mww 0xb81161C8 0x03000000 + # QCA_PLL_SRIF_CPU_DPLL2_REG + mww 0xb81161C4 0xD0800F00 + + # QCA_PLL_SRIF_CPU_DPLL3_REG + mww 0xb81161C8 0x03000000 + # QCA_PLL_SRIF_CPU_DPLL3_REG + mww 0xb81161C8 0x43000000 + # QCA_PLL_SRIF_CPU_DPLL3_REG + mww 0xb81161C8 0x030003E8 + + # QCA_PLL_SRIF_DDR_DPLL2_REG + mww 0xb8116244 0x10810F00 + mww 0xb8116240 0x41680000 + # QCA_PLL_SRIF_DDR_DPLL2_REG + mww 0xb8116244 0xD0810F00 + # QCA_PLL_SRIF_DDR_DPLL3_REG + mww 0xb8116248 0x03000000 + # QCA_PLL_SRIF_DDR_DPLL2_REG + mww 0xb8116244 0xD0800F00 + + # QCA_PLL_SRIF_DDR_DPLL3_REG + mww 0xb8116248 0x03000000 + # QCA_PLL_SRIF_DDR_DPLL3_REG + mww 0xb8116248 0x43000000 + # QCA_PLL_SRIF_DDR_DPLL3_REG + mww 0xb8116248 0x03000718 + + # QCA_PLL_CPU_DDR_CLK_CTRL_REG + mww 0xb8050008 0x01308018 + mww 0xb8050008 0x01308010 + mww 0xb8050008 0x01308000 + + # QCA_PLL_DDR_PLL_DITHER_REG + mww 0xb8050044 0x78180200 + # QCA_PLL_CPU_PLL_DITHER_REG + mww 0xb8050048 0x41C00000 + +} + +proc ar9344_ddr_init {} { + # QCA_DDR_CTRL_CFG_REG + mww 0xb8000108 0x40 + # QCA_DDR_RD_DATA_THIS_CYCLE_REG + mww 0xb8000018 0xFF + # QCA_DDR_BURST_REG + mww 0xb80000C4 0x74444444 + # QCA_DDR_BURST2_REG + mww 0xb80000C8 0x0222 + # QCA_AHB_MASTER_TOUT_MAX_REG + mww 0xb80000CC 0xFFFFF + + # QCA_DDR_CFG_REG + mww 0xb8000000 0xC7D48CD0 + # QCA_DDR_CFG2_REG + mww 0xb8000004 0x9DD0E6A8 + + # QCA_DDR_DDR2_CFG_REG + mww 0xb80000B8 0x0E59 + # QCA_DDR_CFG2_REG + mww 0xb8000004 0x9DD0E6A8 + + # QCA_DDR_CTRL_REG + mww 0xb8000010 0x08 + mww 0xb8000010 0x08 + mww 0xb8000010 0x10 + mww 0xb8000010 0x20 + # QCA_DDR_EMR_REG + mww 0xb800000C 0x02 + # QCA_DDR_CTRL_REG + mww 0xb8000010 0x02 + + # QCA_DDR_MR_REG + mww 0xb8000008 0x0133 + # QCA_DDR_CTRL_REG + mww 0xb8000010 0x1 + mww 0xb8000010 0x8 + mww 0xb8000010 0x8 + mww 0xb8000010 0x4 + mww 0xb8000010 0x4 + + # QCA_DDR_MR_REG + mww 0xb8000008 0x33 + # QCA_DDR_CTRL_REG + mww 0xb8000010 0x1 + + # QCA_DDR_EMR_REG + mww 0xb800000C 0x0382 + # QCA_DDR_CTRL_REG + mww 0xb8000010 0x2 + # QCA_DDR_EMR_REG + mww 0xb800000C 0x0402 + # QCA_DDR_CTRL_REG + mww 0xb8000010 0x2 + + # QCA_DDR_REFRESH_REG + mww 0xb8000014 0x4270 + + # QCA_DDR_TAP_CTRL_0_REG + mww 0xb800001C 0x0e + # QCA_DDR_TAP_CTRL_1_REG + mww 0xb8000020 0x0e + # QCA_DDR_TAP_CTRL_2_REG + mww 0xb8000024 0x0e + # QCA_DDR_TAP_CTRL_3_REG + mww 0xb8000028 0x0e +} + +$_TARGETNAME configure -event reset-init { + + # mww 0xb806001c 0x1000000 + ar9344_40mhz_pll_init + sleep 100 + + # flash remap + # SPI_CONTROL_ADDR + mww 0xbF000004 0x43 + + ar9344_ddr_init + sleep 100 +} + +set ram_boot_address 0xa0000000 +$_TARGETNAME configure -work-area-phys 0x1d000000 -work-area-size 0x1000 + +flash bank flash0 ath79 0 0 0 0 $_TARGETNAME cs0 diff --git a/tcl/interface/ftdi/minimodule.cfg b/tcl/interface/ftdi/minimodule.cfg index 57249dfc1..7df096d9c 100644 --- a/tcl/interface/ftdi/minimodule.cfg +++ b/tcl/interface/ftdi/minimodule.cfg @@ -4,14 +4,13 @@ # http://www.ftdichip.com/Support/Documents/DataSheets/Modules/DS_FT2232H_Mini_Module.pdf # -echo "WARNING!" -echo "This file was not tested with real interface, it is based on code in ft2232.c." -echo "Please report your experience with this file to openocd-devel mailing list," -echo "so it could be marked as working or fixed." - interface ftdi ftdi_device_desc "FT2232H MiniModule" ftdi_vid_pid 0x0403 0x6010 -ftdi_layout_init 0x0018 0x05fb -ftdi_layout_signal nSRST -data 0x0020 +# Every pin set as high impedance except TCK, TDI, TDO and TMS +ftdi_layout_init 0x0008 0x000b + +# nSRST defined on pin CN2-13 of the MiniModule (pin ADBUS5 [AD5] on the FT2232H chip) +# This choice is arbitrary. Use other GPIO pin if desired. +ftdi_layout_signal nSRST -data 0x0020 -oe 0x0020 diff --git a/tcl/interface/ftdi/openrd.cfg b/tcl/interface/ftdi/openrd.cfg index 8c1a80596..9ec5b5f65 100644 --- a/tcl/interface/ftdi/openrd.cfg +++ b/tcl/interface/ftdi/openrd.cfg @@ -4,15 +4,10 @@ # http://www.marvell.com/products/embedded_processors/developer/kirkwood/openrd.jsp # -echo "WARNING!" -echo "This file was not tested with real interface, it is based on code in ft2232.c." -echo "Please report your experience with this file to openocd-devel mailing list," -echo "so it could be marked as working or fixed." - interface ftdi -ftdi_device_desc "OpenRD JTAGKey FT2232D" +ftdi_device_desc "OpenRD JTAGKey FT2232D B" ftdi_vid_pid 0x0403 0x9e90 -ftdi_channel 1 +ftdi_channel 0 ftdi_layout_init 0x0608 0x0f1b ftdi_layout_signal nTRST -data 0x0200 diff --git a/tcl/interface/ftdi/sheevaplug.cfg b/tcl/interface/ftdi/sheevaplug.cfg index f299f27a9..625aad398 100644 --- a/tcl/interface/ftdi/sheevaplug.cfg +++ b/tcl/interface/ftdi/sheevaplug.cfg @@ -7,7 +7,7 @@ interface ftdi ftdi_device_desc "SheevaPlug JTAGKey FT2232D B" ftdi_vid_pid 0x9e88 0x9e8f -ftdi_channel 1 +ftdi_channel 0 ftdi_layout_init 0x0608 0x0f1b ftdi_layout_signal nTRST -data 0x0200 diff --git a/tcl/interface/stlink-v1.cfg b/tcl/interface/stlink-v1.cfg index 13f207dc6..000422725 100644 --- a/tcl/interface/stlink-v1.cfg +++ b/tcl/interface/stlink-v1.cfg @@ -1,9 +1,2 @@ -# -# STMicroelectronics ST-LINK/V1 in-circuit debugger/programmer -# - -interface hla -hla_layout stlink -hla_device_desc "ST-LINK/V1" -hla_vid_pid 0x0483 0x3744 - +echo "WARNING: interface/stlink-v1.cfg is deprecated, please switch to interface/stlink.cfg" +source [find interface/stlink.cfg] diff --git a/tcl/interface/stlink-v2-1.cfg b/tcl/interface/stlink-v2-1.cfg index 093e80177..62f37dc33 100644 --- a/tcl/interface/stlink-v2-1.cfg +++ b/tcl/interface/stlink-v2-1.cfg @@ -1,16 +1,2 @@ -# -# STMicroelectronics ST-LINK/V2-1 in-circuit debugger/programmer -# - -interface hla -hla_layout stlink -hla_device_desc "ST-LINK/V2-1" -hla_vid_pid 0x0483 0x374b - -# Optionally specify the serial number of ST-LINK/V2 usb device. ST-LINK/V2 -# devices seem to have serial numbers with unreadable characters. ST-LINK/V2 -# firmware version >= V2.J21.S4 recommended to avoid issues with adapter serial -# number reset issues. -# eg. -#hla_serial "\xaa\xbc\x6e\x06\x50\x75\xff\x55\x17\x42\x19\x3f" - +echo "WARNING: interface/stlink-v2-1.cfg is deprecated, please switch to interface/stlink.cfg" +source [find interface/stlink.cfg] diff --git a/tcl/interface/stlink-v2.cfg b/tcl/interface/stlink-v2.cfg index ae545a118..070e46958 100644 --- a/tcl/interface/stlink-v2.cfg +++ b/tcl/interface/stlink-v2.cfg @@ -1,16 +1,2 @@ -# -# STMicroelectronics ST-LINK/V2 in-circuit debugger/programmer -# - -interface hla -hla_layout stlink -hla_device_desc "ST-LINK/V2" -hla_vid_pid 0x0483 0x3748 - -# Optionally specify the serial number of ST-LINK/V2 usb device. ST-LINK/V2 -# devices seem to have serial numbers with unreadable characters. ST-LINK/V2 -# firmware version >= V2.J21.S4 recommended to avoid issues with adapter serial -# number reset issues. -# eg. -#hla_serial "\xaa\xbc\x6e\x06\x50\x75\xff\x55\x17\x42\x19\x3f" - +echo "WARNING: interface/stlink-v2.cfg is deprecated, please switch to interface/stlink.cfg" +source [find interface/stlink.cfg] diff --git a/tcl/interface/stlink.cfg b/tcl/interface/stlink.cfg new file mode 100644 index 000000000..d747d8533 --- /dev/null +++ b/tcl/interface/stlink.cfg @@ -0,0 +1,17 @@ +# +# STMicroelectronics ST-LINK/V1, ST-LINK/V2, ST-LINK/V2-1 in-circuit +# debugger/programmer +# + +interface hla +hla_layout stlink +hla_device_desc "ST-LINK" +hla_vid_pid 0x0483 0x3744 0x0483 0x3748 0x0483 0x374b + +# Optionally specify the serial number of ST-LINK/V2 usb device. ST-LINK/V2 +# devices seem to have serial numbers with unreadable characters. ST-LINK/V2 +# firmware version >= V2.J21.S4 recommended to avoid issues with adapter serial +# number reset issues. +# eg. +#hla_serial "\xaa\xbc\x6e\x06\x50\x75\xff\x55\x17\x42\x19\x3f" + diff --git a/tcl/target/1986ве1т.cfg b/tcl/target/1986Be1T.cfg similarity index 100% rename from tcl/target/1986ве1т.cfg rename to tcl/target/1986Be1T.cfg diff --git a/tcl/target/к1879xб1я.cfg b/tcl/target/K1879x61R.cfg similarity index 100% rename from tcl/target/к1879xб1я.cfg rename to tcl/target/K1879x61R.cfg diff --git a/tcl/target/atheros_ar9344.cfg b/tcl/target/atheros_ar9344.cfg new file mode 100644 index 000000000..b698f2503 --- /dev/null +++ b/tcl/target/atheros_ar9344.cfg @@ -0,0 +1,39 @@ +if { [info exists CHIPNAME] } { + set _CHIPNAME $_CHIPNAME +} else { + set _CHIPNAME ar9344 +} + +if { [info exists CPUTAPID] } { + set _CPUTAPID $CPUTAPID +} else { + set _CPUTAPID 0x00000001 +} + +jtag newtap $_CHIPNAME cpu -irlen 5 -expected-id $_CPUTAPID + +set _TARGETNAME $_CHIPNAME.cpu +target create $_TARGETNAME mips_m4k -endian big -chain-position $_TARGETNAME + +proc test_ar9344_uart0_tx {} { + echo "configuring uart0.." + mww 0xb802000c 0x87 + mww 0xb8020000 0x15 + mww 0xb8020004 0 + mww 0xb802000c 7 + mww 0xb8020004 0 + + echo "send message: hallo world" + mww 0xb8020000 0x68 + mww 0xb8020000 0x65 + mww 0xb8020000 0x6c + mww 0xb8020000 0x6c + mww 0xb8020000 0x6f + mww 0xb8020000 0x20 + mww 0xb8020000 0x77 + mww 0xb8020000 0x6f + mww 0xb8020000 0x72 + mww 0xb8020000 0x6c + mww 0xb8020000 0x64 + mww 0xb8020000 0x0a +} diff --git a/tcl/target/hi3798.cfg b/tcl/target/hi3798.cfg new file mode 100644 index 000000000..9eda15035 --- /dev/null +++ b/tcl/target/hi3798.cfg @@ -0,0 +1,49 @@ +# Hisilicon Hi3798 Target + +if { [info exists CHIPNAME] } { + set _CHIPNAME $CHIPNAME +} else { + set _CHIPNAME hi3798 +} + +# +# Main DAP +# +if { [info exists DAP_TAPID] } { + set _DAP_TAPID $DAP_TAPID +} else { + set _DAP_TAPID 0x5ba00477 +} + +# declare the one JTAG tap to access the DAP +jtag newtap $_CHIPNAME dap -irlen 4 -ircapture 0x1 -irmask 0xf -expected-id $_DAP_TAPID -ignore-version -enable + +# declare the 4 main application cores +set _TARGETNAME $_CHIPNAME.cpu +set _smp_command "" + +set $_TARGETNAME.cti(0) 0x80020000 +set $_TARGETNAME.cti(1) 0x80120000 +set $_TARGETNAME.cti(2) 0x80220000 +set $_TARGETNAME.cti(3) 0x80320000 + +set _cores 4 +for { set _core 0 } { $_core < $_cores } { incr _core 1 } { + + set _command "target create ${_TARGETNAME}$_core aarch64 \ + -chain-position $_CHIPNAME.dap -coreid $_core -ctibase [set $_TARGETNAME.cti($_core)]" + + if { $_core != 0 } { + # non-boot core examination may fail + #set _command "$_command -defer-examine" + set _smp_command "$_smp_command ${_TARGETNAME}$_core" + } else { + # uncomment when "hawt" rtos is merged + # set _command "$_command -rtos hawt" + set _smp_command "target smp ${_TARGETNAME}$_core" + } + + eval $_command +} + +eval $_smp_command diff --git a/tcl/target/marvell/88f3710.cfg b/tcl/target/marvell/88f3710.cfg new file mode 100644 index 000000000..6e35f293d --- /dev/null +++ b/tcl/target/marvell/88f3710.cfg @@ -0,0 +1,5 @@ +# Marvell Armada 3710 + +set CORES 1 + +source [find target/marvell/88f37x0.cfg] diff --git a/tcl/target/marvell/88f3720.cfg b/tcl/target/marvell/88f3720.cfg new file mode 100644 index 000000000..799d614ba --- /dev/null +++ b/tcl/target/marvell/88f3720.cfg @@ -0,0 +1,5 @@ +# Marvell Armada 3720 + +set CORES 2 + +source [find target/marvell/88f37x0.cfg] diff --git a/tcl/target/marvell/88f37x0.cfg b/tcl/target/marvell/88f37x0.cfg new file mode 100644 index 000000000..dba7da21e --- /dev/null +++ b/tcl/target/marvell/88f37x0.cfg @@ -0,0 +1,68 @@ +# Main file for Marvell Armada 3700 series targets +# +# !!!!!! +# +# This file should not be included directly. Instead, please include +# either marvell/88f3710.cfg or marvell/88f3720.cfg, which set the needed +# variables to the appropriate values. +# +# !!!!!! + +# Armada 3700 supports both JTAG and SWD transports. +source [find target/swj-dp.tcl] + +if { [info exists CORES] } { + set _cores $CORES +} else { + error "CORES not set. Please do not include marvell/88f37x0.cfg directly, but the specific chip configuration file (marvell/88f3710.cfg, marvell/88f3720.cfg, etc.)." +} + +if { [info exists CHIPNAME] } { + set _CHIPNAME $CHIPNAME +} else { + set _CHIPNAME [format a37%s0 $_cores] +} + +set _ctis {0x80820000 0x80920000} + +# +# Main DAP +# +if { [info exists DAP_TAPID] } { + set _DAP_TAPID $DAP_TAPID +} else { + set _DAP_TAPID 0x4ba00477 +} + +# declare the one JTAG tap to access the DAP +swj_newdap $_CHIPNAME dap -irlen 4 -ircapture 0x1 -irmask 0xf -expected-id $_DAP_TAPID -ignore-version -enable + +# declare the main application cores +set _TARGETNAME $_CHIPNAME.cpu +set _smp_command "" + +for { set _core 0 } { $_core < $_cores } { incr _core 1 } { + + set _command "target create ${_TARGETNAME}$_core aarch64 \ + -chain-position $_CHIPNAME.dap -coreid $_core \ + -ctibase [lindex $_ctis $_core]" + + if { $_core != 0 } { + # non-boot core examination may fail + set _command "$_command -defer-examine" + set _smp_command "$_smp_command ${_TARGETNAME}$_core" + } else { + # uncomment when "hawt" rtos is merged + # set _command "$_command -rtos hawt" + set _smp_command "target smp ${_TARGETNAME}$_core" + } + + eval $_command +} + +eval $_smp_command + +# declare the auxiliary Cortex-M3 core on AP #3 +target create ${_TARGETNAME}.m3 cortex_m -chain-position $_CHIPNAME.dap -ap-num 3 -defer-examine + +targets ${_TARGETNAME}0 diff --git a/tcl/target/nrf52.cfg b/tcl/target/nrf52.cfg index c1cbf1a21..e73017503 100644 --- a/tcl/target/nrf52.cfg +++ b/tcl/target/nrf52.cfg @@ -10,6 +10,14 @@ if { [info exists CHIPNAME] } { set _CHIPNAME nrf52 } +# Work-area is a space in RAM used for flash programming +# By default use 16kB +if { [info exists WORKAREASIZE] } { + set _WORKAREASIZE $WORKAREASIZE +} else { + set _WORKAREASIZE 0x4000 +} + if { [info exists CPUTAPID] } { set _CPUTAPID $CPUTAPID } else { @@ -21,8 +29,13 @@ swj_newdap $_CHIPNAME cpu -expected-id $_CPUTAPID set _TARGETNAME $_CHIPNAME.cpu target create $_TARGETNAME cortex_m -chain-position $_TARGETNAME -adapter_khz 10000 +adapter_khz 1000 + +$_TARGETNAME configure -work-area-phys 0x20000000 -work-area-size $_WORKAREASIZE -work-area-backup 0 if { ![using_hla] } { cortex_m reset_config sysresetreq } + +flash bank $_CHIPNAME.flash nrf5 0x00000000 0 1 1 $_TARGETNAME +flash bank $_CHIPNAME.uicr nrf5 0x10001000 0 1 1 $_TARGETNAME diff --git a/tcl/target/stm32h7x.cfg b/tcl/target/stm32h7x.cfg new file mode 100644 index 000000000..02dbed4a8 --- /dev/null +++ b/tcl/target/stm32h7x.cfg @@ -0,0 +1,93 @@ +# script for stm32h7x family + +# +# stm32h7 devices support both JTAG and SWD transports. +# +source [find target/swj-dp.tcl] +source [find mem_helper.tcl] + +if { [info exists CHIPNAME] } { + set _CHIPNAME $CHIPNAME +} else { + set _CHIPNAME stm32h7x +} + +set _ENDIAN little + +# Work-area is a space in RAM used for flash programming +# By default use 64kB +if { [info exists WORKAREASIZE] } { + set _WORKAREASIZE $WORKAREASIZE +} else { + set _WORKAREASIZE 0x10000 +} + +#jtag scan chain +if { [info exists CPUTAPID] } { + set _CPUTAPID $CPUTAPID +} else { + if { [using_jtag] } { + set _CPUTAPID 0x6ba00477 + } { + set _CPUTAPID 0x6ba02477 + } +} + +swj_newdap $_CHIPNAME cpu -irlen 4 -ircapture 0x1 -irmask 0xf -expected-id $_CPUTAPID + +if {[using_jtag]} { + swj_newdap $_CHIPNAME bs -irlen 5 +} + +set _TARGETNAME $_CHIPNAME.cpu +target create $_TARGETNAME cortex_m -endian $_ENDIAN -chain-position $_TARGETNAME + +$_TARGETNAME configure -work-area-phys 0x20000000 -work-area-size $_WORKAREASIZE -work-area-backup 0 + +set _FLASHNAME $_CHIPNAME.flash +flash bank $_FLASHNAME stm32h7x 0x08000000 0 0 0 $_TARGETNAME + +# Clock after reset is HSI at 64 MHz, no need of PLL +adapter_khz 1800 + +adapter_nsrst_delay 100 +if {[using_jtag]} { + jtag_ntrst_delay 100 +} + +# use hardware reset, connect under reset +reset_config srst_only srst_nogate + +if {![using_hla]} { + # if srst is not fitted use SYSRESETREQ to + # perform a soft reset + cortex_m reset_config sysresetreq +} + +$_TARGETNAME configure -event examine-end { + # Enable D3 and D1 DBG clocks + # DBGMCU_CR |= D3DBGCKEN | D1DBGCKEN + mmw 0x5C001004 0x00600000 0 + + # Enable debug during low power modes (uses more power) + # DBGMCU_CR |= DBG_STANDBY | DBG_STOP | DBG_SLEEP in D3 & D1 Domains + mmw 0x5C001004 0x00000187 0 + + # Stop watchdog counters during halt + # DBGMCU_APB3FZ1 |= WWDG1 + mmw 0x5C001034 0x00000040 0 + # DBGMCU_APB4FZ1 |= WDGLSD1 + mmw 0x5C001054 0x00040000 0 +} + +$_TARGETNAME configure -event trace-config { + # Set TRACECLKEN; TRACE_MODE is set to async; when using sync + # change this value accordingly to configure trace pins + # assignment + mmw 0x5C001004 0x00100000 0 +} + +$_TARGETNAME configure -event reset-init { + # Clock after reset is HSI at 64 MHz, no need of PLL + adapter_khz 4000 +} diff --git a/tcl/target/stm32h7x_dual_bank.cfg b/tcl/target/stm32h7x_dual_bank.cfg new file mode 100644 index 000000000..7e342f931 --- /dev/null +++ b/tcl/target/stm32h7x_dual_bank.cfg @@ -0,0 +1,7 @@ +# script for stm32h7x family (dual flash bank) +source [find target/stm32h7x.cfg] + +# STM32H7xxxI 2Mo have a dual bank flash. +# Add the second flash bank. +set _FLASHNAME $_CHIPNAME.flash1 +flash bank $_FLASHNAME stm32h7x 0x08100000 0 0 0 $_TARGETNAME diff --git a/tcl/target/stm32l0.cfg b/tcl/target/stm32l0.cfg index 245213b42..417b282d3 100644 --- a/tcl/target/stm32l0.cfg +++ b/tcl/target/stm32l0.cfg @@ -15,11 +15,11 @@ if { [info exists CHIPNAME] } { set _ENDIAN little # Work-area is a space in RAM used for flash programming -# By default use 8kB (max ram on smallest part) +# By default use 2kB (max ram on smallest part) if { [info exists WORKAREASIZE] } { set _WORKAREASIZE $WORKAREASIZE } else { - set _WORKAREASIZE 0x2000 + set _WORKAREASIZE 0x800 } # JTAG speed should be <= F_CPU/6. diff --git a/tcl/target/stm8l.cfg b/tcl/target/stm8l.cfg new file mode 100644 index 000000000..5cc99e191 --- /dev/null +++ b/tcl/target/stm8l.cfg @@ -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 index 000000000..d55e61b08 --- /dev/null +++ b/tcl/target/stm8s.cfg @@ -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