/** library for Serial Wire Debug (SWD) communication * @file * @author King Kévin * @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_NOREPLY = 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 * @param[in] freq clock frequency, in Hz * @note above 50 kHz the communication still works, but the clock is not periodic anymore */ void swd_setup(uint32_t freq); /** release peripherals used for SWD communication */ void swd_release(void); /** release used pins */ void swd_release_pins(void); /** set SWCLK and SWDIO pins * @param[in] swclk_port port for SWCLK pin * @param[in] swclk_pin pin for SWCLK pin * @param[in] swclk_port port for SWDIO pin * @param[in] swclk_pin pin for SWDIO pin * @return true if operation succeeded, false if pin is unknown */ bool swd_set_pins(uint32_t swclk_port, uint32_t swclk_pin, uint32_t swdio_port, uint32_t swdio_pin); /* 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 */ 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 (bits 1-11 in DPIDR/IDCODE) * @param[in] partno PARTNO part number bits 12-27 in DPIDR/IDCODE) * @return DAP name */ const char* swd_dpidr_partno(uint16_t designer, uint16_t partno);