add SWD library
This commit is contained in:
parent
fa29cfc29f
commit
7a74f9709f
1416
lib/jep106.inc
Normal file
1416
lib/jep106.inc
Normal file
File diff suppressed because it is too large
Load Diff
324
lib/swd.c
Normal file
324
lib/swd.c
Normal file
@ -0,0 +1,324 @@
|
||||
/** 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 0074A)
|
||||
* @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
|
||||
* @{
|
||||
*/
|
||||
#define SWD_SWCLK_PIN PB10 /**< GPIO pin for clock signal */
|
||||
#define SWD_SWDIO_PIN PB2 /**< GPIO pin for data input/output signal */
|
||||
/** @} */
|
||||
|
||||
/** @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)
|
||||
{
|
||||
// setup GPIO for clock and data signals
|
||||
rcc_periph_clock_enable(GPIO_RCC(SWD_SWCLK_PIN)); // enable clock for GPIO peripheral for clock signal
|
||||
gpio_set(GPIO_PORT(SWD_SWCLK_PIN), GPIO_PIN(SWD_SWCLK_PIN)); // inactive clock is high
|
||||
gpio_mode_setup(GPIO_PORT(SWD_SWCLK_PIN), GPIO_MODE_OUTPUT, GPIO_PUPD_NONE, GPIO_PIN(SWD_SWCLK_PIN)); // the host controls the clock
|
||||
gpio_set_output_options(GPIO_PORT(SWD_SWCLK_PIN), GPIO_OTYPE_PP, GPIO_OSPEED_50MHZ, GPIO_PIN(SWD_SWCLK_PIN)); // set SWCLK pin output as push-pull
|
||||
rcc_periph_clock_enable(GPIO_RCC(SWD_SWDIO_PIN)); // enable clock for GPIO peripheral for data signal
|
||||
gpio_set(GPIO_PORT(SWD_SWDIO_PIN), GPIO_PIN(SWD_SWDIO_PIN)); // inactive data is high (resetting the target when clock is active)
|
||||
gpio_mode_setup(GPIO_PORT(SWD_SWDIO_PIN), GPIO_MODE_OUTPUT, GPIO_PUPD_NONE, GPIO_PIN(SWD_SWDIO_PIN)); // the data signal is half duplex, with the host controlling who is driving the data signal when
|
||||
gpio_set_output_options(GPIO_PORT(SWD_SWDIO_PIN), GPIO_OTYPE_PP, GPIO_OSPEED_50MHZ, GPIO_PIN(SWD_SWDIO_PIN)); // set SWDIO pin output as push-pull
|
||||
|
||||
// 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;
|
||||
}
|
||||
|
||||
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
|
||||
|
||||
// release GPIO
|
||||
gpio_mode_setup(GPIO_PORT(SWD_SWCLK_PIN), GPIO_MODE_INPUT, GPIO_PUPD_NONE, GPIO_PIN(SWD_SWCLK_PIN)); // put clock signal pin back to input floating
|
||||
gpio_mode_setup(GPIO_PORT(SWD_SWDIO_PIN), GPIO_MODE_INPUT, GPIO_PUPD_NONE, GPIO_PIN(SWD_SWDIO_PIN)); // put data signal pin back to input floating
|
||||
}
|
||||
|
||||
/** 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) {
|
||||
gpio_mode_setup(GPIO_PORT(SWD_SWDIO_PIN), GPIO_MODE_OUTPUT, GPIO_PUPD_NONE, GPIO_PIN(SWD_SWDIO_PIN)); // we drive the data signal to output data
|
||||
swd_bits_out = output; // set data to output
|
||||
} else {
|
||||
gpio_mode_setup(GPIO_PORT(SWD_SWDIO_PIN), GPIO_MODE_INPUT, GPIO_PUPD_PULLUP, GPIO_PIN(SWD_SWDIO_PIN)); // the target will drive the data signal, we just pull it up
|
||||
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)
|
||||
{
|
||||
swd_transaction(~0ULL, 50+1, true); // sent high for at least 50 cycle to issue line reset and put target in reset state
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
uint8_t request = (1<<0)|(0<<6)|(1<<7); // start, stop, and park bits are set
|
||||
if (apndp) {
|
||||
request |= (1<<1); // set APnDP bit
|
||||
}
|
||||
if (rnw) {
|
||||
request |= (1<<2); // set RnW bit
|
||||
}
|
||||
request |= ((a & 0xc)<<1); // set A[3:2]
|
||||
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]) {
|
||||
request |= (1<<5); // set even parity bit
|
||||
}
|
||||
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)
|
||||
{
|
||||
swd_transaction(~0ULL, 50+1, true); // place SWD TAP is reset state
|
||||
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;
|
||||
uint8_t partno;
|
||||
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
|
||||
.partno = 0xBA,
|
||||
.name = "CM3DAP", // used in Cortex-M3 and Cortex-M4
|
||||
},
|
||||
{
|
||||
.designer = 0x23B, // ARM
|
||||
.partno = 0xBB,
|
||||
.name = "CM0DAP", // used in Cortex-M0
|
||||
},
|
||||
{
|
||||
.designer = 0x23B, // ARM
|
||||
.partno = 0xBC,
|
||||
.name = "CM0PDAP", // used in Cortex-M0+
|
||||
},
|
||||
};
|
||||
|
||||
const char* swd_dpidr_partno(uint16_t designer, uint8_t partno)
|
||||
{
|
||||
uint32_t i = 0; // swd_partno index
|
||||
// find matching part number
|
||||
for (i = 0; i < LENGTH(swd_partno); i++) {
|
||||
if (designer == swd_partno[i].designer && partno == swd_partno[i].partno) {
|
||||
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
|
||||
* the host will write data on the falling edge, and read data just before the rising edge (we could also do it on the rising edge)
|
||||
* 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
|
||||
gpio_set(GPIO_PORT(SWD_SWDIO_PIN), GPIO_PIN(SWD_SWDIO_PIN)); // output high
|
||||
} else {
|
||||
gpio_clear(GPIO_PORT(SWD_SWDIO_PIN), GPIO_PIN(SWD_SWDIO_PIN)); // output low
|
||||
}
|
||||
gpio_clear(GPIO_PORT(SWD_SWCLK_PIN), GPIO_PIN(SWD_SWCLK_PIN)); // output falling clock edge
|
||||
} else { // rising edge: read data
|
||||
if (gpio_get(GPIO_PORT(SWD_SWDIO_PIN), GPIO_PIN(SWD_SWDIO_PIN))) { // read data
|
||||
swd_bits_out |= (1ULL << swd_bits_i); // set bit
|
||||
} else {
|
||||
swd_bits_out &= ~(1ULL << swd_bits_i); // clear bit
|
||||
}
|
||||
swd_bits_i++;
|
||||
gpio_set(GPIO_PORT(SWD_SWCLK_PIN), GPIO_PIN(SWD_SWCLK_PIN)); // output rising clock edge
|
||||
}
|
||||
edge_falling = !edge_falling; // remember opposite upcoming edge
|
||||
} else { // no other interrupt should occur
|
||||
while (true); // unhandled exception: wait for the watchdog to bite
|
||||
}
|
||||
}
|
197
lib/swd.h
Normal file
197
lib/swd.h
Normal file
@ -0,0 +1,197 @@
|
||||
/** 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 0074A)
|
||||
* @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)
|
||||
*/
|
||||
/** @remark{
|
||||
* This library implements DP architecture version 3 (DPv3), but only DPv1 feature could be tested.
|
||||
*
|
||||
* See B2.1.1 DP architecture versions summary for more details about the versions:
|
||||
* - DPv0 is only available using JTAG (e.g. does not support SWD).
|
||||
* - DPv1 introduces SWD (with protocol version 1).
|
||||
* - DPv2 adds multi-drop support (with protocol version 2).
|
||||
* - DPv3 add register for 64-bit support.
|
||||
*
|
||||
* I couldn't find any hardware supporting SWD protocol version 2 to test DPv2 and DPv3 features, in particular dormant state and target select.
|
||||
* According to https://developer.arm.com/docs/103489514/latest/does-the-cortex-m3-or-cortex-m4-processor-that-i-have-licensed-support-multi-drop-serial-wire-debug, although it is a property of the debug standard (e.g. CoreSight block) rather than core, no Cortex-M3 or Cortex-M4 seem to support DPv2/SWDv2.
|
||||
* }
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
/** Debug Port (DP) register address
|
||||
* @implements ARM IHI 0074A B2.1.2 DP architecture version 3 (DPv3) address map
|
||||
*/
|
||||
enum swd_a_dp_e {
|
||||
SWD_A_DP_DPIDR = 0x0, /**< Debug Port Identification Register (DP: v1, access: RO) */
|
||||
SWD_A_DP_DPIDR1 = 0x0, /**< Debug Port Identification Register 1 (DP: v3, access: RO) */
|
||||
SWD_A_DP_BASEPTR0 = 0x0, /**< Base Pointer 0 (DP: v3, access: RO) */
|
||||
SWD_A_DP_BASEPTR1 = 0x0, /**< Base Pointer 1 (DP: v3, access: RO) */
|
||||
SWD_A_DP_ABORT = 0x0, /**< AP Abort register (DP: v0, access: WO) */
|
||||
SWD_A_DP_CTRL_STAT = 0x4, /**< Control/Status register (DP: v0, access: RW) */
|
||||
SWD_A_DP_DLCR = 0x4, /**< Data Link Control Register (DP: v1, access: RW) */
|
||||
SWD_A_DP_TARGETID = 0x4, /**< Target Identification register (DP: v2, access: RO) */
|
||||
SWD_A_DP_DLPIDR = 0x4, /**< Data Link Protocol Identification Register (DP: v2, access: RO) */
|
||||
SWD_A_DP_EVENTSTAT = 0x4, /**< Event Status register (DP: v0, access: RO) */
|
||||
SWD_A_DP_SELECT1 = 0x4, /**< AP Select register 1 (DP: v3, access: WO) */
|
||||
SWD_A_DP_SELECT = 0x8, /**< AP Select register (DP: v0, access: WO) */
|
||||
SWD_A_DP_RESEND = 0x8, /**< Read Resend register (DP: v1, access: RO) */
|
||||
SWD_A_DP_RDBUFF = 0xc, /**< Read Buffer register (DP: v0, access: RO) */
|
||||
SWD_A_DP_TARGETSEL = 0xc, /**< Target Selection register (DP: v2, access: WO) */
|
||||
};
|
||||
|
||||
/** Access Port (AP) MEM-AP register address
|
||||
* @implements ARM IHI 0074A C2.5 MEM-AP Programmers’ Model
|
||||
* @note only the registers addressable with A[3:2] are included
|
||||
*/
|
||||
enum swd_a_ap_memap_e {
|
||||
SWD_A_AP_MEMAP_CSW = 0x0, /**< Control/Status Word register (access: RW) */
|
||||
SWD_A_AP_MEMAP_TAR = 0x4, /**< Transfer Address Register (access: RW) */
|
||||
SWD_A_AP_MEMAP_TAR_EXT = 0x8, /**< Transfer Address Register, large physical address extension (access: RW) */
|
||||
SWD_A_AP_MEMAP_DRW = 0xc, /**< Data Read/Write register (access: RW) */
|
||||
};
|
||||
|
||||
/** Access Port (AP) JTAG-AP register address
|
||||
* @implements ARM IHI 0074A C3.4 JTAG-AP programmers’ model
|
||||
* @note only the registers addressable with A[3:2] are included
|
||||
*/
|
||||
enum swd_a_ap_jtagap_e {
|
||||
SWD_A_AP_JTAGAP_CSW = 0x0, /**< Control/Status Word register (access: RW) */
|
||||
SWD_A_AP_JTAGAP_PSEL = 0x4, /**< Port Select register (access: RW) */
|
||||
SWD_A_AP_JTAGAP_PSTA = 0x8, /**< Port Status Register (access: RW) */
|
||||
};
|
||||
|
||||
/** ACk acknowledge response
|
||||
* @implements ARM IHI 0074A B4.2 SWD protocol operation
|
||||
*/
|
||||
enum swd_ack_e {
|
||||
SWD_ACK_OK = 0x1, /**< Successful operation */
|
||||
SWD_ACK_WAIT = 0x2, /**< Wait for response */
|
||||
SWD_ACK_FAULT = 0x4, /**< Fault */
|
||||
SWD_ACK_ERROR = 0x7, /**< Error (pulled-up line not driven) */
|
||||
};
|
||||
|
||||
/** Activation codes
|
||||
* @implements ARM IHI 0074A B5.3.4 Leaving dormant state
|
||||
*/
|
||||
enum swd_activation_code_e {
|
||||
SWD_ACTIVATION_CODE_JTAG, /**< JTAG-Serial */
|
||||
SWD_ACTIVATION_CODE_SWDP, /**< ARM CoreSight SW-DP */
|
||||
SWD_ACTIVATION_CODE_JTAGDP, /**< ARM CoreSight JTAG-DP */
|
||||
};
|
||||
|
||||
/** setup SWD peripheral
|
||||
*/
|
||||
void swd_setup(uint32_t freq);
|
||||
/** release 1-wire peripheral
|
||||
*/
|
||||
void swd_release(void);
|
||||
|
||||
/* DPv1/SWDv1 operations */
|
||||
|
||||
/** send line reset sequence to put target in reset state
|
||||
* @implements ARM IHI 0074A B4.3.3 Connection and line reset sequence
|
||||
* @note this does not include the tailing idle cycles
|
||||
*/
|
||||
void swd_line_reset(void);
|
||||
/** send JTAG to SWD sequence to switch the target SWJ-DP from JTAG to SWD
|
||||
* @note a line reset must be perform just before
|
||||
* @implements ARM IHI 0074A B5.2.2 Switching from JTAG to SWD operation
|
||||
*/
|
||||
void swd_jtag_to_swd(void);
|
||||
/** send SWD to JTAG sequence to switch the target SWJ-DP from SWD to JTAG
|
||||
* @note a line reset must be perform just before
|
||||
* @implements ARM IHI 0074A B5.2.3 Switching from SWD to JTAG operation
|
||||
*/
|
||||
void swd_swd_to_jtag(void);
|
||||
/** send idle cycles
|
||||
* @param[in] nb number idle cycles to send
|
||||
* @note at least two idle cycles a required after a line reset and before a packet request
|
||||
* @implements ARM IHI 0074A B4.1.4 Idle cycles
|
||||
*/
|
||||
void swd_idle_cycles(uint8_t nb);
|
||||
/** send packet request
|
||||
* @param[in] apndp true to access the Access Port (AP) or false to access the Debug Port (DP) access register
|
||||
* @param[in] a address field for the DP or AP register Address (complete address, not just bits 3:2)
|
||||
* @param[in] rnw true for read access, false for write access
|
||||
* @implements ARM IHI 0074A B4.2 SWD protocol operation
|
||||
*/
|
||||
void swd_packet_request(bool apndp, uint8_t a, bool rnw);
|
||||
/** insert turnaround cycle
|
||||
* @param[in] nb number idle for the turnaround (by default 1, else set in DLCR.TURNROUND)
|
||||
* @note used to switch between reading and writing on the data signal
|
||||
* @implements ARM IHI 0074A B4.1.3 Line turnaround
|
||||
*/
|
||||
void swd_turnaround(uint8_t nb);
|
||||
/** read acknowledge response (ACK)
|
||||
* @return ACK
|
||||
* @implements ARM IHI 0074A B4.2 SWD protocol operation
|
||||
*/
|
||||
enum swd_ack_e swd_acknowledge_response(void);
|
||||
/** write data
|
||||
* @param[in] wdata data to write
|
||||
* @implements ARM IHI 0074A B4.2 SWD protocol operation
|
||||
*/
|
||||
void swd_write(uint32_t wdata);
|
||||
/** read data
|
||||
* @param[out] rdata read data
|
||||
* @return if parity check passed
|
||||
* @implements ARM IHI 0074A B4.2 SWD protocol operation
|
||||
*/
|
||||
bool swd_read(uint32_t* rdata);
|
||||
|
||||
/* DPv2/SWDv2 operations */
|
||||
|
||||
/** send JTAG to dormant state (DS) sequence
|
||||
* @note the preceding line reset does not seem mandatory, but recommended
|
||||
* @implements ARM IHI 0074A B5.3.2 Switching from JTAG to dormant state
|
||||
* @remark it is not mentioned what happens if the TAP is in SWD state (probably nothing)
|
||||
*/
|
||||
void swd_jtag_to_ds(void);
|
||||
|
||||
/** send SWD to dormant state (DS) sequence
|
||||
* @note no preceding line reset is requires
|
||||
* @implements ARM IHI 0074A B5.3.3 Switching from SWD to dormant state
|
||||
* @remark it is not mentioned what happens if the TAP is in JTAG state (probably nothing)
|
||||
*/
|
||||
void swd_swd_to_ds(void);
|
||||
|
||||
/** send selection code and activation alert to leave dormant state and go into JTAG or SWD mode
|
||||
* @param[in] activation_code activation code to select protocol (JTAG, or SWD)
|
||||
* @implements ARM IHI 0074A B5.3.3 Switching from SWD to dormant state
|
||||
* @remark it is not mentioned what happens if the TAP is in any other state
|
||||
*/
|
||||
void swd_selection_alert(enum swd_activation_code_e activation_code);
|
||||
|
||||
|
||||
/** 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
|
||||
* @note this should only be used for unimplemented operations
|
||||
*/
|
||||
uint64_t swd_transaction(uint64_t output, uint8_t bit_count, bool write);
|
||||
|
||||
/** get manufacturer name corresponding to the designer id
|
||||
* @param[in] bank bank number (first 4 bits of the DESIGNER value)
|
||||
* @param[in] id JEP106 id (last 7 bits of the DESIGNER value)
|
||||
* @return manufacturer name
|
||||
* @implements JEDEC JEP106
|
||||
* @note from the OpenOCD project
|
||||
* @date 2015
|
||||
* @copyright Andreas Fritiofson <andreas.fritiofson@gmail.com>
|
||||
*/
|
||||
const char* swd_jep106_manufacturer(uint8_t bank, uint8_t id);
|
||||
|
||||
/** get DAP name corresponding to the DPIDR part number
|
||||
* @param[in] designer DESIGNER value
|
||||
* @param[in] partno DPIDR part number
|
||||
* @return DAP name
|
||||
*/
|
||||
const char* swd_dpidr_partno(uint16_t designer, uint8_t partno);
|
||||
|
Loading…
Reference in New Issue
Block a user