From 0e5b3ab00ea7515ec75976c9ea2bcf2a2af737f9 Mon Sep 17 00:00:00 2001 From: Nick Downing Date: Fri, 14 Jul 2017 20:45:54 +1000 Subject: [PATCH 01/18] Make Cortex M driver write DCCIMVAC (Data cache clean and invalidate by address to the PoC=Point of Coherency) prior to reading or writing each 32 bytes of RAM --- src/target/cortexm.c | 32 ++++++++++++++++++++++++++++++++ src/target/cortexm.h | 3 +++ 2 files changed, 35 insertions(+) diff --git a/src/target/cortexm.c b/src/target/cortexm.c index 4e941060..380f2fcc 100644 --- a/src/target/cortexm.c +++ b/src/target/cortexm.c @@ -181,11 +181,43 @@ ADIv5_AP_t *cortexm_ap(target *t) static void cortexm_mem_read(target *t, void *dest, target_addr src, size_t len) { + /* flush data cache for RAM regions that intersect requested region */ + target_addr src_end = src + len; /* following code is NOP if wraparound */ + /* requested region is [src, src_end) */ + for (struct target_ram *r = t->ram; r; r = r->next) { + target_addr ram = r->start; + target_addr ram_end = r->start + r->length; + /* RAM region is [ram, ram_end) */ + if (src > ram) + ram = src; + if (src_end < ram_end) + ram_end = src_end; + /* intersection is [ram, ram_end) */ + for (ram &= ~0x1f; ram < ram_end; ram += 0x20) + adiv5_mem_write(cortexm_ap(t), CORTEXM_DCCIMVAC, &ram, 4); + } + adiv5_mem_read(cortexm_ap(t), dest, src, len); } static void cortexm_mem_write(target *t, target_addr dest, const void *src, size_t len) { + /* flush data cache for RAM regions that intersect requested region */ + target_addr dest_end = dest + len; /* following code is NOP if wraparound */ + /* requested region is [dest, dest_end) */ + for (struct target_ram *r = t->ram; r; r = r->next) { + target_addr ram = r->start; + target_addr ram_end = r->start + r->length; + /* RAM region is [ram, ram_end) */ + if (dest > ram) + ram = dest; + if (dest_end < ram_end) + ram_end = dest_end; + /* intersection is [ram, ram_end) */ + for (ram &= ~0x1f; ram < ram_end; ram += 0x20) + adiv5_mem_write(cortexm_ap(t), CORTEXM_DCCIMVAC, &ram, 4); + } + adiv5_mem_write(cortexm_ap(t), dest, src, len); } diff --git a/src/target/cortexm.h b/src/target/cortexm.h index bf1d821d..ca5e10b3 100644 --- a/src/target/cortexm.h +++ b/src/target/cortexm.h @@ -37,6 +37,9 @@ #define CORTEXM_DCRDR (CORTEXM_SCS_BASE + 0xDF8) #define CORTEXM_DEMCR (CORTEXM_SCS_BASE + 0xDFC) +/* Data cache clean and invalidate by address to the PoC=Point of Coherency */ +#define CORTEXM_DCCIMVAC (CORTEXM_SCS_BASE + 0xF70) + #define CORTEXM_FPB_BASE (CORTEXM_PPB_BASE + 0x2000) /* ARM Literature uses FP_*, we use CORTEXM_FPB_* consistently */ From c53a12bfd1fc9ce4d41dd4bd3ceb87cae917c96f Mon Sep 17 00:00:00 2001 From: Gareth McMullin Date: Thu, 12 Oct 2017 09:26:01 +1300 Subject: [PATCH 02/18] cortexm: Better cache support for Cortex-M7 - On probe, read CTR for cache presence and minimum line length - Make D-Cache clean a function - Clean before memory reads - Clean and invalidate before memory writes - Flush all I-Cache before resume --- src/target/cortexm.c | 58 ++++++++++++++++++++++++++------------------ src/target/cortexm.h | 10 +++++++- 2 files changed, 43 insertions(+), 25 deletions(-) diff --git a/src/target/cortexm.c b/src/target/cortexm.c index 380f2fcc..abd64f4a 100644 --- a/src/target/cortexm.c +++ b/src/target/cortexm.c @@ -78,6 +78,9 @@ struct cortexm_priv { unsigned hw_breakpoint_max; /* Copy of DEMCR for vector-catch */ uint32_t demcr; + /* Cache parameters */ + bool has_cache; + uint32_t dcache_minline; }; /* Register number tables */ @@ -179,45 +182,40 @@ ADIv5_AP_t *cortexm_ap(target *t) return ((struct cortexm_priv *)t->priv)->ap; } -static void cortexm_mem_read(target *t, void *dest, target_addr src, size_t len) +static void cortexm_cache_clean(target *t, target_addr addr, size_t len, bool invalidate) { + struct cortexm_priv *priv = t->priv; + if (!priv->has_cache || (priv->dcache_minline == 0)) + return; + uint32_t cache_reg = invalidate ? CORTEXM_DCCIMVAC : CORTEXM_DCCMVAC; + size_t minline = priv->dcache_minline; + /* flush data cache for RAM regions that intersect requested region */ - target_addr src_end = src + len; /* following code is NOP if wraparound */ + target_addr mem_end = addr + len; /* following code is NOP if wraparound */ /* requested region is [src, src_end) */ for (struct target_ram *r = t->ram; r; r = r->next) { target_addr ram = r->start; target_addr ram_end = r->start + r->length; /* RAM region is [ram, ram_end) */ - if (src > ram) - ram = src; - if (src_end < ram_end) - ram_end = src_end; + if (addr > ram) + ram = addr; + if (mem_end < ram_end) + ram_end = mem_end; /* intersection is [ram, ram_end) */ - for (ram &= ~0x1f; ram < ram_end; ram += 0x20) - adiv5_mem_write(cortexm_ap(t), CORTEXM_DCCIMVAC, &ram, 4); + for (ram &= ~(minline-1); ram < ram_end; ram += minline) + adiv5_mem_write(cortexm_ap(t), cache_reg, &ram, 4); } +} +static void cortexm_mem_read(target *t, void *dest, target_addr src, size_t len) +{ + cortexm_cache_clean(t, src, len, false); adiv5_mem_read(cortexm_ap(t), dest, src, len); } static void cortexm_mem_write(target *t, target_addr dest, const void *src, size_t len) { - /* flush data cache for RAM regions that intersect requested region */ - target_addr dest_end = dest + len; /* following code is NOP if wraparound */ - /* requested region is [dest, dest_end) */ - for (struct target_ram *r = t->ram; r; r = r->next) { - target_addr ram = r->start; - target_addr ram_end = r->start + r->length; - /* RAM region is [ram, ram_end) */ - if (dest > ram) - ram = dest; - if (dest_end < ram_end) - ram_end = dest_end; - /* intersection is [ram, ram_end) */ - for (ram &= ~0x1f; ram < ram_end; ram += 0x20) - adiv5_mem_write(cortexm_ap(t), CORTEXM_DCCIMVAC, &ram, 4); - } - + cortexm_cache_clean(t, dest, len, true); adiv5_mem_write(cortexm_ap(t), dest, src, len); } @@ -283,6 +281,15 @@ bool cortexm_probe(ADIv5_AP_t *ap) priv->demcr = CORTEXM_DEMCR_TRCENA | CORTEXM_DEMCR_VC_HARDERR | CORTEXM_DEMCR_VC_CORERESET; + /* Check cache type */ + uint32_t ctr = target_mem_read32(t, CORTEXM_CTR); + if ((ctr >> 29) == 4) { + priv->has_cache = true; + priv->dcache_minline = 4 << (ctr & 0xf); + } else { + target_check_error(t); + } + #define PROBE(x) \ do { if ((x)(t)) return true; else target_check_error(t); } while (0) @@ -583,6 +590,9 @@ void cortexm_halt_resume(target *t, bool step) cortexm_pc_write(t, pc + 2); } + if (priv->has_cache) + target_mem_write32(t, CORTEXM_ICIALLU, 0); + target_mem_write32(t, CORTEXM_DHCSR, dhcsr); } diff --git a/src/target/cortexm.h b/src/target/cortexm.h index ca5e10b3..e9bf5476 100644 --- a/src/target/cortexm.h +++ b/src/target/cortexm.h @@ -37,7 +37,15 @@ #define CORTEXM_DCRDR (CORTEXM_SCS_BASE + 0xDF8) #define CORTEXM_DEMCR (CORTEXM_SCS_BASE + 0xDFC) -/* Data cache clean and invalidate by address to the PoC=Point of Coherency */ +/* Cache identification */ +#define CORTEXM_CLIDR (CORTEXM_SCS_BASE + 0xD78) +#define CORTEXM_CTR (CORTEXM_SCS_BASE + 0xD7C) +#define CORTEXM_CCSIDR (CORTEXM_SCS_BASE + 0xD80) +#define CORTEXM_CSSELR (CORTEXM_SCS_BASE + 0xD84) + +/* Cache maintenance operations */ +#define CORTEXM_ICIALLU (CORTEXM_SCS_BASE + 0xF50) +#define CORTEXM_DCCMVAC (CORTEXM_SCS_BASE + 0xF68) #define CORTEXM_DCCIMVAC (CORTEXM_SCS_BASE + 0xF70) #define CORTEXM_FPB_BASE (CORTEXM_PPB_BASE + 0x2000) From 1f3c2352054846a6e9825f0fc6698eb21dc035bc Mon Sep 17 00:00:00 2001 From: Uwe Bonnes Date: Fri, 8 Dec 2017 13:39:24 +0100 Subject: [PATCH 03/18] src/target/stm32f1.c: Add CCM Ram of STM32F303 devices. --- src/target/stm32f1.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/target/stm32f1.c b/src/target/stm32f1.c index c9823db3..1b706599 100644 --- a/src/target/stm32f1.c +++ b/src/target/stm32f1.c @@ -136,11 +136,12 @@ bool stm32f1_probe(target *t) stm32f1_add_flash(t, 0x8000000, 0x80000, 0x800); target_add_commands(t, stm32f1_cmd_list, "STM32 HD/CL"); return true; - case 0x422: /* STM32F30x */ - case 0x432: /* STM32F37x */ case 0x438: /* STM32F303x6/8 and STM32F328 */ - case 0x439: /* STM32F302C8 */ + case 0x422: /* STM32F30x */ case 0x446: /* STM32F303xD/E and STM32F398xE */ + target_add_ram(t, 0x10000000, 0x4000); + case 0x432: /* STM32F37x */ + case 0x439: /* STM32F302C8 */ t->driver = "STM32F3"; target_add_ram(t, 0x20000000, 0x10000); stm32f1_add_flash(t, 0x8000000, 0x80000, 0x800); From 922f857de7ee6b59f128812be1e38172f3a2cada Mon Sep 17 00:00:00 2001 From: Uwe Bonnes Date: Mon, 18 Dec 2017 13:55:55 +0100 Subject: [PATCH 04/18] stm32f1.c: Add missing fall through statement needed by GCC7. --- src/target/stm32f1.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/target/stm32f1.c b/src/target/stm32f1.c index 1b706599..4064e01a 100644 --- a/src/target/stm32f1.c +++ b/src/target/stm32f1.c @@ -140,6 +140,7 @@ bool stm32f1_probe(target *t) case 0x422: /* STM32F30x */ case 0x446: /* STM32F303xD/E and STM32F398xE */ target_add_ram(t, 0x10000000, 0x4000); + /* fall through */ case 0x432: /* STM32F37x */ case 0x439: /* STM32F302C8 */ t->driver = "STM32F3"; From f5cac4c78d49f91731048fc4ddc32edb0a7b833b Mon Sep 17 00:00:00 2001 From: Adam Heinrich Date: Sat, 13 Jan 2018 21:11:17 +0100 Subject: [PATCH 05/18] platforms/stm32: Ignore noise errors on USBUART --- src/platforms/stm32/usbuart.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/platforms/stm32/usbuart.c b/src/platforms/stm32/usbuart.c index 0d1772a3..a466ab55 100644 --- a/src/platforms/stm32/usbuart.c +++ b/src/platforms/stm32/usbuart.c @@ -217,7 +217,7 @@ void USBUSART_ISR(void) { uint32_t err = USART_SR(USBUSART); char c = usart_recv(USBUSART); - if (err & (USART_SR_ORE | USART_SR_FE)) + if (err & (USART_SR_ORE | USART_SR_FE | USART_SR_NE)) return; /* Turn on LED */ From 1fe870b8df453c65c882bbb83474cb8abce577e8 Mon Sep 17 00:00:00 2001 From: konsgn Date: Tue, 16 Jan 2018 13:23:36 -0500 Subject: [PATCH 06/18] added MKL27<128kB support --- src/target/kinetis.c | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/src/target/kinetis.c b/src/target/kinetis.c index b65366c7..a2d8cdf6 100644 --- a/src/target/kinetis.c +++ b/src/target/kinetis.c @@ -25,6 +25,8 @@ * KL25 Sub-family Reference Manual * * Extended with support for KL02 family + * + * And KL27 */ #include "general.h" @@ -110,11 +112,29 @@ bool kinetis_probe(target *t) kl_gen_add_flash(t, 0x00000000, 0x20000, 0x400); break; case 0x231: - t->driver = "KL27"; + t->driver = "KL27x128-256"; // MKL27 >=128kb target_add_ram(t, 0x1fffe000, 0x2000); target_add_ram(t, 0x20000000, 0x6000); kl_gen_add_flash(t, 0x00000000, 0x40000, 0x400); break; + case 0x271: + switch((sdid>>16)&0x0f){ + case 4: + t->driver = "KL27x32"; + target_add_ram(t, 0x1ffff800, 0x0800); + target_add_ram(t, 0x20000000, 0x1800); + kl_gen_add_flash(t, 0x00000000, 0x8000, 0x400); + break; + case 5: + t->driver = "KL27x64"; + target_add_ram(t, 0x1ffff000, 0x1000); + target_add_ram(t, 0x20000000, 0x3000); + kl_gen_add_flash(t, 0x00000000, 0x10000, 0x400); + break; + default: + return false; + } + break; case 0x021: /* KL02 family */ switch((sdid>>16) & 0x0f){ case 3: From 04fbabb2999df5d400e00846c1f6894986161666 Mon Sep 17 00:00:00 2001 From: Konsgn Date: Sun, 21 Jan 2018 23:43:01 -0500 Subject: [PATCH 07/18] mkl27 support --- src/target/kinetis.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/target/kinetis.c b/src/target/kinetis.c index a2d8cdf6..2b323bbf 100644 --- a/src/target/kinetis.c +++ b/src/target/kinetis.c @@ -112,7 +112,7 @@ bool kinetis_probe(target *t) kl_gen_add_flash(t, 0x00000000, 0x20000, 0x400); break; case 0x231: - t->driver = "KL27x128-256"; // MKL27 >=128kb + t->driver = "KL27x128"; // MKL27 >=128kb target_add_ram(t, 0x1fffe000, 0x2000); target_add_ram(t, 0x20000000, 0x6000); kl_gen_add_flash(t, 0x00000000, 0x40000, 0x400); From 0ddd8b55d756c4af3afbfbb00b4bfce4fd6ff53b Mon Sep 17 00:00:00 2001 From: jrwhite Date: Sun, 11 Mar 2018 14:35:38 -0500 Subject: [PATCH 08/18] divide-by-zero fix --- scripts/gdb.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/gdb.py b/scripts/gdb.py index eb28ed88..286627c2 100644 --- a/scripts/gdb.py +++ b/scripts/gdb.py @@ -241,7 +241,7 @@ class Target: block = 0 for i in range(len(self.blocks)): block += 1 - if callable(progress_cb): + if callable(progress_cb) and totalblocks > 0: progress_cb(block*100/totalblocks) # Erase the block From 1ee6d4503ea765fda22fbb65c081aaa7c2668678 Mon Sep 17 00:00:00 2001 From: Emil Fresk Date: Sat, 24 Mar 2018 16:44:59 +0100 Subject: [PATCH 09/18] Update to split 'special' into its sane parts (update from @mubes) --- src/target/cortexm.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/target/cortexm.c b/src/target/cortexm.c index abd64f4a..8c15128b 100644 --- a/src/target/cortexm.c +++ b/src/target/cortexm.c @@ -125,7 +125,10 @@ static const char tdesc_cortex_m[] = " " " " " " - " " + " " + " " + " " + " " " " ""; @@ -154,7 +157,10 @@ static const char tdesc_cortex_mf[] = " " " " " " - " " + " " + " " + " " + " " " " " " " " From a41d8cb97a25cd2a4f6c24257353238ca331f54b Mon Sep 17 00:00:00 2001 From: Mark Rages Date: Sun, 25 Mar 2018 13:37:51 -0600 Subject: [PATCH 10/18] Another nRF52 device id. (#315) --- src/target/nrf51.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/target/nrf51.c b/src/target/nrf51.c index 91f1d43f..985005d1 100644 --- a/src/target/nrf51.c +++ b/src/target/nrf51.c @@ -165,6 +165,7 @@ bool nrf51_probe(target *t) return true; case 0x00AC: /* nRF52832 Preview QFAA BA0 */ case 0x00C7: /* nRF52832 Revision 1 QFAA B00 */ + case 0x00E3: /* nRF52832-CIAA CSP */ t->driver = "Nordic nRF52"; target_add_ram(t, 0x20000000, 64*1024); nrf51_add_flash(t, 0x00000000, 512*1024, NRF52_PAGE_SIZE); From 471ce2547c42a55d4b3ce39d731e02cb35502ee5 Mon Sep 17 00:00:00 2001 From: Akila Ravihansa Perera Date: Mon, 26 Mar 2018 01:23:30 +0530 Subject: [PATCH 11/18] Added LPC17xx support (#317) --- src/Makefile | 1 + src/target/cortexm.c | 1 + src/target/jtag_scan.c | 2 + src/target/lpc17xx.c | 201 +++++++++++++++++++++++++++++++++++ src/target/lpc_common.h | 1 + src/target/target_internal.h | 1 + 6 files changed, 207 insertions(+) create mode 100644 src/target/lpc17xx.c diff --git a/src/Makefile b/src/Makefile index aef13fb3..6ef91994 100644 --- a/src/Makefile +++ b/src/Makefile @@ -40,6 +40,7 @@ SRC = \ lmi.c \ lpc_common.c \ lpc11xx.c \ + lpc17xx.c \ lpc15xx.c \ lpc43xx.c \ kinetis.c \ diff --git a/src/target/cortexm.c b/src/target/cortexm.c index abd64f4a..ec9608e1 100644 --- a/src/target/cortexm.c +++ b/src/target/cortexm.c @@ -299,6 +299,7 @@ bool cortexm_probe(ADIv5_AP_t *ap) PROBE(stm32l4_probe); PROBE(lpc11xx_probe); PROBE(lpc15xx_probe); + PROBE(lpc17xx_probe); PROBE(lpc43xx_probe); PROBE(sam3x_probe); PROBE(sam4l_probe); diff --git a/src/target/jtag_scan.c b/src/target/jtag_scan.c index a30a8050..5487efc0 100644 --- a/src/target/jtag_scan.c +++ b/src/target/jtag_scan.c @@ -61,6 +61,8 @@ static const struct jtag_dev_descr_s { .descr = "ST Microelectronics: STM32F4xx."}, {.idcode = 0x0BB11477 , .idmask = 0xFFFFFFFF, .descr = "NPX: LPC11C24."}, + {.idcode = 0x4BA00477 , .idmask = 0xFFFFFFFF, + .descr = "NXP: LPC17xx family."}, /* Just for fun, unsupported */ {.idcode = 0x8940303F, .idmask = 0xFFFFFFFF, .descr = "ATMEL: ATMega16."}, {.idcode = 0x0792603F, .idmask = 0xFFFFFFFF, .descr = "ATMEL: AT91SAM9261."}, diff --git a/src/target/lpc17xx.c b/src/target/lpc17xx.c new file mode 100644 index 00000000..5aae5a98 --- /dev/null +++ b/src/target/lpc17xx.c @@ -0,0 +1,201 @@ +/* + * This file is part of the Black Magic Debug project. + * + * Copyright (C) 2012 Gareth McMullin + * + * 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 . + */ + +#include "general.h" +#include "target.h" +#include "target_internal.h" +#include "cortexm.h" +#include "lpc_common.h" +#include "adiv5.h" + +#define IAP_PGM_CHUNKSIZE 4096 + +#define MIN_RAM_SIZE 8192 // LPC1751 +#define RAM_USAGE_FOR_IAP_ROUTINES 32 // IAP routines use 32 bytes at top of ram + +#define IAP_ENTRYPOINT 0x1FFF1FF1 +#define IAP_RAM_BASE 0x10000000 + +#define ARM_CPUID 0xE000ED00 +#define CORTEX_M3_CPUID 0x412FC230 // Cortex-M3 r2p0 +#define CORTEX_M3_CPUID_MASK 0xFF00FFF0 +#define MEMMAP 0x400FC040 +#define LPC17xx_JTAG_IDCODE 0x4BA00477 +#define LPC17xx_SWDP_IDCODE 0x2BA01477 +#define FLASH_NUM_SECTOR 30 + +struct flash_param { + uint16_t opcode; + uint16_t pad0; + uint32_t command; + uint32_t words[4]; + uint32_t result[5]; // return code and maximum of 4 result parameters +} __attribute__((aligned(4))); + +static void lpc17xx_extended_reset(target *t); +static bool lpc17xx_cmd_erase(target *t, int argc, const char *argv[]); +enum iap_status lpc17xx_iap_call(target *t, struct flash_param *param, enum iap_cmd cmd, ...); + +const struct command_s lpc17xx_cmd_list[] = { + {"erase_mass", lpc17xx_cmd_erase, "Erase entire flash memory"}, + {NULL, NULL, NULL} +}; + +void lpc17xx_add_flash(target *t, uint32_t addr, size_t len, size_t erasesize, unsigned int base_sector) +{ + struct lpc_flash *lf = lpc_add_flash(t, addr, len); + lf->f.blocksize = erasesize; + lf->base_sector = base_sector; + lf->f.buf_size = IAP_PGM_CHUNKSIZE; + lf->f.write_buf = lpc_flash_write_magic_vect; + lf->iap_entry = IAP_ENTRYPOINT; + lf->iap_ram = IAP_RAM_BASE; + lf->iap_msp = IAP_RAM_BASE + MIN_RAM_SIZE - RAM_USAGE_FOR_IAP_ROUTINES; +} + +bool +lpc17xx_probe(target *t) +{ + /* Read the IDCODE register from the SW-DP */ + ADIv5_AP_t *ap = cortexm_ap(t); + uint32_t ap_idcode = ap->dp->idcode; + + if (ap_idcode == LPC17xx_JTAG_IDCODE || ap_idcode == LPC17xx_SWDP_IDCODE) { + /* LPC176x/5x family. See UM10360.pdf 33.7 JTAG TAP Identification*/ + } else { + return false; + } + + uint32_t cpuid = target_mem_read32(t, ARM_CPUID); + if (((cpuid & CORTEX_M3_CPUID_MASK) == (CORTEX_M3_CPUID & CORTEX_M3_CPUID_MASK))) { + /* + * Now that we're sure it's a Cortex-M3, we need to halt the + * target and make an IAP call to get the part number. + * There appears to have no other method of reading the part number. + */ + target_halt_request(t); + + /* Read the Part ID */ + struct flash_param param; + lpc17xx_iap_call(t, ¶m, IAP_CMD_PARTID); + target_halt_resume(t, 0); + + if (param.result[0]) { + return false; + } + + switch (param.result[1]) { + case 0x26113F37: /* LPC1769 */ + case 0x26013F37: /* LPC1768 */ + case 0x26012837: /* LPC1767 */ + case 0x26013F33: /* LPC1766 */ + case 0x26013733: /* LPC1765 */ + case 0x26011922: /* LPC1764 */ + case 0x25113737: /* LPC1759 */ + case 0x25013F37: /* LPC1758 */ + case 0x25011723: /* LPC1756 */ + case 0x25011722: /* LPC1754 */ + case 0x25001121: /* LPC1752 */ + case 0x25001118: /* LPC1751 */ + case 0x25001110: /* LPC1751 (No CRP) */ + + t->driver = "LPC17xx"; + t->extended_reset = lpc17xx_extended_reset; + target_add_ram(t, 0x10000000, 0x8000); + target_add_ram(t, 0x2007C000, 0x4000); + target_add_ram(t, 0x20080000, 0x4000); + lpc17xx_add_flash(t, 0x00000000, 0x10000, 0x1000, 0); + lpc17xx_add_flash(t, 0x00010000, 0x70000, 0x8000, 16); + target_add_commands(t, lpc17xx_cmd_list, "LPC17xx"); + + return true; + } + } + return false; +} + +static bool +lpc17xx_cmd_erase(target *t, int argc, const char *argv[]) +{ + (void)argc; + (void)argv; + struct flash_param param; + + if (lpc17xx_iap_call(t, ¶m, IAP_CMD_PREPARE, 0, FLASH_NUM_SECTOR-1)) { + DEBUG("lpc17xx_cmd_erase: prepare failed %d\n", (unsigned int)param.result[0]); + return false; + } + + if (lpc17xx_iap_call(t, ¶m, IAP_CMD_ERASE, 0, FLASH_NUM_SECTOR-1, CPU_CLK_KHZ)) { + DEBUG("lpc17xx_cmd_erase: erase failed %d\n", (unsigned int)param.result[0]); + return false; + } + + if (lpc17xx_iap_call(t, ¶m, IAP_CMD_BLANKCHECK, 0, FLASH_NUM_SECTOR-1)) { + DEBUG("lpc17xx_cmd_erase: blankcheck failed %d\n", (unsigned int)param.result[0]); + return false; + } + tc_printf(t, "Erase OK.\n"); + return true; +} + +/** + * Target has been reset, make sure to remap the boot ROM + * from 0x00000000 leaving the user flash visible + */ +static void +lpc17xx_extended_reset(target *t) +{ + /* From ยง33.6 Debug memory re-mapping (Page 643) UM10360.pdf (Rev 2) */ + target_mem_write32(t, MEMMAP, 1); +} + +enum iap_status +lpc17xx_iap_call(target *t, struct flash_param *param, enum iap_cmd cmd, ...) { + param->opcode = ARM_THUMB_BREAKPOINT; + param->command = cmd; + + /* fill out the remainder of the parameters */ + va_list ap; + va_start(ap, cmd); + for (int i = 0; i < 4; i++) + param->words[i] = va_arg(ap, uint32_t); + va_end(ap); + + /* copy the structure to RAM */ + target_mem_write(t, IAP_RAM_BASE, param, sizeof(struct flash_param)); + + /* set up for the call to the IAP ROM */ + uint32_t regs[t->regs_size / sizeof(uint32_t)]; + target_regs_read(t, regs); + regs[0] = IAP_RAM_BASE + offsetof(struct flash_param, command); + regs[1] = IAP_RAM_BASE + offsetof(struct flash_param, result); + regs[REG_MSP] = IAP_RAM_BASE + MIN_RAM_SIZE - RAM_USAGE_FOR_IAP_ROUTINES; + regs[REG_LR] = IAP_RAM_BASE | 1; + regs[REG_PC] = IAP_ENTRYPOINT; + target_regs_write(t, regs); + + /* start the target and wait for it to halt again */ + target_halt_resume(t, false); + while (!target_halt_poll(t, NULL)); + + /* copy back just the parameters structure */ + target_mem_read(t, (void *)param, IAP_RAM_BASE, sizeof(struct flash_param)); + return param->result[0]; +} \ No newline at end of file diff --git a/src/target/lpc_common.h b/src/target/lpc_common.h index dd7dc881..5b132263 100644 --- a/src/target/lpc_common.h +++ b/src/target/lpc_common.h @@ -26,6 +26,7 @@ enum iap_cmd { IAP_CMD_PROGRAM = 51, IAP_CMD_ERASE = 52, IAP_CMD_BLANKCHECK = 53, + IAP_CMD_PARTID = 54, IAP_CMD_SET_ACTIVE_BANK = 60, }; diff --git a/src/target/target_internal.h b/src/target/target_internal.h index 33f16632..b5ebe708 100644 --- a/src/target/target_internal.h +++ b/src/target/target_internal.h @@ -175,6 +175,7 @@ bool stm32l4_probe(target *t); bool lmi_probe(target *t); bool lpc11xx_probe(target *t); bool lpc15xx_probe(target *t); +bool lpc17xx_probe(target *t); bool lpc43xx_probe(target *t); bool sam3x_probe(target *t); bool sam4l_probe(target *t); From 31965a5bbc5b9560c575e7b7701e75b2e2c52b87 Mon Sep 17 00:00:00 2001 From: Christopher Woodall Date: Sun, 25 Mar 2018 17:43:33 -0400 Subject: [PATCH 12/18] Added support for k64 (#301) --- src/target/kinetis.c | 87 +++++++++++++++++++++++++++++++++----------- 1 file changed, 65 insertions(+), 22 deletions(-) diff --git a/src/target/kinetis.c b/src/target/kinetis.c index b65366c7..4e18711a 100644 --- a/src/target/kinetis.c +++ b/src/target/kinetis.c @@ -25,6 +25,12 @@ * KL25 Sub-family Reference Manual * * Extended with support for KL02 family + * + * Extended with support for K64 family with info from K22P64M50SF4RM: + * K22 Sub-Family Reference Manual + * + * Extended with support for K64 family with info from K64P144M120SF5RM: + * K64 Sub-Family Reference Manual, Rev. 2, */ #include "general.h" @@ -51,7 +57,9 @@ #define FTFA_CMD_CHECK_ERASE 0x01 #define FTFA_CMD_PROGRAM_CHECK 0x02 #define FTFA_CMD_READ_RESOURCE 0x03 -#define FTFA_CMD_PROGRAM_LONGWORD 0x06 +#define FTFA_CMD_PROGRAM_LONGWORD 0x06 +/* Part of the FTFE module for K64 */ +#define FTFE_CMD_PROGRAM_PHRASE 0x07 #define FTFA_CMD_ERASE_SECTOR 0x09 #define FTFA_CMD_CHECK_ERASE_ALL 0x40 #define FTFA_CMD_READ_ONCE 0x41 @@ -59,7 +67,9 @@ #define FTFA_CMD_ERASE_ALL 0x44 #define FTFA_CMD_BACKDOOR_ACCESS 0x45 -#define KL_GEN_PAGESIZE 0x400 +#define KL_WRITE_LEN 4 +/* 8 byte phrases need to be written to the k64 flash */ +#define K64_WRITE_LEN 8 static bool kinetis_cmd_unsafe(target *t, int argc, char *argv[]); static bool unsafe_enabled; @@ -85,7 +95,8 @@ static int kl_gen_flash_write(struct target_flash *f, static int kl_gen_flash_done(struct target_flash *f); static void kl_gen_add_flash(target *t, - uint32_t addr, size_t length, size_t erasesize) + uint32_t addr, size_t length, size_t erasesize, + size_t write_len) { struct target_flash *f = calloc(1, sizeof(*f)); f->start = addr; @@ -94,7 +105,7 @@ static void kl_gen_add_flash(target *t, f->erase = kl_gen_flash_erase; f->write = kl_gen_flash_write; f->done = kl_gen_flash_done; - f->align = 4; + f->align = write_len; f->erased = 0xff; target_add_flash(t, f); } @@ -107,13 +118,13 @@ bool kinetis_probe(target *t) t->driver = "KL25"; target_add_ram(t, 0x1ffff000, 0x1000); target_add_ram(t, 0x20000000, 0x3000); - kl_gen_add_flash(t, 0x00000000, 0x20000, 0x400); + kl_gen_add_flash(t, 0x00000000, 0x20000, 0x400, KL_WRITE_LEN); break; case 0x231: t->driver = "KL27"; target_add_ram(t, 0x1fffe000, 0x2000); target_add_ram(t, 0x20000000, 0x6000); - kl_gen_add_flash(t, 0x00000000, 0x40000, 0x400); + kl_gen_add_flash(t, 0x00000000, 0x40000, 0x400, KL_WRITE_LEN); break; case 0x021: /* KL02 family */ switch((sdid>>16) & 0x0f){ @@ -121,19 +132,19 @@ bool kinetis_probe(target *t) t->driver = "KL02x32"; target_add_ram(t, 0x1FFFFC00, 0x400); target_add_ram(t, 0x20000000, 0xc00); - kl_gen_add_flash(t, 0x00000000, 0x7FFF, 0x400); + kl_gen_add_flash(t, 0x00000000, 0x7FFF, 0x400, KL_WRITE_LEN); break; case 2: t->driver = "KL02x16"; target_add_ram(t, 0x1FFFFE00, 0x200); target_add_ram(t, 0x20000000, 0x600); - kl_gen_add_flash(t, 0x00000000, 0x3FFF, 0x400); + kl_gen_add_flash(t, 0x00000000, 0x3FFF, 0x400, KL_WRITE_LEN); break; case 1: t->driver = "KL02x8"; target_add_ram(t, 0x1FFFFF00, 0x100); target_add_ram(t, 0x20000000, 0x300); - kl_gen_add_flash(t, 0x00000000, 0x1FFF, 0x400); + kl_gen_add_flash(t, 0x00000000, 0x1FFF, 0x400, KL_WRITE_LEN); break; default: return false; @@ -143,14 +154,25 @@ bool kinetis_probe(target *t) t->driver = "KL03"; target_add_ram(t, 0x1ffffe00, 0x200); target_add_ram(t, 0x20000000, 0x600); - kl_gen_add_flash(t, 0, 0x8000, 0x400); + kl_gen_add_flash(t, 0, 0x8000, 0x400, KL_WRITE_LEN); break; case 0x220: /* K22F family */ t->driver = "K22F"; target_add_ram(t, 0x1c000000, 0x4000000); target_add_ram(t, 0x20000000, 0x100000); - kl_gen_add_flash(t, 0, 0x40000, 0x800); - kl_gen_add_flash(t, 0x40000, 0x40000, 0x800); + kl_gen_add_flash(t, 0, 0x40000, 0x800, KL_WRITE_LEN); + kl_gen_add_flash(t, 0x40000, 0x40000, 0x800, KL_WRITE_LEN); + break; + case 0x620: /* K64F family. */ + /* This should be 0x640, but according to the errata sheet + * (KINETIS_1N83J) K64 and K24's will show up with the + * subfamily nibble as 2 + */ + t->driver = "K64"; + target_add_ram(t, 0x1FFF0000, 0x10000); + target_add_ram(t, 0x20000000, 0x30000); + kl_gen_add_flash(t, 0, 0x80000, 0x1000, K64_WRITE_LEN); + kl_gen_add_flash(t, 0x80000, 0x80000, 0x1000, K64_WRITE_LEN); break; default: return false; @@ -200,8 +222,9 @@ static int kl_gen_flash_erase(struct target_flash *f, target_addr addr, size_t l { while (len) { if (kl_gen_command(f->t, FTFA_CMD_ERASE_SECTOR, addr, NULL)) { - len -= KL_GEN_PAGESIZE; - addr += KL_GEN_PAGESIZE; + /* Different targets have different flash erase sizes */ + len -= f->blocksize; + addr += f->blocksize; } else { return 1; } @@ -223,11 +246,19 @@ static int kl_gen_flash_write(struct target_flash *f, FLASH_SECURITY_BYTE_UNSECURED; } + /* Determine write command based on the alignment. */ + uint8_t write_cmd; + if (f->align == K64_WRITE_LEN) { + write_cmd = FTFE_CMD_PROGRAM_PHRASE; + } else { + write_cmd = FTFA_CMD_PROGRAM_LONGWORD; + } + while (len) { - if (kl_gen_command(f->t, FTFA_CMD_PROGRAM_LONGWORD, dest, src)) { - len -= 4; - dest += 4; - src += 4; + if (kl_gen_command(f->t, write_cmd, dest, src)) { + len -= f->align; + dest += f->align; + src += f->align; } else { return 1; } @@ -245,10 +276,22 @@ static int kl_gen_flash_done(struct target_flash *f) FLASH_SECURITY_BYTE_UNSECURED) return 0; - uint32_t val = target_mem_read32(f->t, FLASH_SECURITY_BYTE_ADDRESS); - val = (val & 0xffffff00) | FLASH_SECURITY_BYTE_UNSECURED; - kl_gen_command(f->t, FTFA_CMD_PROGRAM_LONGWORD, - FLASH_SECURITY_BYTE_ADDRESS, (uint8_t*)&val); + /* Load the security byte based on the alignment (determine 8 byte phrases + * vs 4 byte phrases). + */ + if (f->align == 8) { + uint32_t vals[2]; + vals[0] = target_mem_read32(f->t, FLASH_SECURITY_BYTE_ADDRESS-4); + vals[1] = target_mem_read32(f->t, FLASH_SECURITY_BYTE_ADDRESS); + vals[1] = (vals[1] & 0xffffff00) | FLASH_SECURITY_BYTE_UNSECURED; + kl_gen_command(f->t, FTFE_CMD_PROGRAM_PHRASE, + FLASH_SECURITY_BYTE_ADDRESS - 4, (uint8_t*)vals); + } else { + uint32_t val = target_mem_read32(f->t, FLASH_SECURITY_BYTE_ADDRESS); + val = (val & 0xffffff00) | FLASH_SECURITY_BYTE_UNSECURED; + kl_gen_command(f->t, FTFA_CMD_PROGRAM_LONGWORD, + FLASH_SECURITY_BYTE_ADDRESS, (uint8_t*)&val); + } return 0; } From 76bfb4929d02c1ca7ac069296c524e7650dadb87 Mon Sep 17 00:00:00 2001 From: Gareth McMullin Date: Tue, 27 Mar 2018 12:57:42 +1300 Subject: [PATCH 13/18] Use lowercase register names. --- src/target/cortexm.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/target/cortexm.c b/src/target/cortexm.c index 8c15128b..474f28e3 100644 --- a/src/target/cortexm.c +++ b/src/target/cortexm.c @@ -125,10 +125,10 @@ static const char tdesc_cortex_m[] = " " " " " " - " " - " " - " " - " " + " " + " " + " " + " " " " ""; @@ -157,10 +157,10 @@ static const char tdesc_cortex_mf[] = " " " " " " - " " - " " - " " - " " + " " + " " + " " + " " " " " " " " From 3e3e4504088ca9f579aaedf3da273af983de0fe1 Mon Sep 17 00:00:00 2001 From: Uwe Bonnes Date: Thu, 26 Oct 2017 19:05:18 +0200 Subject: [PATCH 14/18] cdcacm.c: Use less buffer for the usb_uart buffers and reallocate. With 128 bytes for both usb_uart buffers, traceswo gives errors! Keep the size for the receive buffer and diminisch the transmit buffer, as there is no flow control to the device. Probably related to https://github.com/libopencm3/libopencm3/issues/477 --- src/platforms/common/cdcacm.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/platforms/common/cdcacm.c b/src/platforms/common/cdcacm.c index 62cc419b..ccf6706e 100644 --- a/src/platforms/common/cdcacm.c +++ b/src/platforms/common/cdcacm.c @@ -517,7 +517,7 @@ static void cdcacm_set_config(usbd_device *dev, uint16_t wValue) /* Serial interface */ usbd_ep_setup(dev, 0x03, USB_ENDPOINT_ATTR_BULK, - CDCACM_PACKET_SIZE, usbuart_usb_out_cb); + CDCACM_PACKET_SIZE / 2, usbuart_usb_out_cb); usbd_ep_setup(dev, 0x83, USB_ENDPOINT_ATTR_BULK, CDCACM_PACKET_SIZE, usbuart_usb_in_cb); usbd_ep_setup(dev, 0x84, USB_ENDPOINT_ATTR_INTERRUPT, 16, NULL); From 93bc3a155aed37158897d5d24b4070b2e6aaf61b Mon Sep 17 00:00:00 2001 From: Uwe Bonnes Date: Thu, 26 Oct 2017 15:11:55 +0200 Subject: [PATCH 15/18] traceswo: Provide command option for async swo. --- src/command.c | 18 ++++++++++++++---- src/platforms/common/traceswo.h | 2 +- 2 files changed, 15 insertions(+), 5 deletions(-) diff --git a/src/command.c b/src/command.c index 9594c3a5..648ec882 100644 --- a/src/command.c +++ b/src/command.c @@ -56,7 +56,7 @@ static bool cmd_hard_srst(void); static bool cmd_target_power(target *t, int argc, const char **argv); #endif #ifdef PLATFORM_HAS_TRACESWO -static bool cmd_traceswo(void); +static bool cmd_traceswo(target *t, int argc, const char **argv); #endif #ifdef PLATFORM_HAS_DEBUG static bool cmd_debug_bmp(target *t, int argc, const char **argv); @@ -75,7 +75,7 @@ const struct command_s cmd_list[] = { {"tpwr", (cmd_handler)cmd_target_power, "Supplies power to the target: (enable|disable)"}, #endif #ifdef PLATFORM_HAS_TRACESWO - {"traceswo", (cmd_handler)cmd_traceswo, "Start trace capture" }, + {"traceswo", (cmd_handler)cmd_traceswo, "Start trace capture [(baudrate) for async swo]" }, #endif #ifdef PLATFORM_HAS_DEBUG {"debug_bmp", (cmd_handler)cmd_debug_bmp, "Output BMP \"debug\" strings to the second vcom: (enable|disable)"}, @@ -277,10 +277,20 @@ static bool cmd_target_power(target *t, int argc, const char **argv) #endif #ifdef PLATFORM_HAS_TRACESWO -static bool cmd_traceswo(void) +static bool cmd_traceswo(target *t, int argc, const char **argv) { +#if defined(STM32L0) || defined(STM32F3) || defined(STM32F4) + extern char serial_no[13]; +#else extern char serial_no[9]; - traceswo_init(); +#endif + uint32_t baudrate = 0; + (void)t; + + if (argc > 1) + baudrate = atoi(argv[1]); + + traceswo_init(baudrate); gdb_outf("%s:%02X:%02X\n", serial_no, 5, 0x85); return true; } diff --git a/src/platforms/common/traceswo.h b/src/platforms/common/traceswo.h index 5566f944..fb57a892 100644 --- a/src/platforms/common/traceswo.h +++ b/src/platforms/common/traceswo.h @@ -22,7 +22,7 @@ #include -void traceswo_init(void); +void traceswo_init(uint32_t baudrate); void trace_buf_drain(usbd_device *dev, uint8_t ep); #endif From fc25a3339aeb511153a787863c71363b6e953e94 Mon Sep 17 00:00:00 2001 From: Uwe Bonnes Date: Thu, 26 Oct 2017 15:13:03 +0200 Subject: [PATCH 16/18] traceswoasync: Implement async swo for stm32. Use for stlink. Uses dma with large buffer. Pull up swo to provide idle level on unconnected swo pin. --- src/platforms/stlink/Makefile.inc | 1 + src/platforms/stlink/platform.h | 20 ++++- src/platforms/stm32/traceswoasync.c | 133 ++++++++++++++++++++++++++++ 3 files changed, 152 insertions(+), 2 deletions(-) create mode 100644 src/platforms/stm32/traceswoasync.c diff --git a/src/platforms/stlink/Makefile.inc b/src/platforms/stlink/Makefile.inc index 18486e83..4bde36c6 100644 --- a/src/platforms/stlink/Makefile.inc +++ b/src/platforms/stlink/Makefile.inc @@ -26,6 +26,7 @@ SRC += cdcacm.c \ serialno.c \ timing.c \ timing_stm32.c \ + traceswoasync.c \ stlink_common.c \ all: blackmagic.bin blackmagic_dfu.bin blackmagic_dfu.hex dfu_upgrade.bin dfu_upgrade.hex diff --git a/src/platforms/stlink/platform.h b/src/platforms/stlink/platform.h index 5cb29ccc..83eb90a3 100644 --- a/src/platforms/stlink/platform.h +++ b/src/platforms/stlink/platform.h @@ -68,6 +68,9 @@ #define LED_PORT_UART GPIOC #define LED_UART GPIO14 +#define PLATFORM_HAS_TRACESWO 1 +#define NUM_TRACE_PACKETS (192) /* This is an 12K buffer */ + #define TMS_SET_MODE() \ gpio_set_mode(TMS_PORT, GPIO_MODE_OUTPUT_50_MHZ, \ GPIO_CNF_OUTPUT_PUSHPULL, TMS_PIN); @@ -87,13 +90,12 @@ #define USB_ISR usb_lp_can_rx0_isr /* Interrupt priorities. Low numbers are high priority. * For now USART2 preempts USB which may spin while buffer is drained. - * TIM3 is used for traceswo capture and must be highest priority. */ #define IRQ_PRI_USB (2 << 4) #define IRQ_PRI_USBUSART (1 << 4) #define IRQ_PRI_USBUSART_TIM (3 << 4) #define IRQ_PRI_USB_VBUS (14 << 4) -#define IRQ_PRI_TIM3 (0 << 4) +#define IRQ_PRI_SWO_DMA (1 << 4) #define USBUSART USART2 #define USBUSART_CR1 USART2_CR1 @@ -115,6 +117,20 @@ int usbuart_debug_write(const char *buf, size_t len); # define DEBUG(...) #endif +/* On F103, only USART1 is on AHB2 and can reach 4.5 MBaud at 72 MHz.*/ +#define SWO_UART USART1 +#define SWO_UART_DR USART1_DR +#define SWO_UART_CLK RCC_USART1 +#define SWO_UART_PORT GPIOA +#define SWO_UART_RX_PIN GPIO10 + +/* This DMA channel is set by the USART in use */ +#define SWO_DMA_BUS DMA1 +#define SWO_DMA_CLK RCC_DMA1 +#define SWO_DMA_CHAN DMA_CHANNEL5 +#define SWO_DMA_IRQ NVIC_DMA1_CHANNEL5_IRQ +#define SWO_DMA_ISR(x) dma1_channel5_isr(x) + extern uint16_t led_idle_run; #define LED_IDLE_RUN led_idle_run #define SET_RUN_STATE(state) {running_status = (state);} diff --git a/src/platforms/stm32/traceswoasync.c b/src/platforms/stm32/traceswoasync.c new file mode 100644 index 00000000..dfa6baee --- /dev/null +++ b/src/platforms/stm32/traceswoasync.c @@ -0,0 +1,133 @@ +/* + * This file is part of the Black Magic Debug project. + * + * Based on work that is Copyright (C) 2017 Black Sphere Technologies Ltd. + * Copyright (C) 2017 Dave Marples + * + * 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 . + */ + +/* This file implements capture of the TRACESWO output using ASYNC signalling. + * + * ARM DDI 0403D - ARMv7M Architecture Reference Manual + * ARM DDI 0337I - Cortex-M3 Technical Reference Manual + * ARM DDI 0314H - CoreSight Components Technical Reference Manual + */ + +/* TDO/TRACESWO signal comes into the SWOUSART RX pin. + */ + +#include "general.h" +#include "cdcacm.h" +#include "platform.h" + +#include +#include +#include +#include +#include +#include + +/* For speed this is set to the USB transfer size */ +#define FULL_SWO_PACKET (64) +/* Default line rate....used as default for a request without baudrate */ +#define DEFAULTSPEED (2250000) + +static volatile uint32_t w; /* Packet currently received via UART */ +static volatile uint32_t r; /* Packet currently waiting to transmit to USB */ +/* Packets arrived from the SWO interface */ +static uint8_t trace_rx_buf[NUM_TRACE_PACKETS * FULL_SWO_PACKET]; +/* Packet pingpong buffer used for receiving packets */ +static uint8_t pingpong_buf[2 * FULL_SWO_PACKET]; + +void trace_buf_drain(usbd_device *dev, uint8_t ep) +{ + static volatile char inBufDrain; + + /* If we are already in this routine then we don't need to come in again */ + if (__atomic_test_and_set (&inBufDrain, __ATOMIC_RELAXED)) + return; + /* Attempt to write everything we buffered */ + if ((w != r) && (usbd_ep_write_packet(dev, ep, + &trace_rx_buf[r * FULL_SWO_PACKET], + FULL_SWO_PACKET))) + r =(r + 1) % NUM_TRACE_PACKETS; + __atomic_clear (&inBufDrain, __ATOMIC_RELAXED); +} + +void traceswo_setspeed(uint32_t baudrate) +{ + dma_disable_channel(SWO_DMA_BUS, SWO_DMA_CHAN); + usart_disable(SWO_UART); + usart_set_baudrate(SWO_UART, baudrate); + usart_set_databits(SWO_UART, 8); + usart_set_stopbits(SWO_UART, USART_STOPBITS_1); + usart_set_mode(SWO_UART, USART_MODE_RX); + usart_set_parity(SWO_UART, USART_PARITY_NONE); + usart_set_flow_control(SWO_UART, USART_FLOWCONTROL_NONE); + + /* Set up DMA channel*/ + dma_channel_reset(SWO_DMA_BUS, SWO_DMA_CHAN); + dma_set_peripheral_address(SWO_DMA_BUS, SWO_DMA_CHAN, + (uint32_t)&SWO_UART_DR); + dma_set_read_from_peripheral(SWO_DMA_BUS, SWO_DMA_CHAN); + dma_enable_memory_increment_mode(SWO_DMA_BUS, SWO_DMA_CHAN); + dma_set_peripheral_size(SWO_DMA_BUS, SWO_DMA_CHAN, DMA_CCR_PSIZE_8BIT); + dma_set_memory_size(SWO_DMA_BUS, SWO_DMA_CHAN, DMA_CCR_MSIZE_8BIT); + dma_set_priority(SWO_DMA_BUS, SWO_DMA_CHAN, DMA_CCR_PL_HIGH); + dma_enable_transfer_complete_interrupt(SWO_DMA_BUS, SWO_DMA_CHAN); + dma_enable_half_transfer_interrupt(SWO_DMA_BUS, SWO_DMA_CHAN); + dma_enable_circular_mode(SWO_DMA_BUS,SWO_DMA_CHAN); + + usart_enable(SWO_UART); + nvic_enable_irq(SWO_DMA_IRQ); + w = r = 0; + dma_set_memory_address(SWO_DMA_BUS, SWO_DMA_CHAN, (uint32_t)pingpong_buf); + dma_set_number_of_data(SWO_DMA_BUS, SWO_DMA_CHAN, 2 * FULL_SWO_PACKET); + dma_enable_channel(SWO_DMA_BUS, SWO_DMA_CHAN); + usart_enable_rx_dma(SWO_UART); +} + +void SWO_DMA_ISR(void) +{ + if (DMA_ISR(SWO_DMA_BUS) & DMA_ISR_HTIF(SWO_DMA_CHAN)) { + DMA_IFCR(SWO_DMA_BUS) |= DMA_ISR_HTIF(SWO_DMA_CHAN); + memcpy(&trace_rx_buf[w * FULL_SWO_PACKET], pingpong_buf, + FULL_SWO_PACKET); + } + if (DMA_ISR(SWO_DMA_BUS) & DMA_ISR_TCIF(SWO_DMA_CHAN)) { + DMA_IFCR(SWO_DMA_BUS) |= DMA_ISR_TCIF(SWO_DMA_CHAN); + memcpy(&trace_rx_buf[w * FULL_SWO_PACKET], + &pingpong_buf[FULL_SWO_PACKET], FULL_SWO_PACKET); + } + w = (w + 1) % NUM_TRACE_PACKETS; + trace_buf_drain(usbdev, 0x85); +} + +void traceswo_init(uint32_t baudrate) +{ + if (!baudrate) + baudrate = DEFAULTSPEED; + + rcc_periph_clock_enable(SWO_UART_CLK); + rcc_periph_clock_enable(SWO_DMA_CLK); + + gpio_set_mode(SWO_UART_PORT, GPIO_MODE_INPUT, + GPIO_CNF_INPUT_PULL_UPDOWN, SWO_UART_RX_PIN); + /* Pull SWO pin high to keep open SWO line ind uart idle state!*/ + gpio_set(SWO_UART_PORT, SWO_UART_RX_PIN); + nvic_set_priority(SWO_DMA_IRQ, IRQ_PRI_SWO_DMA); + nvic_enable_irq(SWO_DMA_IRQ); + traceswo_setspeed(baudrate); +} From 29cdba0d70d5a4e0453c451981a104668a427abd Mon Sep 17 00:00:00 2001 From: Uwe Bonnes Date: Fri, 27 Oct 2017 16:15:46 +0200 Subject: [PATCH 17/18] SWO: Some explanations and a test program. --- UsingSWO | 254 +++++++++++++++++++++ scripts/swolisten.c | 544 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 798 insertions(+) create mode 100644 UsingSWO create mode 100644 scripts/swolisten.c diff --git a/UsingSWO b/UsingSWO new file mode 100644 index 00000000..e582df53 --- /dev/null +++ b/UsingSWO @@ -0,0 +1,254 @@ +SWO is a datastream that comes out of a single pin when the debug interface +is in SWD mode. It can be encoded either using NRZ (UART) or RZ (Manchester) +formats. The pin is a dedicated one that would be used for TDO when the +debug interface is in JTAG mode. On the STM32 it's port PB3. + +When in NRZ mode the SWO data rate that comes out of the chip _must_ match +the rate that the debugger expects. By default on BMP the baudrate is +2.25MBps but that can be changed as an optional parameter to the monitor +traceswo command, like this; + +monitor traceswo 115200 + +....would set the swo output at the low speed of 115kbps. + +We are constrained on maximum input speed by both the capabilities of the +BMP STM32F103 USART and the ability to get the packets back out over the USB +link. The UART baudrate is set by b=(72x10^6)/(16*d)...so for d=1 that means +a maximum speed of 4.5Mbps. For continious streaming that turns out to be +_too_ fast for the USB link, so the next available option is the 2.25Mbps +that we use. ....you can safely use the 4.5Mbps setting if your debug data +is bursty, or if you're using a different CPU to the STM32F103 as your BMP +host, but you potentially run the risk of losing packets if you have long +runs of sending which the usb cannot flush in time (there's a 12K buffer, so +the it is a pretty long run before it becomes a problem). + +Note that the baudrate equation means there are only certain speeds +available. The highest half dozen are; + +1 4.50 Mbps +2 2.25 Mbps +3 1.50 Mbps +4 1.125 Mbps +5 0.900 Mbps +6 0.750 Mbps + +...the USART will cope with some timing slip, but it's advisible to stay as +close to these values as you can. As the speed comes down the spread between +each valid value so mis-timing is less of an issue. The 'monitor traceswo +' command will automatically find the closest divisor to the value you +set for the speed, so be aware the error could be significant. + +Depending on what you're using to wake up SWO on the target side, you may +need code to get it into the correct mode and emitting data. You can do that +via gdb direct memory accesses, or from program code. + +An example for a STM32F103 for the UART (NRZ) data format that we use; + + /* STM32 specific configuration to enable the TRACESWO IO pin */ + RCC->APB2ENR |= RCC_APB2ENR_AFIOEN; + AFIO->MAPR |= (2 << 24); // Disable JTAG to release TRACESWO + DBGMCU->CR |= DBGMCU_CR_TRACE_IOEN; // Enable IO trace pins + + *((volatile unsigned *)(0xE0040010)) = 31; // Output bits at 72000000/(31+1)=2.25MHz. + *((volatile unsigned *)(0xE00400F0)) = 2; // Use Async mode (1 for RZ/Manchester) + *((volatile unsigned *)(0xE0040304)) = 0; // Disable formatter + + /* Configure instrumentation trace macroblock */ + ITM->LAR = 0xC5ACCE55; + ITM->TCR = 0x00010005; + ITM->TER = 0xFFFFFFFF; // Enable all stimulus ports + +Code for the STM32L476 might look like: +#define BAUDRATE 115200 + DBGMCU->CR |= DBGMCU_CR_TRACE_IOEN; /* Enable IO pins for Async trace */ + uint32_t divisor, clk_frequency; + clk_frequency = NutGetCpuClock(); + divisor = clk_frequency / BAUDRATE; + divisor--; + TPI->CSPSR = 1; /* port size = 1 bit */ + TPI->ACPR = divisor; + TPI->SPPR = 2; /*Use Async mode pin protocol */ + TPI->FFCR = 0x00; /* Bypass the TPIU formatter and send output directly*/ + +/* Configure Trace Port Interface Unit */ + CoreDebug->DEMCR |= CoreDebug_DEMCR_TRCENA_Msk; // Enable access to registers + DWT->CTRL = 0x400003FE; // DWT needs to provide sync for ITM + ITM->LAR = 0xC5ACCE55; // Allow access to the Control Register + ITM->TPR = 0x0000000F; // Trace access privilege from user level code, please + ITM->TCR = 0x0001000D; // ITM_TCR_TraceBusID_Msk | ITM_TCR_DWTENA_Msk | ITM_TCR_SYNCENA_Msk | ITM_TCR_ITMENA_Msk + ITM->TER = 1; // Only Enable stimulus port 1 + + while(1) { + for (uint32_t i = 'A'; i <= 'Z'; i++) { + ITM_SendChar(i); + NutSleep(1); + } + } + +If you're using RZ mode (e.g. on a genuine BMP) then you will need the trace +output speed to be quite a lot lower...in the order of 200kHz by means of +changing the divisor to something like 359. That's because the STM32F103 +doesn't have a dedicated RZ decoder so it all has to be done in +software. The advantage of RZ is that the probe can adapt to the speed of +the target, so you don't have to set the speed on the probe in the monitor +traceswo command, and it will be tolerant of different speeds. + +The SWO data appears on USB Interface 5, Endpoint 5. + +SWOListen +========= +A program swolisten.c is found in ./scripts which will listen to this +endpoint, decode the datastream, and output it to a set of unix fifos which +can then be used as the input to other programs (e.g. cat, or something more +sophisticated like gnuplot, octave or whatever). This program doesn't care +if the data originates from a RZ or NRZ port, or at what speed. + +Note that swolisten can be used with either BMP firmware, or with a +conventional TTL serial dongle. See at the bottom of this file for +information on how to use a dongle. + +The command line to build the swolisten tool is; + +gcc -I /usr/local/include/libusb-1.0 -L /usr/local/lib -lusb-1.0 swolisten.c -o swolisten + +For Opensuse: +gcc -I /usr/include/libusb-1.0 -lusb-1.0 swolisten.c swolisten -std=gnu99 -g -Og + +...you will obviously need to change the paths to your libusb files. + +Attach to BMP to your PC: +Start gdb: "arm-none-eabi-gdb" +Choose bmp as target, like: + "target extended /dev/ttyACM0(*)" +Start SWO output: "mon traceswo" +If async SWO is used, give the baudrate your device sends +out as argument. 2.25 MBaud is the default, for the STM32L476 example above +the command would be: "mon traceswo 115200(*)". +Scan the SWD "mon swdp_scan" +Attach to the device: : "attach 1" +Start the program: "r". +(*) Your milage may vary +Now start swolisten without further options. + +By default the tool will create fifos for the first 32 channels in a +directory swo (which you will need to create) as follows; + +>ls swo/ +chan00 chan02 chan04 chan06 chan08 chan0A chan0C chan0E chan10 chan12 chan14 +chan16 chan18 chan1A chan1C chan1E chan01 chan03 chan05 chan07 chan09 chan0B +chan0D chan0F chan11 chan13 chan15 chan17 chan19 chan1B chan1D chan1F + +>cat swo/channel0 +<> + +With the F103 and L476 examples above, an endless stream of +"ABCDEFGHIJKLMNOPQRSTUVWXYZ" should be seen. During reset of the target +device, no output will appear, but with release of reset output restarts. + +Information about command line options can be found with the -h option. +swolisten is specifically designed to be 'hardy' to probe and target +disconnects and restarts (y'know, like you get in the real world). The +intention being to give you streams whenever it can get them. It does _not_ +require gdb to be running. For the time being traceswo is not turned on by +default in the BMP to avoid possible interactions and making the overall +thing less reliable so You do need gdb to send the initial 'monitor +traceswo' to the probe, but beyond that there's no requirement for gdb to be +present. + +Reliability +=========== + +A whole chunk of work has gone into making sure the dataflow over the SWO +link is reliable. The TL;DR is that the link _is_ reliable. There are +factors outside of our control (i.e. the USB bus you connect to) that could +potentially break the reliabilty but there's not too much we can do about +that since the SWO link is unidirectional (no opportunity for +re-transmits). The following section provides evidence for the claim that +the link is good; + +A test 'mule' sends data flat out to the link at the maximum data rate of +2.25Mbps using a loop like the one below; + +while (1) +{ + for (uint32_t r=0; r<26; r++) + { + for (uint32_t g=0; g<31; g++) + { + ITM_SendChar('A'+r); + } + ITM_SendChar('\n'); + } +} + +100MB of data (more than 200MB of actual SWO packets, due to the encoding) was sent from the mule to the BMP where the +output from swolisten chan00 was cat'ted into a file; + +>cat swo/chan00 > o + +....this process was interrupted once the file had grown to 100MB. The first +and last lines were removed from it (these represent previously buffered +data and an incomplete packet at the point where the capture was +interrupted) and the resulting file analysed for consistency; + +> sort o | uniq -c + +The output was; + +126462 AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +126462 BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB +126462 CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC +126462 DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD +126461 EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE +126461 FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF +126461 GGGGGGGGGGGGGGGGGGGGGGGGGGGGGGG +126461 HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH +126461 IIIIIIIIIIIIIIIIIIIIIIIIIIIIIII +126461 JJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJ +126461 KKKKKKKKKKKKKKKKKKKKKKKKKKKKKKK +126461 LLLLLLLLLLLLLLLLLLLLLLLLLLLLLLL +126461 MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM +126461 NNNNNNNNNNNNNNNNNNNNNNNNNNNNNNN +126461 OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO +126461 PPPPPPPPPPPPPPPPPPPPPPPPPPPPPPP +126461 QQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQ +126461 RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR +126461 SSSSSSSSSSSSSSSSSSSSSSSSSSSSSSS +126461 TTTTTTTTTTTTTTTTTTTTTTTTTTTTTTT +126461 UUUUUUUUUUUUUUUUUUUUUUUUUUUUUUU +126461 VVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV +126461 WWWWWWWWWWWWWWWWWWWWWWWWWWWWWWW +126461 XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +126461 YYYYYYYYYYYYYYYYYYYYYYYYYYYYYYY +126461 ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ + +(On inspection, the last line of recorded data was indeed a 'D' line). + +Swolisten, using a TTL Serial Dongle +==================================== + +The NRZ data that comes out of the SWO is just UART formatted, but in a +frame. swolisten has been extended to accomodate TTL Serial Dongles that +can pick this up. Success has been had with CP2102 dongles at up to 921600 +baud. + +To use this mode just connect SWO to the RX pin of your dongle, and start +swolisten with parmeters representing the speed and port. An example; + +>./swolisten -p /dev/cu.SLAB_USBtoUART -v -b swo/ -s 921600 + +Any individual dongle will only support certain baudrates (Generally +multiples of 115200) so you may have to experiment to find the best +supported ones. For the CP2102 dongle 1.3824Mbps wasn't supported and +1.8432Mbps returned corrupted data. + +Please email dave@marples.net with information about dongles you find work +well and at what speed. + +Further information +=================== +SWO is a wide field. Read e.g. the blogs around SWD on +http://shadetail.com/blog/swo-starting-the-steroids/ +An open source program suite for SWO under active development is +https://github.com/mubes/orbuculum \ No newline at end of file diff --git a/scripts/swolisten.c b/scripts/swolisten.c new file mode 100644 index 00000000..1ce141aa --- /dev/null +++ b/scripts/swolisten.c @@ -0,0 +1,544 @@ +/* + * SWO Splitter for Blackmagic Probe and others. + * ============================================= + * + * This file is part of the Black Magic Debug project. + * + * Copyright (C) 2017 Dave Marples + * + * 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 . + */ + + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define VID (0x1d50) +#define PID (0x6018) +#define INTERFACE (5) +#define ENDPOINT (0x85) + +#define TRANSFER_SIZE (64) +#define NUM_FIFOS 32 +#define MAX_FIFOS 128 + +#define CHANNELNAME "chan" + +#define BOOL char +#define FALSE (0) +#define TRUE (!FALSE) + +// Record for options, either defaults or from command line +struct +{ + BOOL verbose; + BOOL dump; + int nChannels; + char *chanPath; + char *port; + int speed; +} options = {.nChannels=NUM_FIFOS, .chanPath="", .speed=115200}; + +// Runtime state +struct +{ + int fifo[MAX_FIFOS]; +} _r; + +// ==================================================================================================== +// ==================================================================================================== +// ==================================================================================================== +// Internals +// ==================================================================================================== +// ==================================================================================================== +// ==================================================================================================== +static BOOL _runFifo(int portNo, int listenHandle, char *fifoName) + +{ + int pid,fifo; + int readDataLen, writeDataLen; + + if (mkfifo(fifoName,0666)<0) + { + return FALSE; + } + + pid=fork(); + + if (pid==0) + { + char rxdata[TRANSFER_SIZE]; + int fifo; + + /* Don't kill this sub-process when any reader or writer evaporates */ + signal(SIGPIPE, SIG_IGN); + + while (1) + { + /* This is the child */ + fifo=open(fifoName,O_WRONLY); + + while (1) + { + readDataLen=read(listenHandle,rxdata,TRANSFER_SIZE); + if (readDataLen<=0) + { + exit(0); + } + + writeDataLen=write(fifo,rxdata,readDataLen); + if (writeDataLen<=0) + { + break; + } + } + close(fifo); + } + } + else if (pid<0) + { + /* The fork failed */ + return FALSE; + } + return TRUE; +} +// ==================================================================================================== +static BOOL _makeFifoTasks(void) + +/* Create each sub-process that will handle a port */ + +{ + char fifoName[PATH_MAX]; + + int f[2]; + + for (int t=0; t0) + { + close(_r.fifo[t]); + sprintf(fifoName,"%s%s%02X",options.chanPath,CHANNELNAME,t); + unlink(fifoName); + remainingProcesses++; + } + } + + while (remainingProcesses--) + { + waitpid(-1,&statloc,0); + } +} +// ==================================================================================================== +// ==================================================================================================== +// ==================================================================================================== +// Handlers for each message type +// ==================================================================================================== +// ==================================================================================================== +// ==================================================================================================== +void _handleSWIT(uint8_t addr, uint8_t length, uint8_t *d) + +{ + if (addr ",*c,_protoNames[p]); +#endif + + switch (p) + { + // ----------------------------------------------------- + case ITM_IDLE: + if (*c==0b01110000) + { + /* This is an overflow packet */ + if (options.verbose) + fprintf(stderr,"Overflow!\n"); + break; + } + // ********** + if (*c==0) + { + /* This is a sync packet - expect to see 4 more 0's followed by 0x80 */ + targetCount=4; + currentCount=0; + p=ITM_SYNCING; + break; + } + // ********** + if (!(*c&0x0F)) + { + currentCount=1; + /* This is a timestamp packet */ + rxPacket[0]=*c; + + if (!(*c&0x80)) + { + /* A one byte output */ + _handleTS(currentCount,rxPacket); + } + else + { + p=ITM_TS; + } + break; + } + // ********** + if ((*c&0x0F) == 0x04) + { + /* This is a reserved packet */ + break; + } + // ********** + if (!(*c&0x04)) + { + /* This is a SWIT packet */ + if ((targetCount=*c&0x03)==3) + targetCount=4; + srcAddr=(*c&0xF8)>>3; + currentCount=0; + p=ITM_SWIT; + break; + } + // ********** + if (options.verbose) + fprintf(stderr,"Illegal packet start in IDLE state\n"); + break; + // ----------------------------------------------------- + case ITM_SWIT: + rxPacket[currentCount]=*c; + currentCount++; + + if (currentCount>=targetCount) + { + p=ITM_IDLE; + _handleSWIT(srcAddr, targetCount, rxPacket); + } + break; + // ----------------------------------------------------- + case ITM_TS: + rxPacket[currentCount++]=*c; + if (!(*c&0x80)) + { + /* We are done */ + _handleTS(currentCount,rxPacket); + } + else + { + if (currentCount>4) + { + /* Something went badly wrong */ + p=ITM_IDLE; + } + break; + } + + // ----------------------------------------------------- + case ITM_SYNCING: + if ((*c==0) && (currentCount

\n",progName); + printf(" b: for channels\n"); + printf(" h: This help\n"); + printf(" d: Dump received data without further processing\n"); + printf(" n: of channels to populate\n"); + printf(" p: to use\n"); + printf(" s: to use\n"); + printf(" v: Verbose mode\n"); +} +// ==================================================================================================== +int _processOptions(int argc, char *argv[]) + +{ + int c; + while ((c = getopt (argc, argv, "vdn:b:hp:s:")) != -1) + switch (c) + { + case 'v': + options.verbose = 1; + break; + case 'd': + options.dump = 1; + break; + case 'p': + options.port=optarg; + break; + case 's': + options.speed=atoi(optarg); + break; + case 'h': + _printHelp(argv[0]); + return FALSE; + case 'n': + options.nChannels=atoi(optarg); + if ((options.nChannels<1) || (options.nChannels>MAX_FIFOS)) + { + fprintf(stderr,"Number of channels out of range (1..%d)\n",MAX_FIFOS); + return FALSE; + } + break; + case 'b': + options.chanPath = optarg; + break; + case '?': + if (optopt == 'b') + fprintf (stderr, "Option '%c' requires an argument.\n", optopt); + else if (!isprint (optopt)) + fprintf (stderr,"Unknown option character `\\x%x'.\n", optopt); + return FALSE; + default: + return FALSE; + } + + if (options.verbose) + { + fprintf(stdout,"Verbose: TRUE\nBasePath: %s\n",options.chanPath); + if (options.port) + { + fprintf(stdout,"Serial Port: %s\nSerial Speed: %d\n",options.port,options.speed); + } + } + return TRUE; +} +// ==================================================================================================== +int usbFeeder(void) + +{ + + unsigned char cbw[TRANSFER_SIZE]; + libusb_device_handle *handle; + libusb_device *dev; + int size; + + while (1) + { + if (libusb_init(NULL) < 0) + { + fprintf(stderr,"Failed to initalise USB interface\n"); + return (-1); + } + + while (!(handle = libusb_open_device_with_vid_pid(NULL, VID, PID))) + { + usleep(500000); + } + + if (!(dev = libusb_get_device(handle))) + continue; + + if (libusb_claim_interface (handle, INTERFACE)<0) + continue; + + while (0==libusb_bulk_transfer(handle, ENDPOINT, cbw, TRANSFER_SIZE, &size, 10)) + { + unsigned char *c=cbw; + if (options.dump) + printf(cbw); + else + while (size--) + _protocolPump(c++); + } + + libusb_close(handle); + } +} +// ==================================================================================================== +int serialFeeder(void) + +{ + int f; + unsigned char cbw[TRANSFER_SIZE]; + ssize_t t; + struct termios settings; + + while (1) + { + while ((f=open(options.port,O_RDONLY))<0) + { + if (options.verbose) + { + fprintf(stderr,"Can't open serial port\n"); + } + usleep(500000); + } + + if (options.verbose) + { + fprintf(stderr,"Port opened\n"); + } + + if (tcgetattr(f, &settings) <0) + { + perror("tcgetattr"); + return(-3); + } + + if (cfsetspeed(&settings, options.speed)<0) + { + perror("Setting input speed"); + return -3; + } + settings.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG); + settings.c_cflag &= ~PARENB; /* no parity */ + settings.c_cflag &= ~CSTOPB; /* 1 stop bit */ + settings.c_cflag &= ~CSIZE; + settings.c_cflag |= CS8 | CLOCAL; /* 8 bits */ + settings.c_oflag &= ~OPOST; /* raw output */ + + if (tcsetattr(f, TCSANOW, &settings)<0) + { + fprintf(stderr,"Unsupported baudrate\n"); + exit(-3); + } + + tcflush(f, TCOFLUSH); + + while ((t=read(f,cbw,TRANSFER_SIZE))>0) + { + unsigned char *c=cbw; + while (t--) + _protocolPump(c++); + } + if (options.verbose) + { + fprintf(stderr,"Read failed\n"); + } + close(f); + } +} +// ==================================================================================================== +int main(int argc, char *argv[]) + +{ + if (!_processOptions(argc,argv)) + { + exit(-1); + } + + atexit(_removeFifoTasks); + /* This ensures the atexit gets called */ + signal(SIGINT, intHandler); + if (!_makeFifoTasks()) + { + fprintf(stderr,"Failed to make channel devices\n"); + exit(-1); + } + + /* Using the exit construct rather than return ensures the atexit gets called */ + if (!options.port) + exit(usbFeeder()); + else + exit(serialFeeder()); + fprintf(stderr,"Returned\n"); + exit(0); +} +// ==================================================================================================== From fa62403ee3dcbf3c554f49f37bbe7fb4fd02b789 Mon Sep 17 00:00:00 2001 From: Mike Walters Date: Mon, 2 Apr 2018 23:45:56 +0100 Subject: [PATCH 18/18] nrf51: Add nRF51802 device id. (#331) --- src/target/nrf51.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/target/nrf51.c b/src/target/nrf51.c index 985005d1..c8c00ea9 100644 --- a/src/target/nrf51.c +++ b/src/target/nrf51.c @@ -130,6 +130,7 @@ bool nrf51_probe(target *t) case 0x007A: /* nRF51422 (rev 3) CEAA C0 */ case 0x008F: /* nRF51822 (rev 3) QFAA H1 See https://devzone.nordicsemi.com/question/97769/can-someone-conform-the-config-id-code-for-the-nrf51822qfaah1/ */ case 0x00D1: /* nRF51822 (rev 3) QFAA H2 */ + case 0x0114: /* nRF51802 (rev ?) QFAA A1 */ t->driver = "Nordic nRF51"; target_add_ram(t, 0x20000000, 0x4000); nrf51_add_flash(t, 0x00000000, 0x40000, NRF51_PAGE_SIZE);