Merge branch 'master' into always_buffer_flash

This commit is contained in:
Gareth McMullin 2018-04-23 10:39:19 +12:00
commit 9d7925792f
20 changed files with 1336 additions and 38 deletions

254
UsingSWO Normal file
View File

@ -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
<x>' 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
<<OUTPUT FROM ITM Channel 0>>
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

View File

@ -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

544
scripts/swolisten.c Normal file
View File

@ -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 <dave@marples.net>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <ctype.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <sys/ioctl.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <stdio.h>
#include <string.h>
#include <libusb.h>
#include <stdint.h>
#include <limits.h>
#include <termios.h>
#include <signal.h>
#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; t<options.nChannels; t++)
{
if (pipe(f)<0)
return FALSE;
fcntl(f[1],F_SETFL,O_NONBLOCK);
_r.fifo[t]=f[1];
sprintf(fifoName,"%s%s%02X",options.chanPath,CHANNELNAME,t);
if (!_runFifo(t,f[0],fifoName))
{
return FALSE;
}
}
return TRUE;
}
// ====================================================================================================
static void _removeFifoTasks(void)
/* Destroy the per-port sub-processes */
{
int statloc;
int remainingProcesses=0;
char fifoName[PATH_MAX];
for (int t=0; t<options.nChannels; t++)
{
if (_r.fifo[t]>0)
{
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<options.nChannels)
write(_r.fifo[addr],d,length);
// if (addr==0)
// fprintf(stdout,"%c",*d);
}
// ====================================================================================================
void _handleTS(uint8_t length, uint8_t *d)
{
}
// ====================================================================================================
// ====================================================================================================
// ====================================================================================================
// Protocol pump for decoding messages
// ====================================================================================================
// ====================================================================================================
// ====================================================================================================
enum _protoState {ITM_IDLE, ITM_SYNCING, ITM_TS, ITM_SWIT};
#ifdef PRINT_TRANSITIONS
static char *_protoNames[]={"IDLE", "SYNCING","TS","SWIT"};
#endif
void _protocolPump(uint8_t *c)
{
static enum _protoState p;
static int targetCount, currentCount, srcAddr;
static uint8_t rxPacket[5];
#ifdef PRINT_TRANSITIONS
printf("%02x %s --> ",*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<targetCount))
{
currentCount++;
}
else
{
if (*c==0x80)
{
p=ITM_IDLE;
}
else
{
/* This should really be an UNKNOWN state */
p=ITM_IDLE;
}
}
break;
// -----------------------------------------------------
}
#ifdef PRINT_TRANSITIONS
printf("%s\n",_protoNames[p]);
#endif
}
// ====================================================================================================
void intHandler(int dummy)
{
exit(0);
}
// ====================================================================================================
void _printHelp(char *progName)
{
printf("Useage: %s <dhnv> <b basedir> <p port> <s speed>\n",progName);
printf(" b: <basedir> for channels\n");
printf(" h: This help\n");
printf(" d: Dump received data without further processing\n");
printf(" n: <Number> of channels to populate\n");
printf(" p: <serialPort> to use\n");
printf(" s: <serialSpeed> 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);
}
// ====================================================================================================

View File

@ -40,6 +40,7 @@ SRC = \
lmi.c \
lpc_common.c \
lpc11xx.c \
lpc17xx.c \
lpc15xx.c \
lpc43xx.c \
kinetis.c \

View File

@ -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;
}

View File

@ -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);

View File

@ -22,7 +22,7 @@
#include <libopencm3/usb/usbd.h>
void traceswo_init(void);
void traceswo_init(uint32_t baudrate);
void trace_buf_drain(usbd_device *dev, uint8_t ep);
#endif

View File

@ -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

View File

@ -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);}

View File

@ -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 <dave@marples.net>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/* 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 <libopencmsis/core_cm3.h>
#include <libopencm3/cm3/nvic.h>
#include <libopencm3/stm32/timer.h>
#include <libopencm3/stm32/f1/rcc.h>
#include <libopencm3/stm32/usart.h>
#include <libopencm3/stm32/dma.h>
/* 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);
}

View File

@ -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 */

View File

@ -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 */
@ -122,7 +125,10 @@ static const char tdesc_cortex_m[] =
" <reg name=\"xpsr\" bitsize=\"32\"/>"
" <reg name=\"msp\" bitsize=\"32\" save-restore=\"no\" type=\"data_ptr\"/>"
" <reg name=\"psp\" bitsize=\"32\" save-restore=\"no\" type=\"data_ptr\"/>"
" <reg name=\"special\" bitsize=\"32\" save-restore=\"no\"/>"
" <reg name=\"primask\" bitsize=\"8\" save-restore=\"no\"/>"
" <reg name=\"basepri\" bitsize=\"8\" save-restore=\"no\"/>"
" <reg name=\"faultmask\" bitsize=\"8\" save-restore=\"no\"/>"
" <reg name=\"control\" bitsize=\"8\" save-restore=\"no\"/>"
" </feature>"
"</target>";
@ -151,7 +157,10 @@ static const char tdesc_cortex_mf[] =
" <reg name=\"xpsr\" bitsize=\"32\"/>"
" <reg name=\"msp\" bitsize=\"32\" save-restore=\"no\" type=\"data_ptr\"/>"
" <reg name=\"psp\" bitsize=\"32\" save-restore=\"no\" type=\"data_ptr\"/>"
" <reg name=\"special\" bitsize=\"32\" save-restore=\"no\"/>"
" <reg name=\"primask\" bitsize=\"8\" save-restore=\"no\"/>"
" <reg name=\"basepri\" bitsize=\"8\" save-restore=\"no\"/>"
" <reg name=\"faultmask\" bitsize=\"8\" save-restore=\"no\"/>"
" <reg name=\"control\" bitsize=\"8\" save-restore=\"no\"/>"
" </feature>"
" <feature name=\"org.gnu.gdb.arm.vfp\">"
" <reg name=\"fpscr\" bitsize=\"32\"/>"
@ -179,13 +188,40 @@ ADIv5_AP_t *cortexm_ap(target *t)
return ((struct cortexm_priv *)t->priv)->ap;
}
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 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 (addr > ram)
ram = addr;
if (mem_end < ram_end)
ram_end = mem_end;
/* intersection is [ram, ram_end) */
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)
{
cortexm_cache_clean(t, dest, len, true);
adiv5_mem_write(cortexm_ap(t), dest, src, len);
}
@ -251,6 +287,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)
@ -260,6 +305,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);
@ -551,6 +597,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);
}

View File

@ -37,6 +37,17 @@
#define CORTEXM_DCRDR (CORTEXM_SCS_BASE + 0xDF8)
#define CORTEXM_DEMCR (CORTEXM_SCS_BASE + 0xDFC)
/* 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)
/* ARM Literature uses FP_*, we use CORTEXM_FPB_* consistently */

View File

@ -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."},

View File

@ -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"
@ -52,6 +58,8 @@
#define FTFA_CMD_PROGRAM_CHECK 0x02
#define FTFA_CMD_READ_RESOURCE 0x03
#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;
@ -84,10 +94,16 @@ static int kl_gen_flash_write(struct target_flash *f,
target_addr dest, const void *src, size_t len);
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)
struct kinetis_flash {
struct target_flash f;
uint8_t write_len;
};
static void kl_gen_add_flash(target *t, uint32_t addr, size_t length,
size_t erasesize, size_t write_len)
{
struct target_flash *f = calloc(1, sizeof(*f));
struct kinetis_flash *kf = calloc(1, sizeof(*kf));
struct target_flash *f = &kf->f;
f->start = addr;
f->length = length;
f->blocksize = erasesize;
@ -95,6 +111,7 @@ static void kl_gen_add_flash(target *t,
f->write = kl_gen_flash_write;
f->done = kl_gen_flash_done;
f->erased = 0xff;
kf->write_len = write_len;
target_add_flash(t, f);
}
@ -106,13 +123,31 @@ 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";
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);
kl_gen_add_flash(t, 0x00000000, 0x40000, 0x400, KL_WRITE_LEN);
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, KL_WRITE_LEN);
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, KL_WRITE_LEN);
break;
default:
return false;
}
break;
case 0x021: /* KL02 family */
switch((sdid>>16) & 0x0f){
@ -120,19 +155,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;
@ -142,14 +177,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;
@ -199,8 +245,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;
}
@ -214,6 +261,8 @@ static int kl_gen_flash_erase(struct target_flash *f, target_addr addr, size_t l
static int kl_gen_flash_write(struct target_flash *f,
target_addr dest, const void *src, size_t len)
{
struct kinetis_flash *kf = (struct kinetis_flash *)f;
/* Ensure we don't write something horrible over the security byte */
if (!unsafe_enabled &&
(dest <= FLASH_SECURITY_BYTE_ADDRESS) &&
@ -222,11 +271,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 (kf->write_len == 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 -= kf->write_len;
dest += kf->write_len;
src += kf->write_len;
} else {
return 1;
}
@ -236,6 +293,7 @@ static int kl_gen_flash_write(struct target_flash *f,
static int kl_gen_flash_done(struct target_flash *f)
{
struct kinetis_flash *kf = (struct kinetis_flash *)f;
if (unsafe_enabled)
return 0;
@ -244,10 +302,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 (kf->write_len == 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;
}

201
src/target/lpc17xx.c Normal file
View File

@ -0,0 +1,201 @@
/*
* This file is part of the Black Magic Debug project.
*
* Copyright (C) 2012 Gareth McMullin <gareth@blacksphere.co.nz>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#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 = 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, &param, 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, &param, 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, &param, 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, &param, 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];
}

View File

@ -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,
};

View File

@ -129,6 +129,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);
@ -164,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);

View File

@ -135,11 +135,13 @@ 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);
/* fall through */
case 0x432: /* STM32F37x */
case 0x439: /* STM32F302C8 */
t->driver = "STM32F3";
target_add_ram(t, 0x20000000, 0x10000);
stm32f1_add_flash(t, 0x8000000, 0x80000, 0x800);

View File

@ -168,6 +168,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);