Skip to content

Commit

Permalink
probe: add CMSIS-DAP PIO-driven implementation
Browse files Browse the repository at this point in the history
CMSIS-DAP implementations explicitly use CPU bitbash to control SWD and
SWDIO, but we can do better. Transfers are handed off to PIO in phases,
which gets us deterministic clock timing as well as freeing the CPU up
for other tasks.

Signed-off-by: Jonathan Bell <[email protected]>
  • Loading branch information
P33M committed Aug 18, 2022
1 parent 02b3199 commit 592b8da
Show file tree
Hide file tree
Showing 3 changed files with 221 additions and 11 deletions.
3 changes: 2 additions & 1 deletion CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -13,14 +13,15 @@ add_executable(picoprobe
src/probe.c
src/cdc_uart.c
src/get_serial.c
src/sw_dp_pio.c
)

target_sources(picoprobe PRIVATE
CMSIS_5/CMSIS/DAP/Firmware/Source/DAP.c
CMSIS_5/CMSIS/DAP/Firmware/Source/JTAG_DP.c
CMSIS_5/CMSIS/DAP/Firmware/Source/DAP_vendor.c
CMSIS_5/CMSIS/DAP/Firmware/Source/SWO.c
CMSIS_5/CMSIS/DAP/Firmware/Source/SW_DP.c
#CMSIS_5/CMSIS/DAP/Firmware/Source/SW_DP.c
)

target_include_directories(picoprobe PRIVATE
Expand Down
27 changes: 17 additions & 10 deletions include/DAP_config.h
Original file line number Diff line number Diff line change
Expand Up @@ -43,11 +43,17 @@ This information includes:
- Debug Access Port supported modes and settings (JTAG/SWD and SWO).
- Optional information about a connected Target Device (for Evaluation Boards).
*/
#include <pico/stdlib.h>
#include <hardware/gpio.h>

#include "cmsis_compiler.h"
#include "picoprobe_config.h"
#include "probe.h"

/// Processor Clock of the Cortex-M MCU used in the Debug Unit.
/// This value is used to calculate the SWD/JTAG clock speed.
#define CPU_CLOCK 100000000U ///< Specifies the CPU Clock in Hz.
/* Picoprobe actually uses kHz rather than Hz, so just lie about it here */
#define CPU_CLOCK 125000000U ///< Specifies the CPU Clock in Hz.

/// Number of processor cycles for I/O Port write operations.
/// This value is used to calculate the SWD/JTAG clock speed that is generated with I/O
Expand All @@ -56,10 +62,11 @@ This information includes:
/// a Cortex-M0+ processor with high-speed peripheral I/O only 1 processor cycle might be
/// required.
#define IO_PORT_WRITE_CYCLES 1U ///< I/O Cycles: 2=default, 1=Cortex-M0+ fast I/0.
#define DELAY_SLOW_CYCLES 1U // We don't differentiate between fast/slow, we've got a 16-bit divisor for that

/// Indicate that Serial Wire Debug (SWD) communication mode is available at the Debug Access Port.
/// This information is returned by the command \ref DAP_Info as part of <b>Capabilities</b>.
#define DAP_SWD 0 ///< SWD Mode: 1 = available, 0 = not available.
#define DAP_SWD 1 ///< SWD Mode: 1 = available, 0 = not available.

/// Indicate that JTAG communication mode is available at the Debug Port.
/// This information is returned by the command \ref DAP_Info as part of <b>Capabilities</b>.
Expand All @@ -82,13 +89,13 @@ This information includes:
/// This configuration settings is used to optimize the communication performance with the
/// debugger and depends on the USB peripheral. Typical vales are 64 for Full-speed USB HID or WinUSB,
/// 1024 for High-speed USB HID and 512 for High-speed USB WinUSB.
#define DAP_PACKET_SIZE 512U ///< Specifies Packet Size in bytes.
#define DAP_PACKET_SIZE 64U ///< Specifies Packet Size in bytes.

/// Maximum Package Buffers for Command and Response data.
/// This configuration settings is used to optimize the communication performance with the
/// debugger and depends on the USB peripheral. For devices with limited RAM or USB buffer the
/// setting can be reduced (valid range is 1 .. 255).
#define DAP_PACKET_COUNT 8U ///< Specifies number of packets buffered.
#define DAP_PACKET_COUNT 2U ///< Specifies number of packets buffered.

/// Indicate that UART Serial Wire Output (SWO) trace is available.
/// This information is returned by the command \ref DAP_Info as part of <b>Capabilities</b>.
Expand All @@ -111,7 +118,7 @@ This information includes:
#define SWO_STREAM 0 ///< SWO Streaming Trace: 1 = available, 0 = not available.

/// Clock frequency of the Test Domain Timer. Timer value is returned with \ref TIMESTAMP_GET.
#define TIMESTAMP_CLOCK 100000000U ///< Timestamp clock in Hz (0 = timestamps not supported).
#define TIMESTAMP_CLOCK 1000000U ///< Timestamp clock in Hz (0 = timestamps not supported).

/// Indicate that UART Communication Port is available.
/// This information is returned by the command \ref DAP_Info as part of <b>Capabilities</b>.
Expand Down Expand Up @@ -309,15 +316,15 @@ Configures the DAP Hardware I/O pins for Serial Wire Debug (SWD) mode:
- TDI, nTRST to HighZ mode (pins are unused in SWD mode).
*/
__STATIC_INLINE void PORT_SWD_SETUP (void) {
;
probe_init();
}

/** Disable JTAG/SWD I/O Pins.
Disables the DAP Hardware I/O pins which configures:
- TCK/SWCLK, TMS/SWDIO, TDI, TDO, nTRST, nRESET to High-Z mode.
*/
__STATIC_INLINE void PORT_OFF (void) {
;
probe_deinit();
}


Expand Down Expand Up @@ -387,15 +394,15 @@ Configure the SWDIO DAP hardware I/O pin to output mode. This function is
called prior \ref PIN_SWDIO_OUT function calls.
*/
__STATIC_FORCEINLINE void PIN_SWDIO_OUT_ENABLE (void) {
;
probe_write_mode();
}

/** SWDIO I/O pin: Switch to Input mode (used in SWD mode only).
Configure the SWDIO DAP hardware I/O pin to input mode. This function is
called prior \ref PIN_SWDIO_IN function calls.
*/
__STATIC_FORCEINLINE void PIN_SWDIO_OUT_DISABLE (void) {
;
probe_read_mode();
}


Expand Down Expand Up @@ -511,7 +518,7 @@ default, the DWT timer is used. The frequency of this timer is configured with
\return Current timestamp value.
*/
__STATIC_INLINE uint32_t TIMESTAMP_GET (void) {
//return (DWT->CYCCNT);
return time_us_32();
}

///@}
Expand Down
202 changes: 202 additions & 0 deletions src/sw_dp_pio.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,202 @@
/*
* Copyright (c) 2013-2022 ARM Limited. All rights reserved.
* Copyright (c) 2022 Raspberry Pi Ltd
*
* SPDX-License-Identifier: Apache-2.0
*
* Licensed under the Apache License, Version 2.0 (the License); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an AS IS BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

/*
* This is a shim between the SW_DP functions and the PIO
* implementation used for Picoprobe. Instead of calling bitbash functions,
* hand off the bit sequences to a SM for asynchronous completion.
*/

#include <stdio.h>

#include "DAP_config.h"
#include "DAP.h"
#include "probe.h"

/* Slight hack - we're not bitbashing so we need to set baudrate off the DAP's delay cycles.
* Ideally we don't want calls to udiv everywhere... */
#define MAKE_KHZ(x) (CPU_CLOCK / (2000 * ((x) + 1)))

// Generate SWJ Sequence
// count: sequence bit count
// data: pointer to sequence bit data
// return: none
#if ((DAP_SWD != 0) || (DAP_JTAG != 0))
void SWJ_Sequence (uint32_t count, const uint8_t *data) {
uint32_t bits;
uint32_t n;

probe_set_swclk_freq(MAKE_KHZ(DAP_Data.clock_delay));
picoprobe_info("SWJ sequence count = %d FDB=0x%2x\n", count, data[0]);
n = count;
while (n > 0) {
if (n > 8)
bits = 8;
else
bits = n;
probe_write_bits(bits, *data++);
n -= bits;
}
}
#endif

// Generate SWD Sequence
// info: sequence information
// swdo: pointer to SWDIO generated data
// swdi: pointer to SWDIO captured data
// return: none
#if (DAP_SWD != 0)
void SWD_Sequence (uint32_t info, const uint8_t *swdo, uint8_t *swdi) {
uint32_t bits;
uint32_t n;

probe_set_swclk_freq(MAKE_KHZ(DAP_Data.clock_delay));
picoprobe_info("SWD sequence\n");
n = info & SWD_SEQUENCE_CLK;
if (n == 0U) {
n = 64U;
}
bits = n;
if (info & SWD_SEQUENCE_DIN) {
while (n > 0) {
if (n > 8)
bits = 8;
else
bits = n;
*swdi++ = probe_read_bits(bits);
n -= bits;
}
} else {
while (n > 0) {
if (n > 8)
bits = 8;
else
bits = n;
probe_write_bits(bits, *swdo++);
n -= bits;
}
}
}
#endif

#if (DAP_SWD != 0)
// SWD Transfer I/O
// request: A[3:2] RnW APnDP
// data: DATA[31:0]
// return: ACK[2:0]
uint8_t SWD_Transfer (uint32_t request, uint32_t *data) {
uint8_t prq = 0;
uint8_t ack;
uint8_t bit;
uint32_t val = 0;
uint32_t parity = 0;
uint32_t n;

probe_set_swclk_freq(MAKE_KHZ(DAP_Data.clock_delay));
picoprobe_info("SWD_transfer\n");
/* Generate the request packet */
prq |= (1 << 0); /* Start Bit */
for (n = 1; n < 5; n++) {
bit = (request >> (n - 1)) & 0x1;
prq |= bit << n;
parity += bit;
}
prq |= (parity & 0x1) << 5; /* Parity Bit */
prq |= (0 << 6); /* Stop Bit */
prq |= (1 << 7); /* Park bit */
probe_write_bits(8, prq);

/* Turnaround (ignore read bits) */
probe_read_mode();
probe_read_bits(DAP_Data.swd_conf.turnaround);

ack = probe_read_bits(3);

if (ack == DAP_TRANSFER_OK) {
/* Data transfer phase */
if (request & DAP_TRANSFER_RnW) {
parity = 0;
/* Read RDATA[0:31] - note probe_read shifts to LSBs */
for (n = 0; n < 32; n += 8) {
bit = probe_read_bits(8);
parity += __builtin_popcount(bit);
val |= (bit & 0xff) << n;
}
bit = probe_read_bits(1);
if ((parity ^ bit) & 1U) {
/* Parity error */
ack = DAP_TRANSFER_ERROR;
}
if (data)
*data = val;

/* Turnaround for line idle */
probe_read_bits(DAP_Data.swd_conf.turnaround);
probe_write_mode();
} else {
/* Turnaround for write */
probe_read_bits(DAP_Data.swd_conf.turnaround);
probe_write_mode();

/* Write WDATA[0:31] */
val = *data;
parity = 0;
for (n = 0; n < 32; n += 8) {
bit = (val >> n) & 0xff;
probe_write_bits(8, bit);
parity += __builtin_popcount(bit);
}
/* Write Parity Bit */
probe_write_bits(1, parity & 0x1);
}
/* Capture Timestamp */
if (request & DAP_TRANSFER_TIMESTAMP) {
DAP_Data.timestamp = time_us_32();
}

/* Idle cycles - drive 0 for N clocks */
if (DAP_Data.transfer.idle_cycles) {
for (n = DAP_Data.transfer.idle_cycles; n; ) {
if (n > 8) {
probe_write_bits(8, 0);
n -= 8;
} else {
probe_write_bits(n, 0);
n -= n;
}
}
}
return ((uint8_t)ack);
}

if ((ack == DAP_TRANSFER_WAIT) || (ack == DAP_TRANSFER_FAULT)) {
probe_read_bits(DAP_Data.swd_conf.turnaround);
probe_write_mode();
return ((uint8_t)ack);
}

/* Protocol error */
n = DAP_Data.swd_conf.turnaround + 32U + 1U;
/* Back off data phase */
probe_read_bits(n);
probe_write_mode();
return ((uint8_t)ack);
}

#endif /* (DAP_SWD != 0) */

0 comments on commit 592b8da

Please sign in to comment.