diff --git a/CMakeLists.txt b/CMakeLists.txt index 1cf33e4..52bd806 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -13,6 +13,7 @@ add_executable(picoprobe src/probe.c src/cdc_uart.c src/get_serial.c + src/sw_dp_pio.c ) target_sources(picoprobe PRIVATE @@ -20,7 +21,7 @@ target_sources(picoprobe PRIVATE 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 diff --git a/include/DAP_config.h b/include/DAP_config.h index 878e03b..e1d91c0 100755 --- a/include/DAP_config.h +++ b/include/DAP_config.h @@ -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 +#include + #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 @@ -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 Capabilities. -#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 Capabilities. @@ -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 Capabilities. @@ -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 Capabilities. @@ -309,7 +316,7 @@ 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. @@ -317,7 +324,7 @@ 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(); } @@ -387,7 +394,7 @@ 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). @@ -395,7 +402,7 @@ 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(); } @@ -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(); } ///@} diff --git a/src/sw_dp_pio.c b/src/sw_dp_pio.c new file mode 100755 index 0000000..a8758dd --- /dev/null +++ b/src/sw_dp_pio.c @@ -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 + +#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) */