diff --git a/src/Makefile b/src/Makefile index 666c080..8bc2659 100644 --- a/src/Makefile +++ b/src/Makefile @@ -48,6 +48,7 @@ SRC = \ nrf51.c \ platform.c \ sam3x.c \ + sam4l.c \ samd.c \ stm32f1.c \ stm32f4.c \ diff --git a/src/gdb_main.c b/src/gdb_main.c index 6e9e958..d41d605 100644 --- a/src/gdb_main.c +++ b/src/gdb_main.c @@ -96,7 +96,9 @@ int gdb_main_loop(struct target_controller *tc, bool in_syscall) int size; bool single_step = false; - DEBUG("Entring GDB protocol main loop\n"); + /* + DEBUG("Entering GDB protocol main loop\n"); + */ /* GDB protocol main loop */ while(1) { SET_IDLE_STATE(1); diff --git a/src/platforms/common/swdptap.c b/src/platforms/common/swdptap.c index bcc3ccb..c7c48ac 100644 --- a/src/platforms/common/swdptap.c +++ b/src/platforms/common/swdptap.c @@ -36,7 +36,9 @@ static void swdptap_turnaround(uint8_t dir) if(dir == olddir) return; olddir = dir; +#if 0 DEBUG("%s", dir ? "\n-> ":"\n<- "); +#endif if(dir) SWDIO_MODE_FLOAT(); @@ -56,14 +58,18 @@ bool swdptap_bit_in(void) gpio_set(SWCLK_PORT, SWCLK_PIN); gpio_clear(SWCLK_PORT, SWCLK_PIN); +#if 0 DEBUG("%d", ret?1:0); +#endif return ret != 0; } void swdptap_bit_out(bool val) { +#if 0 DEBUG("%d", val); +#endif swdptap_turnaround(0); diff --git a/src/target/cortexm.c b/src/target/cortexm.c index 96c8fde..7e587a4 100644 --- a/src/target/cortexm.c +++ b/src/target/cortexm.c @@ -262,6 +262,7 @@ bool cortexm_probe(ADIv5_AP_t *ap) PROBE(lpc15xx_probe); PROBE(lpc43xx_probe); PROBE(sam3x_probe); + PROBE(sam4l_probe); PROBE(nrf51_probe); PROBE(samd_probe); PROBE(lmi_probe); @@ -442,6 +443,11 @@ static void cortexm_reset(target *t) target_mem_write32(t, CORTEXM_AIRCR, CORTEXM_AIRCR_VECTKEY | CORTEXM_AIRCR_SYSRESETREQ); + /* If target needs to do something extra (see Atmel SAM4L for example) */ + if ((t->target_options & CORTEXM_TOPT_EXTENDED_RESET) != 0) { + t->extended_reset(t); + } + /* Poll for release from reset */ while (target_mem_read32(t, CORTEXM_DHCSR) & CORTEXM_DHCSR_S_RESET_ST); diff --git a/src/target/cortexm.h b/src/target/cortexm.h index bf1d821..7d42f19 100644 --- a/src/target/cortexm.h +++ b/src/target/cortexm.h @@ -155,6 +155,7 @@ #define ARM_THUMB_BREAKPOINT 0xBE00 #define CORTEXM_TOPT_INHIBIT_SRST (1 << 2) +#define CORTEXM_TOPT_EXTENDED_RESET (1 << 3) bool cortexm_probe(ADIv5_AP_t *ap); ADIv5_AP_t *cortexm_ap(target *t); diff --git a/src/target/sam4l.c b/src/target/sam4l.c new file mode 100644 index 0000000..1b1eb81 --- /dev/null +++ b/src/target/sam4l.c @@ -0,0 +1,407 @@ +/* + * vim: set noexpandtab ts=4: + * + * This file is part of the Black Magic Debug project. + * + * Copyright (C) 2016 Chuck McManis + * + * 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 Atmel SAM4 target specific functions for detecting + * the device, providing the XML memory map and Flash memory programming. + * + * Supported devices: SAM4L2, SAM4L4, SAM4L8 + */ + +#include "general.h" +#include "target.h" +#include "target_internal.h" +#include "cortexm.h" + +/* + * Flash Controller defines + */ +#define FLASHCALW_BASE 0x400A0000 + +/* Flash Control Register */ +#define FLASHCALW_FCR (FLASHCALW_BASE + 0x00) +#define FLASHALCW_FCR_WS1OPT (1 << 7) +#define FLASHALCW_FCR_FWS (1 << 6) +#define FLASHALCW_FCR_ECCE (1 << 4) +#define FLASHALCW_FCR_PROGE (1 << 3) +#define FLASHALCW_FCR_LOCKE (1 << 2) +#define FLASHALCW_FCR_FRDY (1 << 0) + +/* Flash Command Register */ +#define FLASHCALW_FCMD (FLASHCALW_BASE + 0x04) +#define FLASHCALW_FCMD_KEY_MASK 0xff +#define FLASHCALW_FCMD_KEY_SHIFT 24 +#define FLASHCALW_FCMD_PAGEN_MASK 0xffff +#define FLASHCALW_FCMD_PAGEN_SHIFT 8 +#define FLASHCALW_FCMD_CMD_MASK 0x3f +#define FLASHCALW_FCMD_CMD_SHIFT 0 + +#define FLASH_CMD_NOP 0 +#define FLASH_CMD_WP 1 /* Write Page */ +#define FLASH_CMD_EP 2 /* Erase Page */ +#define FLASH_CMD_CPB 3 /* Clear Page Buffer */ +#define FLASH_CMD_LP 4 /* Lock page region */ +#define FLASH_CMD_UP 5 /* Unlock page region */ +#define FLASH_CMD_EA 6 /* Erase All */ +#define FLASH_CMD_WGPB 7 /* Write General Purpose Fuse Bit */ +#define FLASH_CMD_EGPB 8 /* Erase General Purpose Fuse Bit */ +#define FLASH_CMD_SSB 9 /* Set Security Fuses */ +#define FLASH_CMD_PGPFB 10 /* Program General Purpose Fuse Byte */ +#define FLASH_CMD_EAGPF 11 /* Erase All GP Fuses */ +#define FLASH_CMD_QPR 12 /* Quick Page Read (erase check) */ +#define FLASH_CMD_WUP 13 /* Write User Page */ +#define FLASH_CMD_EUP 14 /* Erase User Page */ +#define FLASH_CMD_QPRUP 15 /* Quick Page Read User Page */ +#define FLASH_CMD_HSEN 16 /* High Speed Enable */ +#define FLASH_CMD_HSDIS 17 /* High Speed Disable */ + +/* Flash Status Register */ +#define FLASHCALW_FSR (FLASHCALW_BASE + 0x08) +#define FLASHCALW_FSR_LOCK(x) (1 << (16 + (x))) +#define FLASHCALW_FSR_ECCERR (1 << 9) +#define FLASHCALW_FSR_ECCERR2 (1 << 8) +#define FLASHCALW_FSR_HSMODE (1 << 6) +#define FLASHCALW_FSR_QPRR (1 << 5) +#define FLASHCALW_FSR_SECURITY (1 << 4) +#define FLASHCALW_FSR_PROGE (1 << 3) +#define FLASHCALW_FSR_LOCKE (1 << 2) +#define FLASHCALW_FSR_FRDY (1 << 0) + +/* Flash Parameter Register */ +#define FLASHCALW_FPR (FLASHCALW_BASE + 0x0a) +#define FLASHCALW_FPR_PSZ_MASK 0x7 /* page size */ +#define FLASHCALW_FPR_PSZ_SHIFT 8 +#define FLASHCALW_FPR_FSZ_MASK 0xf /* flash size */ +#define FLASHCALW_FPR_FSZ_SHIFT 0 + +/* Flash Version Register */ +#define FLASHCALW_FVR (FLASHCALW_BASE + 0x10) +#define FLASHCALW_FVR_VARIANT_MASK 0xf +#define FLASHCALW_FVR_VARIANT_SHIFT 16 +#define FLASHCALW_FVR_VERSION_MASK 0xfff +#define FLASHCALW_FVR_VERSION_SHIFT 0 + +/* Flash General Purpose Registers (high) */ +#define FLASHCALW_FGPFRHI (FLASHCALW_BASE + 0x14) +/* Flash General Purpose Registers (low) */ +#define FLASHCALW_FGPFRLO (FLASHCALW_BASE + 0x18) + +static int sam4_flash_erase(struct target_flash *f, target_addr addr, size_t len); +int sam4_flash_write(struct target_flash *f, target_addr dest, + const void *src, size_t len); + +const struct command_s sam4l_cmds[] = { + {NULL, NULL, NULL} +}; + +/* why Atmel couldn't make it sequential ... */ +static const size_t __ram_size[16] = { + 48 * 1024, /* 0: 48K */ + 1 * 1024, /* 1: 1K */ + 2 * 1024, /* 2: 2K */ + 6 * 1024, /* 3: 6K */ + 24 * 1024, /* 4: 24K */ + 4 * 1024, /* 5: 4K */ + 80 * 1024, /* 6: 80K */ + 160 * 1024, /* 7: 160K */ + 8 * 1024, /* 8: 8K */ + 16 * 1024, /* 9: 16K */ + 32 * 1024, /* 10: 32K */ + 64 * 1024, /* 11: 64K */ + 128 * 1024, /* 12: 128K */ + 256 * 1024, /* 13: 256K */ + 96 * 1024, /* 14: 96K */ + 512 * 1024 /* 15: 512K */ +}; + +static const size_t __nvp_size[16] = { + 0, /* 0: none */ + 8 * 1024, /* 1: 8K */ + 16 * 1024, /* 2: 16K */ + 32 * 1024, /* 3: 32K */ + 0, /* 4: reserved */ + 64 * 1024, /* 5: 64K */ + 0, /* 6: reserved */ + 128 * 1024, /* 7: 128K */ + 0, /* 8: reserved */ + 256 * 1024, /* 9: 256K */ + 512 * 1024, /* 10: 512K */ + 0, /* 11: reserved */ + 1024 * 1024, /* 12: 1024K (1M) */ + 0, /* 13: reserved */ + 2048 * 1024, /* 14: 2048K (2M) */ + 0 /* 15: reserved */ +}; + + +#define SAM4L_PAGE_SIZE 512 +#define SAM4L_ARCH 0xb0 +#define SAM4L_CHIPID_CIDR 0x400E0740 +#define CHIPID_CIDR_ARCH_MASK 0xff +#define CHIPID_CIDR_ARCH_SHIFT 20 +#define CHIPID_CIDR_SRAMSIZ_MASK 0xf +#define CHIPID_CIDR_SRAMSIZ_SHIFT 16 +#define CHIPID_CIDR_NVPSIZ_MASK 0xf +#define CHIPID_CIDR_NVPSIZ_SHIFT 8 + +/* All variants of 4L have a 512 byte page */ +#define SAM4_PAGE_SIZE 512 + +/* Arbitrary time to wait for FLASH controller to be ready */ +#define FLASH_TIMEOUT 10000 + +/* + * Populate a target_flash struct with the necessary function pointers + * and constants to describe our flash. + */ +static void sam4_add_flash(target *t, uint32_t addr, size_t length) +{ + struct target_flash *f = calloc(1, sizeof(struct target_flash)); + f->start = addr; + f->length = length; + f->blocksize = 512; + f->erase = sam4_flash_erase; + f->write = target_flash_write_buffered; + f->done = target_flash_done_buffered; + f->write_buf = sam4_flash_write; + f->buf_size = 512;; + f->align = 512; + f->erased = 0xff; + /* add it into the target structures flash chain */ + target_add_flash(t, f); +} + +/* Return size of RAM */ +static size_t sam_ram_size(uint32_t idcode) { + return __ram_size[((idcode >> CHIPID_CIDR_SRAMSIZ_SHIFT) & CHIPID_CIDR_SRAMSIZ_MASK)]; +} + +/* Return size of FLASH */ +static size_t sam_nvp_size(uint32_t idcode) { + return __nvp_size[((idcode >> CHIPID_CIDR_NVPSIZ_SHIFT) & CHIPID_CIDR_NVPSIZ_MASK)]; +} + +#define SMAP_BASE 0x400a3000 +#define SMAP_CR (SMAP_BASE + 0x00) +#define SMAP_SR (SMAP_BASE + 0x04) +#define SMAP_SR_DONE (1 << 0) +#define SMAP_SR_HCR (1 << 1) +#define SMAP_SR_BERR (1 << 2) +#define SMAP_SR_FAIL (1 << 3) +#define SMAP_SR_LCK (1 << 4) +#define SMAP_SR_EN (1 << 8) +#define SMAP_SR_PROT (1 << 9) +#define SMAP_SR_DBGP (1 << 10) + + +#define SMAP_SCR (SMAP_BASE + 0x08) +#define SMAP_ADDR (SMAP_BASE + 0x0c) +#define SMAP_LEN (SMAP_BASE + 0x10) +#define SMAP_DATA (SMAP_BASE + 0x14) +#define SMAP_VERS (SMAP_BASE + 0x28) +#define SMAP_CHIPID (SMAP_BASE + 0xf0) +#define SMAP_EXTID (SMAP_BASE + 0xf4) +#define SMAP_IDR (SMAP_BASE + 0xfc) + +static void sam4l_reset(target *t); + +/* + * The probe function, look where the CIDR register should be, see if + * it matches the SAM4L architecture code. + * + * Figure out from the register how much RAM and FLASH this variant has. + */ +bool sam4l_probe(target *t) +{ + uint32_t reg; + size_t ram_size, flash_size; + + DEBUG("\nSAM4L: Probe function called\n"); + t->idcode = target_mem_read32(t, SAM4L_CHIPID_CIDR); + if (((t->idcode >> CHIPID_CIDR_ARCH_SHIFT) & CHIPID_CIDR_ARCH_MASK) == SAM4L_ARCH) { + t->driver = "Atmel SAM4L"; +/* Check to see if our extended reset will fix this */ + t->target_options |= CORTEXM_TOPT_INHIBIT_SRST; + t->target_options |= CORTEXM_TOPT_EXTENDED_RESET; + t->extended_reset = sam4l_reset; + ram_size = sam_ram_size(t->idcode); + target_add_ram(t, 0x20000000, ram_size); + flash_size = sam_nvp_size(t->idcode); + sam4_add_flash(t, 0x0, flash_size); + DEBUG("\nSAM4L: RAM = 0x%x (%dK), FLASH = 0x%x (%dK)\n", + ram_size, ram_size / 1024, flash_size, flash_size / 1024); + + target_mem_write32(t, SMAP_CR, 0x1); /* enable SMAP */ + DEBUG("\nSAM4L: SMAP Enabled\n"); + DEBUG("\nSAM4L: Orig CHIPID = 0x%08lx\n", t->idcode); + reg = target_mem_read32(t, SMAP_CHIPID); + DEBUG("\nSAM4L: SMAP CHIPID = 0x%08lx\n", reg); + reg = target_mem_read32(t, SMAP_SR); + DEBUG("\nSAM4L: SMAP is %s\n", (reg & SMAP_SR_EN) ? "Enabled" : "Disabled"); + DEBUG("\nSAM4L: Debugger is %s\n", (reg & SMAP_SR_DBGP) ? "Present" : "Not Present"); + DEBUG("\nSAM4L: Core is %s\n", (reg & SMAP_SR_HCR) ? "In Reset" : "Running"); + sam4l_reset(t); + reg = target_mem_read32(t, SMAP_SR); + DEBUG("\nSAM4L: Core is %s\n", (reg & SMAP_SR_HCR) ? "In Reset" : "Running"); + + DEBUG("\nSAM4L: SAM4L Selected.\n"); + if (target_check_error(t)) { + DEBUG("SAM4L: target_check_error returned true\n"); + } + return true; + } + return false; +} + +/* + * We've been reset, make sure we take the core out of reset + */ +void +sam4l_reset(target *t) +{ + uint32_t reg; + int i; + + DEBUG("SAM4L: Extended Reset\n"); + reg = target_mem_read32(t, SMAP_SR); + DEBUG("\nSAM4L: SMAP_SR has 0x%08lx\n", reg); + if ((reg & SMAP_SR_HCR) != 0) { + target_mem_write32(t, SMAP_SCR, SMAP_SR_HCR); + i = 0; + do { + reg = target_mem_read32(t, SMAP_SR); + i++; + } while (((reg & SMAP_SR_HCR) != 0) && (i < 100)); + if (i == 100) { + DEBUG("\nSAM4L: Reset failed. SMAP_SR has 0x%08lx\n", reg); + } + } + target_check_error(t); +} + +/* + * sam4_flash_command + * + * Helper function, wait for the flash controller to be ready to receive a + * command. Then send it the command, page number, and the authorization + * key (always 0xA5) in the command register. + * + * Need the target struct to call the mem_read32 and mem_write32 function + * pointers. + */ +static int +sam4_flash_command(target *t, uint32_t page, uint32_t cmd) +{ + uint32_t cmd_reg; + uint32_t status; + int timeout; + DEBUG("\nSAM4L: sam4_flash_command: FSR: 0x%08x, page = %d, command = %d\n", + (unsigned int)(FLASHCALW_FSR), (int) page, (int) cmd); + for (timeout = 0; timeout < FLASH_TIMEOUT; timeout++) { + status = target_mem_read32(t, FLASHCALW_FSR); + if (status & FLASHCALW_FSR_FRDY) { + break; + } + } + if (timeout == FLASH_TIMEOUT) { + DEBUG("\nSAM4L: sam4_flash_command: Not ready! Status = 0x%08x\n", (unsigned int) status); + return -1; /* Failed */ + } + cmd_reg = (cmd & FLASHCALW_FCMD_CMD_MASK) | + ((page & FLASHCALW_FCMD_PAGEN_MASK) << FLASHCALW_FCMD_PAGEN_SHIFT) | + (0xA5 << FLASHCALW_FCMD_KEY_SHIFT); + DEBUG("\nSAM4L: sam4_flash_command: Wrting command word 0x%08x\n", (unsigned int) cmd_reg); + target_mem_write32(t, FLASHCALW_FCMD, cmd_reg); + return 0; +} + +/* + * Write data from 'src' into flash using the algorithim provided by + * Atmel in their data sheet. + */ +int +sam4_flash_write(struct target_flash *f, target_addr addr, const void *src, size_t len) +{ + target *t = f->t; + uint32_t *src_data = (uint32_t *)src; + uint16_t page; + + DEBUG("\nSAM4L: sam4_flash_write: addr = 0x%08lx, len %d\n", addr, len); + /* This will fail with unaligned writes */ + while (len) { + + page = addr / SAM4L_PAGE_SIZE; + /* clear the page buffer */ + if (sam4_flash_command(t, 0, FLASH_CMD_CPB)) { + return -1; + } + /* Fill it with data to write */ + target_mem_write(t, addr, src_data, SAM4L_PAGE_SIZE); +#if 0 + for (ndx = 0; ndx < SAM4L_PAGE_SIZE; ndx += 4) { +/* + target_mem_write32(t, addr, *src_data); + addr += 4; +*/ + /* + * the page buffer overlaps flash, its only 512 bytes long + * and no matter where you write it from it goes to the page + * you point it to. So we don't need the specific address here + * instead we just write 0 - pagelen (512) and that fills our + * buffer correctly. + */ + target_mem_write32(t, addr+ndx, *src_data); + src_data++; + } +#endif + /* write the page */ + if (sam4_flash_command(t, page, FLASH_CMD_WP)) { + return -1; + } + len -= SAM4L_PAGE_SIZE; + addr += SAM4L_PAGE_SIZE; + } + return 0; +} + +/* + * Erase flash across the addresses specified by addr and len + */ +static int +sam4_flash_erase(struct target_flash *f, target_addr addr, size_t len) +{ + target *t = f->t; + uint16_t page; + + DEBUG("SAM4L: flash erase address 0x%08x for %d bytes\n", + (unsigned int) addr, (unsigned int) len); + /* force len to be an integral number of pages */ + /* len = (len + (SAM4_PAGE_SIZE - 1)) & ~SAM4_PAGE_SIZE; */ + + while (len) { + page = addr / SAM4_PAGE_SIZE; + if (sam4_flash_command(t, page, FLASH_CMD_EP)) { + return -1; + } + len -= SAM4_PAGE_SIZE; + addr += SAM4_PAGE_SIZE; + } + return 0; +} diff --git a/src/target/target_internal.h b/src/target/target_internal.h index 75bd743..33f1663 100644 --- a/src/target/target_internal.h +++ b/src/target/target_internal.h @@ -99,6 +99,7 @@ struct target_s { /* Halt/resume functions */ void (*reset)(target *t); + void (*extended_reset)(target *t); void (*halt_request)(target *t); enum target_halt_reason (*halt_poll)(target *t, target_addr *watch); void (*halt_resume)(target *t, bool step); @@ -176,6 +177,7 @@ bool lpc11xx_probe(target *t); bool lpc15xx_probe(target *t); bool lpc43xx_probe(target *t); bool sam3x_probe(target *t); +bool sam4l_probe(target *t); bool nrf51_probe(target *t); bool samd_probe(target *t); bool kinetis_probe(target *t);