2021-03-10 14:12:31 +01:00
/** library for Serial Wire Debug (SWD) communication
* @ file
* @ author King Kévin < kingkevin @ cuvoodoo . info >
* @ copyright SPDX - License - Identifier : GPL - 3.0 - or - later
* @ date 2018 - 2021
* @ note peripherals used : timer @ ref swd_timer , GPIO @ ref swd_gpio
* @ implements " ARM Debug Interface Architecture Specification ADIv6.0 " ( ARM IHI 0074 A )
* @ note this library implements DP architecture version 3 ( DPv3 ) , but only DPv1 feature could be tested .
* @ implements The physical layer ( electrical characteristic and timing ) is described in " ARM DSTREAM System and Interface Design Reference Guide " ( ARM DUI0499K )
*/
/* standard libraries */
# include <stdint.h> // standard integer types
# include <stdbool.h> // boolean type
# include <stddef.h> // NULL definition
/* STM32 (including CM3) libraries */
# include <libopencmsis/core_cm3.h> // Cortex M3 utilities
# include <libopencm3/cm3/nvic.h> // interrupt handler
# include <libopencm3/stm32/rcc.h> // real-time control clock library
# include <libopencm3/stm32/gpio.h> // general purpose input output library
# include <libopencm3/stm32/timer.h> // timer library
/* own libraries */
# include "global.h" // helper macros
# include "swd.h" // own definitions
/** @defgroup swd_gpio GPIO used for SWDIO and SWCLK
* @ {
*/
2021-03-10 14:24:52 +01:00
# define SWD_SWCLK_PIN PB10 /**< default GPIO pin for clock signal */
# define SWD_SWDIO_PIN PB2 /**< default GPIO pin for data input/output signal */
static uint32_t swd_swclk_rcc = GPIO_RCC ( SWD_SWCLK_PIN ) ;
static uint32_t swd_swclk_port = GPIO_PORT ( SWD_SWCLK_PIN ) ;
static uint32_t swd_swclk_pin = GPIO_PIN ( SWD_SWCLK_PIN ) ;
static uint32_t swd_swdio_rcc = GPIO_RCC ( SWD_SWDIO_PIN ) ;
static uint32_t swd_swdio_port = GPIO_PORT ( SWD_SWDIO_PIN ) ;
static uint32_t swd_swdio_pin = GPIO_PIN ( SWD_SWDIO_PIN ) ;
2021-03-10 14:12:31 +01:00
/** @} */
/** @defgroup swd_timer timer used to generate a periodic clock
* @ note the clock does not actually need to be periodic ( it makes it just more readable )
* @ {
*/
# define SWD_TIMER 2 /**< timer ID */
/** @} */
static volatile uint64_t swd_bits_out = 0 ; /** a buffer for the data bits to output/write (LSb first) */
static volatile uint64_t swd_bits_in = 0 ; /** a buffer for the data bits to input/read (LSB first) */
static volatile uint8_t swd_bits_count = 0 ; /** number of bits to read/write */
static volatile uint8_t swd_bits_i = 0 ; /** transaction bit index */
void swd_setup ( uint32_t freq )
{
2021-03-10 18:26:13 +01:00
// setup GPIO
swd_set_pins ( swd_swclk_port , swd_swclk_pin , swd_swdio_port , swd_swdio_pin ) ;
2021-03-10 14:12:31 +01:00
// setup timer to generate periodic clock
rcc_periph_clock_enable ( RCC_TIM ( SWD_TIMER ) ) ; // enable clock for timer peripheral
rcc_periph_reset_pulse ( RST_TIM ( SWD_TIMER ) ) ; // reset timer state
timer_disable_counter ( TIM ( SWD_TIMER ) ) ; // disable timer to configure it
timer_set_mode ( TIM ( SWD_TIMER ) , TIM_CR1_CKD_CK_INT , TIM_CR1_CMS_EDGE , TIM_CR1_DIR_UP ) ; // set timer mode, use undivided timer clock, edge alignment (simple count), and count up
timer_set_prescaler ( TIM ( SWD_TIMER ) , 1 - 1 ) ; // don't use prescaler, allowing period down to 72 MHz / 2^16 = 1099 Hz
uint32_t period = rcc_ahb_frequency / freq / 2 ; // get period based on frequency (/2 because on cycle has 2 edges)
if ( period > 0xffff ) { // maximum frequency reached
period = 0xffff ;
} else if ( period > 0 ) { // not minimum frequency reached
period - = 1 ; // correct timer overflow offset
}
timer_set_period ( TIM ( SWD_TIMER ) , period ) ; // set period the generate clock frequency (x2 for the two edges)
timer_clear_flag ( TIM ( SWD_TIMER ) , TIM_SR_UIF ) ; // clear update (overflow) flag
timer_update_on_overflow ( TIM ( SWD_TIMER ) ) ; // only use counter overflow as UEV source (use overflow as start time or timeout)
timer_enable_irq ( TIM ( SWD_TIMER ) , TIM_DIER_UIE ) ; // enable update interrupt for overflow to generate the clock signal
nvic_enable_irq ( NVIC_TIM_IRQ ( SWD_TIMER ) ) ; // catch interrupt in service routine
// reset variables
swd_bits_count = 0 ;
}
2021-03-10 18:26:13 +01:00
/** release used pins */
2021-03-12 13:10:59 +01:00
void swd_release_pins ( void )
2021-03-10 18:26:13 +01:00
{
// release GPIO
gpio_mode_setup ( swd_swclk_port , GPIO_MODE_INPUT , GPIO_PUPD_NONE , swd_swclk_pin ) ; // put clock signal pin back to input floating
gpio_mode_setup ( swd_swdio_port , GPIO_MODE_INPUT , GPIO_PUPD_NONE , swd_swdio_pin ) ; // put data signal pin back to input floating
}
2021-03-10 14:12:31 +01:00
void swd_release ( void )
{
// release timer
timer_disable_counter ( TIM ( SWD_TIMER ) ) ; // disable timer
rcc_periph_reset_pulse ( RST_TIM ( SWD_TIMER ) ) ; // reset timer state
rcc_periph_clock_disable ( RCC_TIM ( SWD_TIMER ) ) ; // disable clock for timer peripheral
2021-03-10 18:26:13 +01:00
swd_release_pins ( ) ; // release GPIO
}
bool swd_set_pins ( uint32_t swclk_port , uint32_t swclk_pin , uint32_t swdio_port , uint32_t swdio_pin )
{
2021-03-12 13:10:59 +01:00
// check if pin exists and is unique
if ( __builtin_popcount ( swclk_pin ) ! = 1 | | __builtin_popcount ( swdio_pin ) ! = 1 ) {
2021-03-10 18:26:13 +01:00
return false ;
}
2021-03-12 13:10:59 +01:00
// check if pin really exists
if ( swclk_pin > ( 1 < < 15 ) | | swdio_pin > ( 1 < < 15 ) ) {
return false ;
}
// ensure pin is different
if ( swclk_port = = swdio_port & & swclk_pin = = swdio_pin ) {
return false ;
}
2021-03-10 18:26:13 +01:00
uint32_t swclk_rcc = 0 ;
switch ( swclk_port ) {
case GPIOA :
swclk_rcc = RCC_GPIOA ;
break ;
case GPIOB :
swclk_rcc = RCC_GPIOB ;
break ;
case GPIOC :
swclk_rcc = RCC_GPIOC ;
break ;
case GPIOD :
swclk_rcc = RCC_GPIOD ;
break ;
case GPIOE :
swclk_rcc = RCC_GPIOE ;
break ;
case GPIOF :
swclk_rcc = RCC_GPIOF ;
break ;
case GPIOG :
swclk_rcc = RCC_GPIOG ;
break ;
default : // unknown port
return false ;
}
uint32_t swdio_rcc = 0 ;
switch ( swdio_port ) {
case GPIOA :
swdio_rcc = RCC_GPIOA ;
break ;
case GPIOB :
swdio_rcc = RCC_GPIOB ;
break ;
case GPIOC :
swdio_rcc = RCC_GPIOC ;
break ;
case GPIOD :
swdio_rcc = RCC_GPIOD ;
break ;
case GPIOE :
swdio_rcc = RCC_GPIOE ;
break ;
case GPIOF :
swdio_rcc = RCC_GPIOF ;
break ;
case GPIOG :
swdio_rcc = RCC_GPIOG ;
break ;
default : // unknown port
return false ;
}
swd_release_pins ( ) ; // release already set pins (not a problem even if not already used
// remember pins
swd_swclk_rcc = swclk_rcc ;
swd_swclk_port = swclk_port ;
swd_swclk_pin = swclk_pin ;
swd_swdio_rcc = swdio_rcc ;
swd_swdio_port = swdio_port ;
swd_swdio_pin = swdio_pin ;
// setup GPIO for clock and data signals
rcc_periph_clock_enable ( swd_swclk_rcc ) ; // enable clock for GPIO peripheral for clock signal
gpio_set ( swd_swclk_port , swd_swclk_pin ) ; // inactive clock is high
gpio_mode_setup ( swd_swclk_port , GPIO_MODE_OUTPUT , GPIO_PUPD_NONE , swd_swclk_pin ) ; // the host controls the clock
gpio_set_output_options ( swd_swclk_port , GPIO_OTYPE_PP , GPIO_OSPEED_50MHZ , swd_swclk_pin ) ; // set SWCLK pin output as push-pull
rcc_periph_clock_enable ( swd_swdio_rcc ) ; // enable clock for GPIO peripheral for data signal
gpio_set ( swd_swdio_port , swd_swdio_pin ) ; // inactive data is high (resetting the target when clock is active)
gpio_mode_setup ( swd_swdio_port , GPIO_MODE_OUTPUT , GPIO_PUPD_NONE , swd_swdio_pin ) ; // the data signal is half duplex, with the host controlling who is driving the data signal when
gpio_set_output_options ( swd_swdio_port , GPIO_OTYPE_PP , GPIO_OSPEED_50MHZ , swd_swdio_pin ) ; // set SWDIO pin output as push-pull
return true ;
2021-03-10 14:12:31 +01:00
}
/** perform SWD transaction (one sequence of bits read or write)
* @ param [ in ] output bits to write ( LSb first )
* @ param [ in ] bit_count number of bits to read / write
* @ param [ in ] write true for write transaction , false for read transaction
* @ return the bits read
*/
uint64_t swd_transaction ( uint64_t output , uint8_t bit_count , bool write )
{
// sanity check
if ( 0 = = bit_count ) {
return ~ 0ULL ;
}
// initialize read/write
if ( write ) {
2021-03-10 14:24:52 +01:00
gpio_mode_setup ( swd_swdio_port , GPIO_MODE_OUTPUT , GPIO_PUPD_NONE , swd_swdio_pin ) ; // we drive the data signal to output data
2021-03-10 14:12:31 +01:00
swd_bits_out = output ; // set data to output
} else {
2021-03-10 14:24:52 +01:00
gpio_mode_setup ( swd_swdio_port , GPIO_MODE_INPUT , GPIO_PUPD_PULLUP , swd_swdio_pin ) ; // the target will drive the data signal, we just pull it up
2021-03-10 14:12:31 +01:00
swd_bits_out = ~ 0ULL ; // set the value so we pull up
}
swd_bits_in = 0 ; // reset input buffer
swd_bits_count = bit_count ; // set the number of bits to read/write
swd_bits_i = 0 ; // reset read index
// start transaction
timer_enable_counter ( TIM ( SWD_TIMER ) ) ; // start timer
while ( swd_bits_i < swd_bits_count ) { // wait until all bits are transmitted
__WFI ( ) ; // go to sleep
}
return swd_bits_out ;
}
void swd_line_reset ( void )
{
2021-03-15 12:30:39 +01:00
swd_transaction ( ~ 0ULL , 50 + 2 , true ) ; // sent high for at least 50 cycle to issue line reset and put target in reset state
2021-03-10 14:12:31 +01:00
}
void swd_jtag_to_swd ( void )
{
swd_transaction ( 0xE79E , 16 , true ) ; // the sequence is a constant magic value
}
void swd_swd_to_jtag ( void )
{
swd_transaction ( 0xE73C , 16 , true ) ; // the sequence is a constant magic value
}
void swd_idle_cycles ( uint8_t nb )
{
swd_transaction ( 0 , nb , true ) ; // idle has data signal low
}
void swd_packet_request ( bool apndp , uint8_t a , bool rnw )
{
2021-03-10 18:26:32 +01:00
uint8_t request = ( 1 < < 0 ) | ( 0 < < 6 ) | ( 1 < < 7 ) ; // start, stop, and park bits are set
2021-03-10 14:12:31 +01:00
if ( apndp ) {
2021-03-10 18:26:32 +01:00
request | = ( 1 < < 1 ) ; // set APnDP bit
2021-03-10 14:12:31 +01:00
}
if ( rnw ) {
2021-03-10 18:26:32 +01:00
request | = ( 1 < < 2 ) ; // set RnW bit
2021-03-10 14:12:31 +01:00
}
2021-03-10 18:26:32 +01:00
request | = ( ( a & 0xc ) < < 1 ) ; // set A[3:2]
2021-03-10 14:12:31 +01:00
static const bool parity_lut [ ] = { false , true , true , false , true , false , false , true , true , false , false , true , false , true , true , false } ; // true if the number of 1's is a 4-bit value is odd, false else
if ( parity_lut [ ( request > > 1 ) & 0xf ] ) {
2021-03-10 18:26:32 +01:00
request | = ( 1 < < 5 ) ; // set even parity bit
2021-03-10 14:12:31 +01:00
}
swd_transaction ( request , 8 , true ) ; // write packet request
}
void swd_turnaround ( uint8_t nb )
{
swd_transaction ( ~ 0ULL , nb , false ) ; // switch to input to read and pull up
}
enum swd_ack_e swd_acknowledge_response ( void )
{
return swd_transaction ( ~ 0ULL , 3 , false ) & 0x7 ; // read 3 bits
}
void swd_write ( uint32_t wdata )
{
swd_transaction ( wdata | ( ( 0 = = __builtin_parity ( wdata ) ) ? ( 0ULL < < 32 ) : ( 1ULL < < 32 ) ) , 33 , true ) ; // set parity and send data
}
bool swd_read ( uint32_t * rdata )
{
uint64_t data = swd_transaction ( ~ 0UL , 33 , false ) ; // read 33 bits
* rdata = data ; // return the 32 bits of read data
return ( ( 0 = = __builtin_parity ( * rdata ) ) ? ( 0ULL < < 32 ) : ( 1ULL < < 32 ) ) = = ( data & ( 1ULL < < 32 ) ) ; // check parity
}
void swd_jtag_to_ds ( void )
{
swd_transaction ( ~ 0ULL , 5 , true ) ; // place JTAG TAP in TLR state
swd_transaction ( 0x33BBBBBA , 21 , true ) ; // send JTAG-to-DS select sequence
}
void swd_swd_to_ds ( void )
{
2021-03-15 12:30:39 +01:00
swd_line_reset ( ) ; // place SWD TAP is reset state
2021-03-10 14:12:31 +01:00
swd_transaction ( 0xE3BC , 16 , true ) ; // send SWD-to-DS select sequence
}
void swd_selection_alert ( enum swd_activation_code_e activation_code )
{
swd_transaction ( ~ 0ULL , 5 , true ) ; // ensure selection alert is detected
swd_transaction ( 0x6209F392 , 32 , true ) ; // send selection alert sequence (part 1)
swd_transaction ( 0x86852D95 , 32 , true ) ; // send selection alert sequence (part 2)
swd_transaction ( 0xE3DDAFE9 , 32 , true ) ; // send selection alert sequence (part 3)
swd_transaction ( 0x19BC0EA2 , 32 , true ) ; // send selection alert sequence (part 4)
swd_transaction ( 0 , 4 , true ) ; // send 4 low cycles
// send activation code, if known
switch ( activation_code ) {
case SWD_ACTIVATION_CODE_JTAG :
swd_transaction ( 0 , 24 , true ) ;
break ;
case SWD_ACTIVATION_CODE_SWDP :
swd_transaction ( 0x58 , 16 , true ) ;
break ;
case SWD_ACTIVATION_CODE_JTAGDP :
swd_transaction ( 0x50 , 16 , true ) ;
break ;
default :
break ; // unknown, but let the user issue it using swd_transaction
}
}
/** name of the manufacturer corresponding to its bank and id
* @ implements JEDEC JEP106
* @ note auto - generated and provided by the OpenOCD project ( https : //sourceforge.net/p/openocd/code/ci/master/tree/src/helper/jep106.inc?format=raw )
*/
static const char * const swd_jep106 [ ] [ 126 ] = {
# include "jep106.inc"
} ;
const char * swd_jep106_manufacturer ( uint8_t bank , uint8_t id )
{
// check id
if ( id < 1 | | id > 126 ) {
return " <invalid> " ;
}
// index is zero based
id - - ;
// check band and name
if ( bank > = LENGTH ( swd_jep106 ) | | 0 = = swd_jep106 [ bank ] [ id ] ) {
return " <unknown> " ;
}
return swd_jep106 [ bank ] [ id ] ;
}
static const struct swd_partno_s {
uint16_t designer ;
2021-03-16 12:15:35 +01:00
uint16_t partno ;
uint16_t partno_mask ;
2021-03-10 14:12:31 +01:00
const char * name ;
} swd_partno [ ] = {
// based on https://developer.arm.com/docs/103489943/latest/what-is-the-id-code-of-a-cortex-m0-dap-or-cortex-m0-dap
{
. designer = 0x23B , // ARM
2021-03-16 12:15:35 +01:00
. partno = 0xBA00 ,
. partno_mask = 0xFF00 ,
2021-03-10 14:12:31 +01:00
. name = " CM3DAP " , // used in Cortex-M3 and Cortex-M4
} ,
{
. designer = 0x23B , // ARM
2021-03-16 12:15:35 +01:00
. partno = 0xBB00 ,
. partno_mask = 0xFF00 ,
2021-03-10 14:12:31 +01:00
. name = " CM0DAP " , // used in Cortex-M0
} ,
{
. designer = 0x23B , // ARM
2021-03-16 12:15:35 +01:00
. partno = 0xBC00 ,
. partno_mask = 0xFF00 ,
2021-03-10 14:12:31 +01:00
. name = " CM0PDAP " , // used in Cortex-M0+
} ,
2021-03-16 12:15:35 +01:00
// based on RM0008 Reference manual, STM32F101xx, STM32F102xx, STM32F103xx, STM32F105xx and STM32F107xx advanced Arm®-based 32-bit MCUs
{
. designer = 0x020 , // ST
. partno = 0x6412 ,
. partno_mask = 0xFFFF ,
. name = " STM32F10xxx low-density " ,
} ,
{
. designer = 0x020 , // ST
. partno = 0x6410 ,
. partno_mask = 0xFFFF ,
. name = " STM32F10xxx medium-density " ,
} ,
{
. designer = 0x020 , // ST
. partno = 0x6414 ,
. partno_mask = 0xFFFF ,
. name = " STM32F10xxx high-density " ,
} ,
{
. designer = 0x020 , // ST
. partno = 0x6430 ,
. partno_mask = 0xFFFF ,
. name = " STM32F10xxx XL-density " ,
} ,
{
. designer = 0x020 , // ST
. partno = 0x6418 ,
. partno_mask = 0xFFFF ,
. name = " STM32F10xxx connectivity line " ,
} ,
// based on RM0368 Reference manual, STM32F401xB/C and STM32F401xD/E advanced Arm®-based 32-bit MCUs
{
. designer = 0x020 , // ST
. partno = 0x6423 ,
. partno_mask = 0xFFFF ,
. name = " STM32F401xB/C " ,
} ,
{
. designer = 0x020 , // ST
. partno = 0x6433 ,
. partno_mask = 0xFFFF ,
. name = " STM32F401xD/E " ,
} ,
2021-03-10 14:12:31 +01:00
} ;
2021-03-16 12:15:35 +01:00
const char * swd_dpidr_partno ( uint16_t designer , uint16_t partno )
2021-03-10 14:12:31 +01:00
{
uint32_t i = 0 ; // swd_partno index
// find matching part number
for ( i = 0 ; i < LENGTH ( swd_partno ) ; i + + ) {
2021-03-16 12:15:35 +01:00
if ( designer = = swd_partno [ i ] . designer & & ( ( partno & swd_partno [ i ] . partno_mask ) = = swd_partno [ i ] . partno ) ) {
2021-03-10 14:12:31 +01:00
break ;
}
}
if ( i < LENGTH ( swd_partno ) ) {
return swd_partno [ i ] . name ;
} else {
return " <unknown> " ;
}
}
/** interrupt service routine called for timer
*
* this is just acting as a shift register
2021-03-15 12:30:39 +01:00
* the host will write data on the falling edge ( - 5 ns < Tos < 5 ns ) , and read data just before the rising edge ( Tis > 4 ns )
2021-03-10 14:12:31 +01:00
* the target will read and write data signal on clock rising edge
* this phase shift is not very clear in the standard , but explains the line turn - round cycle when switching between writing and reading .
*
* only this ISR should change the data and clock signal output
* the timer stops on rising edge when the last bit is sent
* @ implements ARM DUI0499K 2.1 .2 Serial Wire Debug
*/
void TIM_ISR ( SWD_TIMER ) ( void )
{
static bool edge_falling = true ;
if ( timer_get_flag ( TIM ( SWD_TIMER ) , TIM_SR_UIF ) ) { // overflow update event happened
timer_clear_flag ( TIM ( SWD_TIMER ) , TIM_SR_UIF ) ; // clear flag
if ( swd_bits_i > = swd_bits_count ) { // end of activity reached, and no data has been made available in time
timer_disable_counter ( TIM ( SWD_TIMER ) ) ; // disable timer
return ; // nothing to do
}
if ( edge_falling ) { // falling edge: we output data
if ( swd_bits_out & ( 1ULL < < swd_bits_i ) ) { // output data, LSb first
2021-03-10 14:24:52 +01:00
gpio_set ( swd_swdio_port , swd_swdio_pin ) ; // output high
2021-03-10 14:12:31 +01:00
} else {
2021-03-10 14:24:52 +01:00
gpio_clear ( swd_swdio_port , swd_swdio_pin ) ; // output low
2021-03-10 14:12:31 +01:00
}
2021-03-10 14:24:52 +01:00
gpio_clear ( swd_swclk_port , swd_swclk_pin ) ; // output falling clock edge
2021-03-10 14:12:31 +01:00
} else { // rising edge: read data
2021-03-10 14:24:52 +01:00
if ( gpio_get ( swd_swdio_port , swd_swdio_pin ) ) { // read data
2021-03-10 14:12:31 +01:00
swd_bits_out | = ( 1ULL < < swd_bits_i ) ; // set bit
} else {
swd_bits_out & = ~ ( 1ULL < < swd_bits_i ) ; // clear bit
}
swd_bits_i + + ;
2021-03-10 14:24:52 +01:00
gpio_set ( swd_swclk_port , swd_swclk_pin ) ; // output rising clock edge
2021-03-10 14:12:31 +01:00
}
edge_falling = ! edge_falling ; // remember opposite upcoming edge
} else { // no other interrupt should occur
while ( true ) ; // unhandled exception: wait for the watchdog to bite
}
}