2016-11-28 06:44 PM
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);
}
2016-12-06 12:34 AM
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
2016-12-06 02:07 AM
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
2016-12-11 04:05 AM
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 !
2016-12-11 05:27 AM
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;
}
�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?