Merge commit '455e0a74d2a77f2ae1f0acf53b778e41b890523e' into sam-update

This commit is contained in:
Jason Kotzin 2022-08-01 18:56:50 -07:00
commit da701aff6d
25 changed files with 1026 additions and 93 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

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

@ -57,7 +57,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);
@ -86,7 +86,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)"},
@ -295,10 +295,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

@ -521,7 +521,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

@ -239,9 +239,7 @@ static void efm32_add_flash(target *t, target_addr addr, size_t length,
f->length = length;
f->blocksize = page_size;
f->erase = efm32_flash_erase;
f->write = target_flash_write_buffered;
f->done = target_flash_done_buffered;
f->write_buf = efm32_flash_write;
f->write = efm32_flash_write;
f->buf_size = page_size;
target_add_flash(t, f);
}

View File

@ -94,19 +94,24 @@ 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,
size_t write_len)
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;
f->erase = kl_gen_flash_erase;
f->write = kl_gen_flash_write;
f->done = kl_gen_flash_done;
f->align = write_len;
f->erased = 0xff;
kf->write_len = write_len;
target_add_flash(t, f);
}
@ -256,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) &&
@ -266,7 +273,7 @@ static int kl_gen_flash_write(struct target_flash *f,
/* Determine write command based on the alignment. */
uint8_t write_cmd;
if (f->align == K64_WRITE_LEN) {
if (kf->write_len == K64_WRITE_LEN) {
write_cmd = FTFE_CMD_PROGRAM_PHRASE;
} else {
write_cmd = FTFA_CMD_PROGRAM_LONGWORD;
@ -274,9 +281,9 @@ static int kl_gen_flash_write(struct target_flash *f,
while (len) {
if (kl_gen_command(f->t, write_cmd, dest, src)) {
len -= f->align;
dest += f->align;
src += f->align;
len -= kf->write_len;
dest += kf->write_len;
src += kf->write_len;
} else {
return 1;
}
@ -286,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;
@ -297,7 +305,7 @@ static int kl_gen_flash_done(struct target_flash *f)
/* Load the security byte based on the alignment (determine 8 byte phrases
* vs 4 byte phrases).
*/
if (f->align == 8) {
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);

View File

@ -65,7 +65,6 @@ static void lmi_add_flash(target *t, size_t length)
f->blocksize = 0x400;
f->erase = lmi_flash_erase;
f->write = lmi_flash_write;
f->align = 4;
f->erased = 0xff;
target_add_flash(t, f);
}

View File

@ -40,7 +40,7 @@ void lpc11xx_add_flash(target *t, uint32_t addr, size_t len, size_t erasesize)
struct lpc_flash *lf = lpc_add_flash(t, addr, len);
lf->f.blocksize = erasesize;
lf->f.buf_size = IAP_PGM_CHUNKSIZE;
lf->f.write_buf = lpc_flash_write_magic_vect;
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;
@ -122,4 +122,3 @@ lpc11xx_probe(target *t)
return false;
}

View File

@ -40,7 +40,7 @@ void lpc15xx_add_flash(target *t, uint32_t addr, size_t len, size_t erasesize)
struct lpc_flash *lf = lpc_add_flash(t, addr, len);
lf->f.blocksize = erasesize;
lf->f.buf_size = IAP_PGM_CHUNKSIZE;
lf->f.write_buf = lpc_flash_write_magic_vect;
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;
@ -77,4 +77,3 @@ lpc15xx_probe(target *t)
return false;
}

View File

@ -63,7 +63,7 @@ void lpc17xx_add_flash(target *t, uint32_t addr, size_t len, size_t erasesize, u
lf->f.blocksize = erasesize;
lf->base_sector = base_sector;
lf->f.buf_size = IAP_PGM_CHUNKSIZE;
lf->f.write_buf = lpc_flash_write_magic_vect;
lf->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;

View File

@ -40,9 +40,7 @@ struct lpc_flash *lpc_add_flash(target *t, target_addr addr, size_t length)
f->start = addr;
f->length = length;
f->erase = lpc_flash_erase;
f->write = target_flash_write_buffered;
f->done = target_flash_done_buffered;
f->write_buf = lpc_flash_write;
f->write = lpc_flash_write;
f->erased = 0xff;
target_add_flash(t, f);
return lf;
@ -148,4 +146,3 @@ int lpc_flash_write_magic_vect(struct target_flash *f,
}
return lpc_flash_write(f, dest, src, len);
}

View File

@ -97,7 +97,6 @@ static void nrf51_add_flash(target *t,
f->blocksize = erasesize;
f->erase = nrf51_flash_erase;
f->write = nrf51_flash_write;
f->align = 4;
f->erased = 0xff;
target_add_flash(t, f);
}
@ -130,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);

View File

@ -131,9 +131,7 @@ static void sam3_add_flash(target *t,
f->length = length;
f->blocksize = SAM3_PAGE_SIZE;
f->erase = sam3_flash_erase;
f->write = target_flash_write_buffered;
f->done = target_flash_done_buffered;
f->write_buf = sam3x_flash_write;
f->write = sam3x_flash_write;
f->buf_size = SAM3_PAGE_SIZE;
sf->eefc_base = eefc_base;
sf->write_cmd = EEFC_FCR_FCMD_EWP;
@ -149,9 +147,7 @@ static void sam4_add_flash(target *t,
f->length = length;
f->blocksize = SAM4_PAGE_SIZE * 8;
f->erase = sam4_flash_erase;
f->write = target_flash_write_buffered;
f->done = target_flash_done_buffered;
f->write_buf = sam3x_flash_write;
f->write = sam3x_flash_write;
f->buf_size = SAM4_PAGE_SIZE;
sf->eefc_base = eefc_base;
sf->write_cmd = EEFC_FCR_FCMD_WP;
@ -355,4 +351,3 @@ static bool sam3x_cmd_gpnvm_set(target *t, int argc, char *argv[])
return true;
}

View File

@ -173,9 +173,7 @@ static void sam4l_add_flash(target *t, uint32_t addr, size_t length)
f->length = length;
f->blocksize = SAM4L_PAGE_SIZE;
f->erase = sam4l_flash_erase;
f->write = target_flash_write_buffered;
f->done = target_flash_done_buffered;
f->write_buf = sam4l_flash_write_buf;
f->write = sam4l_flash_write_buf;
f->buf_size = SAM4L_PAGE_SIZE;
f->erased = 0xff;
/* add it into the target structures flash chain */

View File

@ -354,9 +354,7 @@ static void samd_add_flash(target *t, uint32_t addr, size_t length)
f->length = length;
f->blocksize = SAMD_ROW_SIZE;
f->erase = samd_flash_erase;
f->write = target_flash_write_buffered;
f->done = target_flash_done_buffered;
f->write_buf = samd_flash_write;
f->write = samd_flash_write;
f->buf_size = SAMD_PAGE_SIZE;
target_add_flash(t, f);
}
@ -744,4 +742,3 @@ static bool samd_cmd_ssb(target *t)
return true;
}

View File

@ -109,7 +109,6 @@ static void stm32f1_add_flash(target *t,
f->blocksize = erasesize;
f->erase = stm32f1_flash_erase;
f->write = stm32f1_flash_write;
f->align = 2;
f->erased = 0xff;
target_add_flash(t, f);
}

View File

@ -161,7 +161,6 @@ static void stm32f4_add_flash(target *t,
f->blocksize = blocksize;
f->erase = stm32f4_flash_erase;
f->write = stm32f4_flash_write;
f->align = 4;
f->erased = 0xff;
sf->base_sector = base_sector;
sf->psize = 32;

View File

@ -237,9 +237,7 @@ static void stm32l_add_flash(target *t,
f->length = length;
f->blocksize = erasesize;
f->erase = stm32lx_nvm_prog_erase;
f->write = target_flash_write_buffered;
f->done = target_flash_done_buffered;
f->write_buf = stm32lx_nvm_prog_write;
f->write = stm32lx_nvm_prog_write;
f->buf_size = erasesize/2;
target_add_flash(t, f);
}
@ -252,7 +250,6 @@ static void stm32l_add_eeprom(target *t, uint32_t addr, size_t length)
f->blocksize = 4;
f->erase = stm32lx_nvm_data_erase;
f->write = stm32lx_nvm_data_write;
f->align = 1;
target_add_flash(t, f);
}

View File

@ -135,9 +135,7 @@ static void stm32l4_add_flash(target *t,
f->length = length;
f->blocksize = blocksize;
f->erase = stm32l4_flash_erase;
f->write = target_flash_write_buffered;
f->done = target_flash_done_buffered;
f->write_buf = stm32l4_flash_write;
f->write = stm32l4_flash_write;
f->buf_size = 2048;
f->erased = 0xff;
sf->bank1_start = bank1_start;

View File

@ -26,6 +26,10 @@
target *target_list = NULL;
static int target_flash_write_buffered(struct target_flash *f,
target_addr dest, const void *src, size_t len);
static int target_flash_done_buffered(struct target_flash *f);
target *target_new(void)
{
target *t = (void*)calloc(1, sizeof(*t));
@ -138,6 +142,8 @@ void target_add_ram(target *t, target_addr start, uint32_t len)
void target_add_flash(target *t, struct target_flash *f)
{
if (f->buf_size == 0)
f->buf_size = MIN(f->blocksize, 0x400);
f->t = t;
f->next = t->flash;
t->flash = f;
@ -216,15 +222,7 @@ int target_flash_write(target *t,
struct target_flash *f = flash_for_addr(t, dest);
size_t tmptarget = MIN(dest + len, f->start + f->length);
size_t tmplen = tmptarget - dest;
if (f->align > 1) {
uint32_t offset = dest % f->align;
uint8_t data[ALIGN(offset + tmplen, f->align)];
memset(data, f->erased, sizeof(data));
memcpy((uint8_t *)data + offset, src, tmplen);
ret |= f->write(f, dest - offset, data, sizeof(data));
} else {
ret |= f->write(f, dest, src, tmplen);
}
ret |= target_flash_write_buffered(f, dest, src, tmplen);
dest += tmplen;
src += tmplen;
len -= tmplen;
@ -235,6 +233,9 @@ int target_flash_write(target *t,
int target_flash_done(target *t)
{
for (struct target_flash *f = t->flash; f; f = f->next) {
int tmp = target_flash_done_buffered(f);
if (tmp)
return tmp;
if (f->done) {
int tmp = f->done(f);
if (tmp)
@ -260,7 +261,7 @@ int target_flash_write_buffered(struct target_flash *f,
if (base != f->buf_addr) {
if (f->buf_addr != (uint32_t)-1) {
/* Write sector to flash if valid */
ret |= f->write_buf(f, f->buf_addr,
ret |= f->write(f, f->buf_addr,
f->buf, f->buf_size);
}
/* Setup buffer for a new sector */
@ -282,7 +283,7 @@ int target_flash_done_buffered(struct target_flash *f)
int ret = 0;
if ((f->buf != NULL) &&(f->buf_addr != (uint32_t)-1)) {
/* Write sector to flash if valid */
ret = f->write_buf(f, f->buf_addr, f->buf, f->buf_size);
ret = f->write(f, f->buf_addr, f->buf, f->buf_size);
f->buf_addr = -1;
free(f->buf);
f->buf = NULL;
@ -564,4 +565,3 @@ int tc_system(target *t, target_addr cmd, size_t cmdlen)
}
return t->tc->system(t->tc, cmd, cmdlen);
}

View File

@ -43,13 +43,9 @@ struct target_flash {
flash_write_func write;
flash_done_func done;
target *t;
struct target_flash *next;
int align;
uint8_t erased;
/* For buffered flash */
size_t buf_size;
flash_write_func write_buf;
struct target_flash *next;
target_addr buf_addr;
void *buf;
};
@ -131,9 +127,6 @@ struct target_s {
void target_add_commands(target *t, const struct command_s *cmds, const char *name);
void target_add_ram(target *t, target_addr start, uint32_t len);
void target_add_flash(target *t, struct target_flash *f);
int target_flash_write_buffered(struct target_flash *f,
target_addr dest, const void *src, size_t len);
int target_flash_done_buffered(struct target_flash *f);
/* Convenience function for MMIO access */
uint32_t target_mem_read32(target *t, uint32_t addr);
@ -185,4 +178,3 @@ bool kinetis_probe(target *t);
bool efm32_probe(target *t);
#endif