cancel
Showing results for 
Search instead for 
Did you mean: 

implementing swd with spi on stm32f3

Geoffrey1
Associate III
Posted on November 29, 2016 at 03:44

I have a working bit-banged implementation of swd, now I want to use the spi device.  The protocol is a bit odd in requiring some short input sequences.  Essentially, I'm using the spi device for everything that's a nice multiple of 8 bits (disabling the MOSI pin for input), but I need to bit-bang a couple of input bits.   I've successfully seized the clock, but I can't seem to read the data from the MOSI or MISO pins reconfigured as input.  The key bit of code follows.  The clock banging works fine, but no joy on the input.  Using a logic analyzer I can see that the correct bits are coming from the SWD slave, but I'm reading all high on the input:

.ExternalClass55FF5732FB374BD098B03B2D208DE3CF p.p1 {margin:0.0px 0.0px 0.0px 0.0px;font:13.0px Menlo;background-color:#fff8d4;} .ExternalClass55FF5732FB374BD098B03B2D208DE3CF p.p2 {margin:0.0px 0.0px 0.0px 0.0px;font:13.0px Menlo;background-color:#fff8d4;min-height:15.0px;} .ExternalClass55FF5732FB374BD098B03B2D208DE3CF span.s1 {;} .ExternalClass55FF5732FB374BD098B03B2D208DE3CF p.p1 {margin:0.0px 0.0px 0.0px 0.0px;font:13.0px Menlo;color:#cb3bff;background-color:#fff8d4;} .ExternalClass55FF5732FB374BD098B03B2D208DE3CF p.p2 {margin:0.0px 0.0px 0.0px 0.0px;font:13.0px Menlo;color:#2f9f23;background-color:#fff8d4;} .ExternalClass55FF5732FB374BD098B03B2D208DE3CF p.p3 {margin:0.0px 0.0px 0.0px 0.0px;font:13.0px Menlo;background-color:#fff8d4;} .ExternalClass55FF5732FB374BD098B03B2D208DE3CF p.p4 {margin:0.0px 0.0px 0.0px 0.0px;font:13.0px Menlo;background-color:#fff8d4;min-height:15.0px;} .ExternalClass55FF5732FB374BD098B03B2D208DE3CF p.p5 {margin:0.0px 0.0px 0.0px 0.0px;font:13.0px Menlo;color:#5934ff;background-color:#fff8d4;} .ExternalClass55FF5732FB374BD098B03B2D208DE3CF span.s1 {;} .ExternalClass55FF5732FB374BD098B03B2D208DE3CF span.s2 {color:#000000;} .ExternalClass55FF5732FB374BD098B03B2D208DE3CF span.s3 {color:#2f9f23;} .ExternalClass55FF5732FB374BD098B03B2D208DE3CF span.s4 {color:#5934ff;} .ExternalClass55FF5732FB374BD098B03B2D208DE3CF span.s5 {color:#c2981e;} .ExternalClass55FF5732FB374BD098B03B2D208DE3CF span.s6 {color:#cb3bff;}

tatic

inline

uint32_t

SWDIO_IN

(){

 

uint8_t

b

;

  LL_GPIO_ResetOutputPin(tgt_SWDCLK_GPIO_Port, tgt_SWDCLK_Pin);

  b = LL_GPIO_IsInputPinSet(tgt_SWDIO_GPIO_Port, tgt_SWDIO_Pin);

  delay();

  LL_GPIO_SetOutputPin(tgt_SWDCLK_GPIO_Port, tgt_SWDCLK_Pin);

 

return

b;

}

static

void

_SetSWDIOasOutput

(){

  LL_GPIO_SetPinMode(tgt_SWDIO_GPIO_Port, tgt_SWDIO_Pin, LL_GPIO_MODE_ALTERNATE);

  LL_GPIO_SetPinMode(tgt_SWDCLK_GPIO_Port, tgt_SWDCLK_Pin, LL_GPIO_MODE_ALTERNATE);

}

static

void

_SetSWDIOasInput

(){

  LL_GPIO_SetPinMode(tgt_SWDIO_GPIO_Port, tgt_SWDIO_Pin, LL_GPIO_MODE_INPUT);

  LL_GPIO_SetPinMode(tgt_SWDCLK_GPIO_Port, tgt_SWDCLK_Pin, LL_GPIO_MODE_OUTPUT);

}

4 REPLIES 4
Kraal
Senior III
Posted on December 06, 2016 at 09:34

Hello Geoffrey,

I'm trying to implement the SWD protocol, but have not found yet any source anywhere. Is it possible for you to share your code ?

Regarding your issue, what is the frequency of your system ? Maybe it is complicated to add a larger delay between _SetSWDIOasInput() and SWDIO_IN(), but what if you could lower the SWD frequency just to try ?

Another thought, maybe you have to also revert AF back to AF0 (i.e. not only change mode ALTERNATE to INPUT, but also invoke LL_GPIO_SetAFPin_0_7(GPIO_TypeDef* GPIOx, uint32_t Pin, uint32_t Alternate)).

Have a nice day,

Carl

Kraal
Senior III
Posted on December 06, 2016 at 11:07

Hi again,

After a bit of searching around, I found (here : 

https://fedcsis.org/proceedings/2012/pliks/279.pdf

 and there : 

https://github.com/cederom/LibSWD/wiki/Project-History#SWDP

 ) that you can change the Turnaround tristate period using the Wire Control Register. So maybe you could use that to give your hardware a little time to actually change its buffer direction without reducing the communication rate.

Have a nice day,

Carl

Seb
ST Employee
Posted on December 11, 2016 at 13:05

SWD means bi-directional SPI-like communication. To maximize the Setup Time, the phase of the SPI clock in write or read should be adjusted. On STM32L4, the SPI bit length can be modified from 4 to 16 bits. It might reduce the need to mix HW SPI (8 or 16 bit mode) and SW IO bit-banging. (not tested, just a thought here)

Good luck !

Geoffrey1
Associate III
Posted on December 11, 2016 at 14:27

Thanks to everyone for their input. In the end I resolved this and go things working pretty well. I don't recall exactly what resolved the problem above except some configuration error.

For those that are interested in SWD, here's the bit of code that handles the low-level transactions. The keys are handling the pin directions and the things that don't 'fit' is a normal SPI transaction. I found it easier to bit-bang those few places I needed odd-lengths that to mess with the SPI configuration. I also took advantage of the fact that at the end of a transaction it's ok (even desirable) for the master to transmit a bunch of 0's. If you've studied SWD, all the constant names should make sense.

In doing this I looked at a bunch of other implementations (libswd, daplink) but they tried to be too generic for my taste and I found them difficult to follow. I also didn't like the lack of control over clock rate that you get with a pure bit-banged solution. With SPI it's easy to set the rate where you want it.

There are a lot of other protocol things above this level, but really this is the hardest part to get working. The code below is about 1/3 of the total which does all the attach/detach read/write memory and read/write register stuff you need for a debugger.

I did really utilize the Saleae SWD protocol analyzer -- a shout out to them for a terrific piece of software/equipment. I never bother firing up my HP logic analyzer for these sorts of things.

static void _SetSWDIOasOutput(){
 LL_GPIO_SetPinMode(tgt_SWDIO_GPIO_Port, tgt_SWDIO_Pin, LL_GPIO_MODE_ALTERNATE);
}
static void _SetSWDIOasInput(){
 LL_GPIO_SetPinMode(tgt_SWDIO_GPIO_Port, tgt_SWDIO_Pin, LL_GPIO_MODE_INPUT);
}
static inline uint32_t SWDIO_IN(){
 uint8_t b;
 LL_GPIO_ResetOutputPin(tgt_SWDCLK_GPIO_Port, tgt_SWDCLK_Pin);
 b = LL_GPIO_IsInputPinSet(tgt_SWDIO_GPIO_Port, tgt_SWDIO_Pin);
 delay(); 
 LL_GPIO_SetOutputPin(tgt_SWDCLK_GPIO_Port, tgt_SWDCLK_Pin);
 return b;
}
static inline uint32_t SW_ShiftIn(uint8_t bits){
 int i;
 uint32_t in = 0;
 LL_GPIO_SetPinMode(tgt_SWDCLK_GPIO_Port, tgt_SWDCLK_Pin, LL_GPIO_MODE_OUTPUT);
 for (i = 0; i < bits; i++) {
 in = (in >> 1) | ((SWDIO_IN()&1) << (bits - 1));
 }
 LL_GPIO_SetPinMode(tgt_SWDCLK_GPIO_Port, tgt_SWDCLK_Pin, LL_GPIO_MODE_ALTERNATE);
 return in;
}

static inline uint32_t SW_ShiftInBytes(uint8_t bytes) {
 int i;
 uint32_t tmp;
 for (i = 0; i < bytes; i++) {
 LL_SPI_TransmitData8(SWD_SPI_DEVICE, 0xff);
 }
 for (i = 0; i < bytes; i++) {
 while (!LL_SPI_IsActiveFlag_RXNE(SWD_SPI_DEVICE));
 ((uint8_t *) &tmp)[i] = LL_SPI_ReceiveData8(SWD_SPI_DEVICE);
 }
 return tmp;
}
static inline void SW_ShiftOutBytes(uint32_t data, uint8_t bytes)
{
 int i;
 for (i = 0; i < bytes; i++) {
 LL_SPI_TransmitData8(SWD_SPI_DEVICE, data);
 data = data >> 8;
 }
 for (i = 0; i < bytes; i++) {
 while (!LL_SPI_IsActiveFlag_RXNE(SWD_SPI_DEVICE));
 LL_SPI_ReceiveData8(SWD_SPI_DEVICE);
 }
}
static inline uint32_t Parity(uint32_t x) {
 uint32_t y;
 y = x ^ (x >> 1);
 y = y ^ (y >> 2);
 y = y ^ (y >> 4);
 y = y ^ (y >> 8);
 y = y ^ (y >>16);
 return y & 1;
}
static uint32_t SWD_TransactionBB(uint32_t req, uint32_t *data) {
 uint32_t ack;
 uint32_t pbit;
 SW_ShiftOutBytes(req,1); // Send header
 _SetSWDIOasInput(); // Set pin direction
 ack = (SW_ShiftIn(4) >> 1) & 7; // ACK, toss the turnaround bit
 switch (ack) {
 case SW_ACK_OK : // good to go
 if (req & SW_REQ_RnW) { // read
 *data = SW_ShiftInBytes(4); // get data
 pbit = (SW_ShiftIn(2) >> 1); // get parity bit, toss turnaround
 if (pbit ^ Parity(*data)) { // parity check
 ack = SW_ACK_PARITY_ERR;
 }
 _SetSWDIOasOutput(); // restore direction
 } else { // write
 SW_ShiftIn(1); // turnaround
 _SetSWDIOasOutput(); // restore direction
 SW_ShiftOutBytes(*data,4); // data
 SW_ShiftOutBytes(Parity(*data), 1);// parity 
 }
 break;
 case SW_ACK_WAIT :
 case SW_ACK_FAULT :
 SW_ShiftIn(1); // turnaround
 _SetSWDIOasOutput(); // restore direction
 break;
 default : // no ack, back off in case of data phase
 SW_ShiftInBytes(4); // data
 SW_ShiftIn(2); // parity + turn
 _SetSWDIOasOutput(); // restore direction
 }
 return ack;
}
�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?