| /* |
| * 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 Debugprobe. 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))) |
| volatile uint32_t cached_delay = 0; |
| |
| // 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; |
| |
| if (DAP_Data.clock_delay != cached_delay) { |
| probe_set_swclk_freq(MAKE_KHZ(DAP_Data.clock_delay)); |
| cached_delay = DAP_Data.clock_delay; |
| } |
| probe_debug("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; |
| |
| if (DAP_Data.clock_delay != cached_delay) { |
| probe_set_swclk_freq(MAKE_KHZ(DAP_Data.clock_delay)); |
| cached_delay = DAP_Data.clock_delay; |
| } |
| probe_debug("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; |
| |
| if (DAP_Data.clock_delay != cached_delay) { |
| probe_set_swclk_freq(MAKE_KHZ(DAP_Data.clock_delay)); |
| cached_delay = DAP_Data.clock_delay; |
| } |
| probe_debug("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) */ |
| ack = probe_read_bits(DAP_Data.swd_conf.turnaround + 3); |
| ack >>= DAP_Data.swd_conf.turnaround; |
| |
| if (ack == DAP_TRANSFER_OK) { |
| /* Data transfer phase */ |
| if (request & DAP_TRANSFER_RnW) { |
| /* Read RDATA[0:31] - note probe_read shifts to LSBs */ |
| val = probe_read_bits(32); |
| bit = probe_read_bits(1); |
| parity = __builtin_popcount(val); |
| if ((parity ^ bit) & 1U) { |
| /* Parity error */ |
| ack = DAP_TRANSFER_ERROR; |
| } |
| if (data) |
| *data = val; |
| probe_debug("Read %02x ack %02x 0x%08x parity %01x\n", |
| prq, ack, val, bit); |
| /* Turnaround for line idle */ |
| probe_hiz_clocks(DAP_Data.swd_conf.turnaround); |
| } else { |
| /* Turnaround for write */ |
| probe_hiz_clocks(DAP_Data.swd_conf.turnaround); |
| |
| /* Write WDATA[0:31] */ |
| val = *data; |
| probe_write_bits(32, val); |
| parity = __builtin_popcount(val); |
| /* Write Parity Bit */ |
| probe_write_bits(1, parity & 0x1); |
| probe_debug("write %02x ack %02x 0x%08x parity %01x\n", |
| prq, ack, val, parity); |
| } |
| /* 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 > 256) { |
| probe_write_bits(256, 0); |
| n -= 256; |
| } else { |
| probe_write_bits(n, 0); |
| n -= n; |
| } |
| } |
| } |
| return ((uint8_t)ack); |
| } |
| |
| if ((ack == DAP_TRANSFER_WAIT) || (ack == DAP_TRANSFER_FAULT)) { |
| if (DAP_Data.swd_conf.data_phase && ((request & DAP_TRANSFER_RnW) != 0U)) { |
| /* Dummy Read RDATA[0:31] + Parity */ |
| probe_read_bits(33); |
| } |
| probe_hiz_clocks(DAP_Data.swd_conf.turnaround); |
| if (DAP_Data.swd_conf.data_phase && ((request & DAP_TRANSFER_RnW) == 0U)) { |
| /* Dummy Write WDATA[0:31] + Parity */ |
| probe_write_bits(32, 0); |
| probe_write_bits(1, 0); |
| } |
| return ((uint8_t)ack); |
| } |
| |
| /* Protocol error */ |
| n = DAP_Data.swd_conf.turnaround + 32U + 1U; |
| /* Back off data phase */ |
| probe_read_bits(n); |
| return ((uint8_t)ack); |
| } |
| |
| #endif /* (DAP_SWD != 0) */ |