Merge branch 'master' into always_buffer_flash
This commit is contained in:
commit
9d7925792f
|
@ -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
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
// ====================================================================================================
|
|
@ -40,6 +40,7 @@ SRC = \
|
|||
lmi.c \
|
||||
lpc_common.c \
|
||||
lpc11xx.c \
|
||||
lpc17xx.c \
|
||||
lpc15xx.c \
|
||||
lpc43xx.c \
|
||||
kinetis.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;
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);}
|
||||
|
|
|
@ -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);
|
||||
}
|
|
@ -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 */
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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."},
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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, ¶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];
|
||||
}
|
|
@ -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,
|
||||
};
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
Loading…
Reference in New Issue