add SWD library

This commit is contained in:
King Kévin 2021-03-10 14:12:31 +01:00
parent fa29cfc29f
commit 7a74f9709f
3 changed files with 1937 additions and 0 deletions

1416
lib/jep106.inc Normal file

File diff suppressed because it is too large Load Diff

324
lib/swd.c Normal file
View 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
View 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);