SWD PIO works now, too!

This commit is contained in:
Triss 2021-09-25 13:22:09 +02:00
parent 9aeaea5d9f
commit b461946751
10 changed files with 386 additions and 189 deletions

View File

@ -92,7 +92,7 @@ __WEAK void SWJ_Sequence (uint32_t count, const uint8_t *data) {
// swdi: pointer to SWDIO captured data // swdi: pointer to SWDIO captured data
// return: none // return: none
#if (DAP_SWD != 0) #if (DAP_SWD != 0)
void SWD_Sequence (uint32_t info, const uint8_t *swdo, uint8_t *swdi) { __WEAK void SWD_Sequence (uint32_t info, const uint8_t *swdo, uint8_t *swdi) {
uint32_t val; uint32_t val;
uint32_t bit; uint32_t bit;
uint32_t n, k; uint32_t n, k;

View File

@ -137,6 +137,7 @@ if(FAMILY STREQUAL "rp2040")
endif() endif()
pico_generate_pio_header(${PROJECT} ${CMAKE_CURRENT_SOURCE_DIR}/bsp/${FAMILY}/m_default/dap_jtag.pio) pico_generate_pio_header(${PROJECT} ${CMAKE_CURRENT_SOURCE_DIR}/bsp/${FAMILY}/m_default/dap_jtag.pio)
pico_generate_pio_header(${PROJECT} ${CMAKE_CURRENT_SOURCE_DIR}/bsp/${FAMILY}/m_default/dap_swd.pio)
pico_generate_pio_header(${PROJECT} ${CMAKE_CURRENT_SOURCE_DIR}/bsp/${FAMILY}/m_default/swo_uart_rx.pio) pico_generate_pio_header(${PROJECT} ${CMAKE_CURRENT_SOURCE_DIR}/bsp/${FAMILY}/m_default/swo_uart_rx.pio)
pico_generate_pio_header(${PROJECT} ${CMAKE_CURRENT_SOURCE_DIR}/bsp/${FAMILY}/m_default/swo_manchester_encoding.pio) pico_generate_pio_header(${PROJECT} ${CMAKE_CURRENT_SOURCE_DIR}/bsp/${FAMILY}/m_default/swo_manchester_encoding.pio)

36
LICENSE.pico-examples Normal file
View File

@ -0,0 +1,36 @@
The BSD-3-Clause license applies to portions of the following files, which have
been modified from their original versions in <https://github.com/raspberrypi/pico-examples/>
(the "Pico examples repository")
- bsp/rp2040/m_default/swo_manchester_encoding.pio
- bsp/rp2040/m_default/swo_uart_rx.pio
The below notice does not apply to any modifications made to the above files
since the versions present in the Pico examples repository, nor to any files
not present in the Pico exmaples repository.
Copyright 2020 (c) 2020 Raspberry Pi (Trading) Ltd.
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the
following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following
disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following
disclaimer in the documentation and/or other materials provided with the distribution.
3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products
derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

View File

@ -71,15 +71,17 @@ libco is licensed under the [ISC license](https://opensource.org/licenses/ISC).
Some code has been incorporated from the [DapperMime](https://github.com/majbthrd/DapperMime), Some code has been incorporated from the [DapperMime](https://github.com/majbthrd/DapperMime),
[picoprobe-sump](https://github.com/perexg/picoprobe-sump), [picoprobe-sump](https://github.com/perexg/picoprobe-sump),
[JTAGenum](https://github.com/cyphunk/JTAGenum) and [JTAGenum](https://github.com/cyphunk/JTAGenum),
[SWDscan](https://github.com/szymonh/SWDscan) [SWDscan](https://github.com/szymonh/SWDscan) and the [Raspberry Pico
examples](https://github.com/raspberrypi/pico-examples/)
projects. These respective licenses can be found in projects. These respective licenses can be found in
[this](./LICENSE.dappermime), [this](./LICENSE.picoprobe-sump), [this](./LICENSE.dappermime), [this](./LICENSE.picoprobe-sump),
[this](./LICENSE.jtagenum) and [this](./LICENSE.swdscan) file. [this](./LICENSE.jtagenum), [this](./LICENSE.swdscan) and
[this](./LICENSE.pico-examples) file.
## TODO ## TODO
- [ ] **A (VID and) PID, and maybe better subclass & protocol IDs for the vnd cfg itf** - [ ] **A (VID and) PID**
- [ ] GPL license headers on every file - [ ] GPL license headers on every file
- [x] Debug interface to send printf stuff directly to USB, instead of having - [x] Debug interface to send printf stuff directly to USB, instead of having
to use the UART interface as a loopback thing. to use the UART interface as a loopback thing.
@ -87,7 +89,7 @@ projects. These respective licenses can be found in
- [x] I2C support ~~by emulating the I2C Tiny USB~~ - [x] I2C support ~~by emulating the I2C Tiny USB~~
- [x] Expose RP2040-internal temperature ADC on I2C-over-USB bus? - [x] Expose RP2040-internal temperature ADC on I2C-over-USB bus?
- [ ] ~~Does SMBus stuff need special treatment here?~~ ~~No.~~ Actually, some - [ ] ~~Does SMBus stuff need special treatment here?~~ ~~No.~~ Actually, some
parts do, but, laziness. parts do, but, laziness.
- [x] 10-bit I2C address support (Needs poking at the Pico SDK, as it only - [x] 10-bit I2C address support (Needs poking at the Pico SDK, as it only
supports 7-bit ones). supports 7-bit ones).
- [ ] **1-wire** - [ ] **1-wire**
@ -98,7 +100,6 @@ projects. These respective licenses can be found in
- interface 1 ("B"): index 2, epin 0x04, epout 0x83 - interface 1 ("B"): index 2, epin 0x04, epout 0x83
- interface 2 ("C"): index 3, epin 0x06, epout 0x85 - interface 2 ("C"): index 3, epin 0x06, epout 0x85
- interface 3 ("D"): index 4, epin 0x08, epout 0x87 - interface 3 ("D"): index 4, epin 0x08, epout 0x87
- ~~or, FX2 emulation mode??? (useful links: https://sigrok.org/wiki/Fx2lafw ; https://sigrok.org/wiki/CWAV_USBee_SX/Info )~~ has a ROM/fw and everything, so, maybe not
- [ ] "Complex Trigger" mode for aiding with glitching triggers, by turning - [ ] "Complex Trigger" mode for aiding with glitching triggers, by turning
UART/SPI/I2C/eMMC/... sequences into a GPIO toggle UART/SPI/I2C/eMMC/... sequences into a GPIO toggle
- [ ] Mode where you can define custom PIO stuff for custom pinouts/protocols?????? - [ ] Mode where you can define custom PIO stuff for custom pinouts/protocols??????

View File

@ -440,13 +440,15 @@ __STATIC_FORCEINLINE void PIN_SWDIO_OUT(uint32_t bit) {
Configure the SWDIO DAP hardware I/O pin to output mode. This function is Configure the SWDIO DAP hardware I/O pin to output mode. This function is
called prior \ref PIN_SWDIO_OUT function calls. called prior \ref PIN_SWDIO_OUT function calls.
*/ */
__STATIC_FORCEINLINE void PIN_SWDIO_OUT_ENABLE(void) { sio_hw->gpio_oe_set = PINOUT_SWDIO_MASK; } //__STATIC_FORCEINLINE void PIN_SWDIO_OUT_ENABLE(void) { sio_hw->gpio_oe_set = PINOUT_SWDIO_MASK; }
void PIN_SWDIO_OUT_ENABLE(void);
/** SWDIO I/O pin: Switch to Input mode (used in SWD mode only). /** 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 Configure the SWDIO DAP hardware I/O pin to input mode. This function is
called prior \ref PIN_SWDIO_IN function calls. called prior \ref PIN_SWDIO_IN function calls.
*/ */
__STATIC_FORCEINLINE void PIN_SWDIO_OUT_DISABLE(void) { sio_hw->gpio_oe_clr = PINOUT_SWDIO_MASK; } //__STATIC_FORCEINLINE void PIN_SWDIO_OUT_DISABLE(void) { sio_hw->gpio_oe_clr = PINOUT_SWDIO_MASK; }
void PIN_SWDIO_OUT_DISABLE(void);
// TDI Pin I/O --------------------------------------------- // TDI Pin I/O ---------------------------------------------
@ -636,5 +638,7 @@ __STATIC_INLINE uint8_t RESET_TARGET(void) {
///@} ///@}
extern int swdsm, swdoffset, jtagsm, jtagoffset;
#endif /* __DAP_CONFIG_H__ */ #endif /* __DAP_CONFIG_H__ */

View File

@ -7,10 +7,8 @@
#include "DAP_config.h" #include "DAP_config.h"
#include <DAP.h> #include <DAP.h>
#include "tusb_config.h"
#include <tusb.h>
#include "dap_jtag.pio.h" #include "dap_jtag.pio.h"
#include "dap_swd.pio.h"
#define JTAG_PIO #define JTAG_PIO
@ -103,7 +101,7 @@ void JTAG_Sequence(uint32_t info, const uint8_t* tdi, uint8_t* tdo) {
} else printf("%s", "\nno tdo\n"); } else printf("%s", "\nno tdo\n");
} }
#else #else
static int jtagsm = -1, offset = -1; int jtagsm = -1, jtagoffset = -1;
void PORT_JTAG_SETUP(void) { void PORT_JTAG_SETUP(void) {
resets_hw->reset &= ~(RESETS_RESET_IO_BANK0_BITS | RESETS_RESET_PADS_BANK0_BITS); resets_hw->reset &= ~(RESETS_RESET_IO_BANK0_BITS | RESETS_RESET_PADS_BANK0_BITS);
@ -145,17 +143,28 @@ void PORT_JTAG_SETUP(void) {
iobank0_hw->io[PINOUT_JTAG_nTRST].ctrl = GPIO_FUNC_SIO << IO_BANK0_GPIO0_CTRL_FUNCSEL_LSB; iobank0_hw->io[PINOUT_JTAG_nTRST].ctrl = GPIO_FUNC_SIO << IO_BANK0_GPIO0_CTRL_FUNCSEL_LSB;
iobank0_hw->io[PINOUT_JTAG_nRESET].ctrl = GPIO_FUNC_SIO << IO_BANK0_GPIO0_CTRL_FUNCSEL_LSB; iobank0_hw->io[PINOUT_JTAG_nRESET].ctrl = GPIO_FUNC_SIO << IO_BANK0_GPIO0_CTRL_FUNCSEL_LSB;
jtagsm = pio_claim_unused_sm(PINOUT_JTAG_PIO_DEV, true); if (jtagsm == -1) jtagsm = pio_claim_unused_sm(PINOUT_JTAG_PIO_DEV, true);
offset = pio_add_program(PINOUT_JTAG_PIO_DEV, &dap_jtag_program); if (jtagoffset == -1)
dap_jtag_program_init(PINOUT_JTAG_PIO_DEV, jtagsm, offset, jtagoffset = pio_add_program(PINOUT_JTAG_PIO_DEV, &dap_jtag_program);
dap_jtag_program_init(PINOUT_JTAG_PIO_DEV, jtagsm, jtagoffset,
50*1000, PINOUT_JTAG_TCK, PINOUT_JTAG_TDI, PINOUT_JTAG_TDO); 50*1000, PINOUT_JTAG_TCK, PINOUT_JTAG_TDI, PINOUT_JTAG_TDO);
} }
void PORT_OFF(void) { void PORT_OFF(void) {
pio_sm_set_enabled(PINOUT_JTAG_PIO_DEV, jtagsm, false); if (jtagsm) {
pio_sm_unclaim(PINOUT_JTAG_PIO_DEV, jtagsm); pio_sm_set_enabled(PINOUT_JTAG_PIO_DEV, jtagsm, false);
pio_remove_program(PINOUT_JTAG_PIO_DEV, &dap_jtag_program, offset); pio_sm_unclaim(PINOUT_JTAG_PIO_DEV, jtagsm);
offset = jtagsm = -1; }
if (jtagoffset)
pio_remove_program(PINOUT_JTAG_PIO_DEV, &dap_jtag_program, jtagoffset);
jtagoffset = jtagsm = -1;
if (swdsm) {
pio_sm_set_enabled(PINOUT_JTAG_PIO_DEV, swdsm, false);
pio_sm_unclaim(PINOUT_JTAG_PIO_DEV, swdsm);
}
if (swdoffset)
pio_remove_program(PINOUT_JTAG_PIO_DEV, &dap_swd_program, swdoffset);
sio_hw->gpio_oe_clr = PINOUT_SWCLK_MASK | PINOUT_SWDIO_MASK | sio_hw->gpio_oe_clr = PINOUT_SWCLK_MASK | PINOUT_SWDIO_MASK |
PINOUT_TDI_MASK //| PINOUT_TDO_MASK PINOUT_TDI_MASK //| PINOUT_TDO_MASK
@ -201,10 +210,9 @@ void JTAG_Sequence(uint32_t info, const uint8_t* tdi, uint8_t* tdo) {
} }
printf("%c", '\n');*/ printf("%c", '\n');*/
PINOUT_JTAG_PIO_DEV->txf[jtagsm] = (uint8_t)(n - 1); pio_sm_put_blocking(PINOUT_JTAG_PIO_DEV, jtagsm, (uint8_t)(n - 1));
size_t oi = 0, ii = 0; for (size_t oi = 0, ii = 0; txremain || rxremain; tight_loop_contents()) {
while (txremain || rxremain) {
if (txremain && !pio_sm_is_tx_fifo_full(PINOUT_JTAG_PIO_DEV, jtagsm)) { if (txremain && !pio_sm_is_tx_fifo_full(PINOUT_JTAG_PIO_DEV, jtagsm)) {
*tx = bitswap(tdi[ii]); *tx = bitswap(tdi[ii]);
--txremain; --txremain;
@ -243,22 +251,28 @@ static void jtag_seq(uint32_t num, int tms, const void* tdi, void* tdo) {
static uint64_t last_bit = ~(uint64_t)0; static uint64_t last_bit = ~(uint64_t)0;
uint64_t devnull = 0; uint64_t devnull = 0;
bool notdi, notdo; bool notdi = tdi == NULL, notdo = tdo == NULL;
if ((notdi = (tdi == NULL))) tdi = &last_bit; if (!notdo) tms |= JTAG_SEQUENCE_TDO;
if ((notdo = (tdo == NULL))) tdo = &devnull;
else tms |= JTAG_SEQUENCE_TDO; const void* jdi;
void* jdo;
uint32_t nreal; uint32_t nreal;
for (uint32_t i = 0; i < num; i += nreal) { for (uint32_t i = 0; i < num; i += nreal) {
uint32_t nmod = (num - i) & 63; uint32_t nmod = (num - i) & 63;
nreal = nmod ? nmod : 64; nreal = nmod ? nmod : 64;
JTAG_Sequence(nmod | tms, (const uint8_t*)tdi + (i >> 3), (uint8_t*)tdo + (i >> 3)); jdi = notdi ? (const void*)&last_bit : ((const uint8_t*)tdi + (i >> 3));
jdo = notdo ? ( void*)&devnull : (( uint8_t*)tdo + (i >> 3));
JTAG_Sequence(nmod | tms, jdi, jdo);
} }
uint8_t lastbyte = *((const uint8_t*)tdi + (num >> 3)); if (tdi) {
last_bit = (lastbyte & (1 << (num & 7))) ? ~(uint64_t)0 : (uint64_t)0; uint8_t lastbyte = *((const uint8_t*)tdi + (((num + 7) >> 3)) - 1);
last_bit = (lastbyte & (1 << (num & 7))) ? ~(uint64_t)0 : (uint64_t)0;
}
} }
uint32_t JTAG_ReadIDCode(void) { uint32_t JTAG_ReadIDCode(void) {

View File

@ -1,5 +1,8 @@
; vim: set et: ; vim: set et:
; code mostly from https://github.com/phdussud/pico-dirtyJtag/blob/master/jtag.pio
; which doesn't have a license...
.program dap_jtag .program dap_jtag
.side_set 1 opt .side_set 1 opt

View File

@ -1,9 +1,16 @@
// vim: set et: // vim: set et:
#include <hardware/clocks.h>
#include <hardware/gpio.h>
#include <hardware/pio.h>
#include <hardware/timer.h>
#include "DAP_config.h" #include "DAP_config.h"
#include <DAP.h> #include <DAP.h>
/*#define SWD_PIO*/ #include "dap_swd.pio.h"
#define SWD_PIO
#ifndef SWD_PIO #ifndef SWD_PIO
void PORT_SWD_SETUP(void) { void PORT_SWD_SETUP(void) {
@ -20,16 +27,177 @@ void PORT_SWD_SETUP(void) {
iobank0_hw->io[PINOUT_SWCLK].ctrl = GPIO_FUNC_SIO << IO_BANK0_GPIO0_CTRL_FUNCSEL_LSB; iobank0_hw->io[PINOUT_SWCLK].ctrl = GPIO_FUNC_SIO << IO_BANK0_GPIO0_CTRL_FUNCSEL_LSB;
iobank0_hw->io[PINOUT_SWDIO].ctrl = GPIO_FUNC_SIO << IO_BANK0_GPIO0_CTRL_FUNCSEL_LSB; iobank0_hw->io[PINOUT_SWDIO].ctrl = GPIO_FUNC_SIO << IO_BANK0_GPIO0_CTRL_FUNCSEL_LSB;
} }
#else
void PORT_SWD_SETUP(void) { void PIN_SWDIO_OUT_ENABLE(void) {
// TODO... sio_hw->gpio_oe_set = PINOUT_SWDIO_MASK;
}
void PIN_SWDIO_OUT_DISABLE(void) {
sio_hw->gpio_oe_clr = PINOUT_SWDIO_MASK;
}
inline static void PIN_SWDIO_SET_PIO(void) { PIN_SWDIO_TMS_SET(); }
#else
//#error "no"
int swdsm = -1, swdoffset = -1;
void PORT_SWD_SETUP(void) {
resets_hw->reset &= ~(RESETS_RESET_IO_BANK0_BITS | RESETS_RESET_PADS_BANK0_BITS);
/* set to default high level */
sio_hw->gpio_oe_set = PINOUT_SWCLK_MASK | PINOUT_SWDIO_MASK;
sio_hw->gpio_set = PINOUT_SWCLK_MASK | PINOUT_SWDIO_MASK;
hw_write_masked(&padsbank0_hw->io[PINOUT_SWCLK], PADS_BANK0_GPIO0_IE_BITS,
PADS_BANK0_GPIO0_IE_BITS | PADS_BANK0_GPIO0_OD_BITS);
hw_write_masked(&padsbank0_hw->io[PINOUT_SWDIO], PADS_BANK0_GPIO0_IE_BITS,
PADS_BANK0_GPIO0_IE_BITS | PADS_BANK0_GPIO0_OD_BITS);
iobank0_hw->io[PINOUT_SWCLK].ctrl = GPIO_FUNC_SIO << IO_BANK0_GPIO0_CTRL_FUNCSEL_LSB;
iobank0_hw->io[PINOUT_SWDIO].ctrl = GPIO_FUNC_SIO << IO_BANK0_GPIO0_CTRL_FUNCSEL_LSB;
if (swdsm == -1) swdsm = pio_claim_unused_sm(PINOUT_JTAG_PIO_DEV, true);
if (swdoffset == -1)
swdoffset = pio_add_program(PINOUT_JTAG_PIO_DEV, &dap_swd_program);
dap_swd_program_init(PINOUT_JTAG_PIO_DEV, swdsm, swdoffset,
50*1000, PINOUT_SWCLK, PINOUT_SWDIO);
} }
// TODO: also hijack PIN_SWDIO_OUT_{DIS,EN}ABLE ! (used in DAP_SWD_Sequence)
// TODO: also hijack DAP_SWJ_PINS(?: should data pins be controlled like that? only rst stuff tbh) // TODO: also hijack DAP_SWJ_PINS(?: should data pins be controlled like that? only rst stuff tbh)
void PIN_SWDIO_OUT_ENABLE(void) {
pio_sm_set_pindirs_with_mask(PINOUT_JTAG_PIO_DEV, swdsm,
(1u << PINOUT_SWDIO), (1u << PINOUT_SWDIO));
}
void PIN_SWDIO_OUT_DISABLE(void) {
pio_sm_set_pindirs_with_mask(PINOUT_JTAG_PIO_DEV, swdsm,
(0u << PINOUT_SWDIO), (1u << PINOUT_SWDIO));
}
inline static void PIN_SWDIO_SET_PIO(void) {
pio_sm_set_pins_with_mask(PINOUT_JTAG_PIO_DEV, swdsm,
(1u << PINOUT_SWDIO), (1u << PINOUT_SWDIO));
}
static uint8_t bitswap(uint8_t in) {
static const uint8_t lut[16] = {
0x0, 0x8, 0x4, 0xc, 0x2, 0xa, 0x6, 0xe,
0x1, 0x9, 0x5, 0xd, 0x3, 0xb, 0x7, 0xf
};
return (lut[in&0xf] << 4) | lut[in>>4];
}
void SWD_Sequence(uint32_t info, const uint8_t* swdo, uint8_t* swdi) {
pio_sm_set_enabled(PINOUT_JTAG_PIO_DEV, swdsm, true);
//busy_wait_us_32(0);
float div = (float)clock_get_hz(clk_sys) / (2 * DAP_Data.clock_freq);
if (div < 2) div = 2;
else if (div > 65536) div = 65536;
pio_sm_set_clkdiv(PINOUT_JTAG_PIO_DEV, swdsm, div);
uint32_t n = info & SWD_SEQUENCE_CLK;
if (n == 0) n = 64;
io_wo_8* tx = (io_wo_8*)&PINOUT_JTAG_PIO_DEV->txf[swdsm];
io_ro_8* rx = (io_ro_8*)&PINOUT_JTAG_PIO_DEV->rxf[swdsm];
uint32_t bytelen = (n + 7) >> 3;
uint32_t last_shift = (8 - n) & 7;
uint32_t txremain = bytelen,
rxremain = last_shift ? bytelen : (bytelen + 1);
/*printf("seq start n=%lu bytelen=%lu lsh=%lu txr=%lu rxr=%lu\n",
n, bytelen, last_shift, txremain, rxremain);*/
pio_sm_put_blocking(PINOUT_JTAG_PIO_DEV, swdsm,
(uint8_t)(n - 1) | ((info & SWD_SEQUENCE_DIN) ? 0x80000000u : 0));
if (info & SWD_SEQUENCE_DIN) {
for (size_t i = 0; rxremain; tight_loop_contents()) {
if (!pio_sm_is_rx_fifo_empty(PINOUT_JTAG_PIO_DEV, swdsm)) {
uint8_t v = *rx;
--rxremain;
//printf("seq din rx %02x rxr=%lu\n", bitswap(v), rxremain);
if (i < bytelen) {
if (last_shift && i == bytelen - 1) {
swdi[i] = bitswap(v) >> last_shift;
} else {
swdi[i] = bitswap(v);
}
++i;
}
}
}
} else {
for (size_t i = 0; txremain; tight_loop_contents()) {
if (!pio_sm_is_tx_fifo_full(PINOUT_JTAG_PIO_DEV, swdsm)) {
*tx = bitswap(swdo[i]);
--txremain;
//printf("seq dout tx %02x txr=%lu\n", swdo[i], txremain);
++i;
}
}
// wait until FIFO empty, so that all bytes have been xmitted
//while (!pio_sm_is_tx_fifo_empty(PINOUT_JTAG_PIO_DEV, swdsm)) tight_loop_contents();
// ^ isn't enough, because of the side-set, so we need the loop below
// wait until last bit xmitted, and back at the starting insn
while (pio_sm_get_pc(PINOUT_JTAG_PIO_DEV, swdsm) != swdoffset) tight_loop_contents();
}
// we need to disable (and reenable at the start of this routine) the SM
// because we also use it to set the SWDIO pin and direction elsewhere,
// which shouldn't happen when the SM is enabled
//busy_wait_us_32(0);
pio_sm_set_enabled(PINOUT_JTAG_PIO_DEV, swdsm, false);
//printf("seq done, disabled\n");
}
#endif
void swd_seq(uint32_t count, uint32_t flags, const uint8_t* swdo, uint8_t* swdi) {
static uint64_t last_bit = ~(uint64_t)0;
uint64_t devnull = 0;
if ( (flags & SWD_SEQUENCE_DIN) && !swdi) swdi = (uint8_t*)&devnull;
if (!(flags & SWD_SEQUENCE_DIN) && !swdo) swdo = (const uint8_t*)&last_bit;
if (flags & SWD_SEQUENCE_DIN) {
PIN_SWDIO_OUT_DISABLE();
} else {
PIN_SWDIO_OUT_ENABLE();
}
const uint8_t* sdo = NULL;
uint8_t* sdi = NULL;
uint32_t nreal;
for (uint32_t i = 0; i < count; i += nreal) {
uint32_t nmod = (count - i) & 63;
nreal = nmod ? nmod : 64;
if (flags & SWD_SEQUENCE_DIN) {
sdi = swdi ? &swdi[i >> 3] : ( uint8_t*)&devnull ;
sdo = NULL;
} else {
sdo = swdo ? &swdo[i >> 3] : (const uint8_t*)&last_bit;
sdi = NULL;
}
SWD_Sequence(nmod | flags, sdo, sdi);
}
if (!(flags & SWD_SEQUENCE_DIN) && swdo) {
uint8_t lastbyte = swdo[((count + 7) >> 3) - 1];
last_bit = (lastbyte & (1 << (count & 7))) ? ~(uint64_t)0 : (uint64_t)0;
}
}
void SWJ_Sequence(uint32_t count, const uint8_t* data) { void SWJ_Sequence(uint32_t count, const uint8_t* data) {
for (uint32_t i = 0, k = 0; i < count; ++i) { swd_seq(count, 0, data, NULL);
/*for (uint32_t i = 0, k = 0; i < count; ++i) {
if ((i & 7) == 0) { if ((i & 7) == 0) {
val = data[k]; val = data[k];
++k; ++k;
@ -38,187 +206,90 @@ void SWJ_Sequence(uint32_t count, const uint8_t* data) {
swdio = (val >> (i & 7)) & 1; swdio = (val >> (i & 7)) & 1;
// SET SWDIO // SET SWDIO
// SWCLK LO; DELAY; SWCLK HI; DELAY // SWCLK LO; DELAY; SWCLK HI; DELAY
} }*/
} }
void SWD_Sequence(uint32_t info, const uint8_t* swdo, uint8_t* swdi) { uint8_t SWD_Transfer(uint32_t request, uint32_t* data) {
uint32_t n = info & SWD_SEQUENCE_CLK; uint32_t parity;
if (n == 0) n = 64; uint8_t swdo;
if (info & SWD_SEQUENCE_DIN) { parity = ((request >> 0) & 1) + ((request >> 1) & 1)
for (uint32_t i = 0; i < n; ) { + ((request >> 2) & 1) + ((request >> 3) & 1);
uint32_t v = 0; swdo = 1 | ((request & 0xf) << 1) | ((parity & 1) << 5) | (0<<6) | (1<<7);
for (uint32_t k = 0; k < 8; ++k, ++i) { swd_seq(8, 0, &swdo, NULL);
// SWCLK LO; DELAY
// GET SWDIO; SWCLK HI; DELAY
val |= swdio << k;
}
swdi[i >> 3] = v;
}
} else {
for (uint32_t i = 0; i < n; ) {
uint32_t val = swdo[i >> 3];
for (uint32_t k = 0; k < 8; ++i, ++k) {
swdio = (val >> k) & 1;
// SET SWDIO
// SWCLK LO; DELAY; SWCLK HI; DELAY
}
}
}
}
void SWD_Transfer(uint32_t request, uint32_t* data) { swd_seq(DAP_Data.swd_conf.turnaround, SWD_SEQUENCE_DIN, NULL, NULL);
// TODO: to SWD_Sequence stuff(?)
uint32_t parity = 0; uint8_t ack = 0;
swd_seq(3, SWD_SEQUENCE_DIN, NULL, &ack);
swdio = 1;
parity += swdio;
// SET SWDIO
// SWCLK LO; DELAY; SWCLK HI; DELAY
swdio = (request >> 0) & 1;
parity += swdio;
// SET SWDIO
// SWCLK LO; DELAY; SWCLK HI; DELAY
swdio = (request >> 1) & 1;
parity += swdio;
// SET SWDIO
// SWCLK LO; DELAY; SWCLK HI; DELAY
swdio = (request >> 2) & 1;
parity += swdio;
// SET SWDIO
// SWCLK LO; DELAY; SWCLK HI; DELAY
swdio = (request >> 3) & 1;
parity += swdio;
// SET SWDIO
// SWCLK LO; DELAY; SWCLK HI; DELAY
swdio = parity & 1;
// SET SWDIO
// SWCLK LO; DELAY; SWCLK HI; DELAY
swdio = 0;
// SET SWDIO
// SWCLK LO; DELAY; SWCLK HI; DELAY
swdio = 1;
// SET SWDIO
// SWCLK LO; DELAY; SWCLK HI; DELAY
// TODO: SWDIO is now input
for (size_t i = 0; i < DAP_Data.swd_conf.turnaround; ++i) {
// SWCLK LO; DELAY; SWCLK HI; DELAY
}
uint32_t ack = 0;
// SWCLK LO; DELAY
// GET SWDIO; SWCLK HI; DELAY
ack |= swdio << 0;
// SWCLK LO; DELAY
// GET SWDIO; SWCLK HI; DELAY
ack |= swdio << 1;
// SWCLK LO; DELAY
// GET SWDIO; SWCLK HI; DELAY
ack |= swdio << 2;
uint32_t num;
switch (ack) { switch (ack) {
case DAP_TRANSFER_OK: case DAP_TRANSFER_OK:
if (request & DAP_TRANSFER_RnW) { // read if (request & DAP_TRANSFER_RnW) {
uint32_t val = 0; uint64_t val = 0;
parity = 0; parity = 0;
// FIXME: this is little-endian-only!
swd_seq(32, SWD_SEQUENCE_DIN, NULL, (uint8_t*)&val);
for (size_t i = 0; i < 32; ++i) { for (size_t i = 0; i < 32; ++i) parity += ((uint32_t)val >> i) & 1;
// SWCLK LO; DELAY if ((parity & 1) != ((val >> 33) & 1)) {
// GET SWDIO; SWCLK HI; DELAY
parity += swdio;
val |= swdio << i;
}
// SWCLK LO; DELAY
// GET SWDIO; SWCLK HI; DELAY
if ((parity & 1) != (swdio & 1)) {
ack = DAP_TRANSFER_ERROR; ack = DAP_TRANSFER_ERROR;
} }
if (data) *data = val; if (data) *data = (uint32_t)val;
for (size_t i = 0; i < DAP_Data.swd_conf.turnaround; ++i) { swd_seq(DAP_Data.swd_conf.turnaround, SWD_SEQUENCE_DIN, NULL, NULL);
// SWCLK LO; DELAY; SWCLK HI; DELAY
}
// TODO: swdio is now output! //PIN_SWDIO_OUT_ENABLE();
} else { // write } else { // write
for (size_t i = 0; i < DAP_Data.swd_conf.turnaround; ++i) { swd_seq(DAP_Data.swd_conf.turnaround, SWD_SEQUENCE_DIN, NULL, NULL);
// SWCLK LO; DELAY; SWCLK HI; DELAY
}
// TODO: SWDIO is now output! //PIN_SWDIO_OUT_ENABLE();
uint32_t val = *data; uint32_t val = *data;
parity = 0;
for (size_t i = 0; i < 32; ++i) { parity = 0;
swdio = (val >> i) & 1; for (size_t i = 0; i < 32; ++i) parity += (val >> i) & 1;
parity += swdio;
// SET SWDIO uint64_t out = val | ((uint64_t)(parity & 1) << 33);
// SWCLK LO; DELAY; SWCLK HI; DELAY // FIXME: this is little-endian-only!
} swd_seq(33, 0, (const uint8_t*)&out, NULL);
swdio = parity;
// SET SWDIO
// SWCLK LO; DELAY; SWCLK HI; DELAY
} }
if (request & DAP_TRANSFER_TIMESTAMP) DAP_Data.timestamp = TIMESTAMP_GET(); if (request & DAP_TRANSFER_TIMESTAMP) DAP_Data.timestamp = TIMESTAMP_GET();
uint32_t n = DAP_Data.transfer.idle_cycles; num = DAP_Data.transfer.idle_cycles;
if (n) { for (uint32_t i = 0; i < num; num += 64) {
swdio = 0; uint64_t swdio = 0;
// SET SWDIO
for (size_t i = 0; i < n; ++i) { uint32_t cyc = num - i;
// SWCLK LO; DELAY; SWCLK HI; DELAY if (cyc > 64) cyc = 64;
}
SWD_Sequence((cyc & SWD_SEQUENCE_CLK), (const uint8_t*)&swdio, NULL);
} }
break;
swdio = 1;
// SET SWDIO (no clk!)
return (uint8_t)ack;
case DAP_TRANSFER_WAIT: case DAP_TRANSFER_FAULT: case DAP_TRANSFER_WAIT: case DAP_TRANSFER_FAULT:
if (DAP_Data.swd_conf.data_phase && ((request & DAP_TRANSFER_RnW) != 0)) { num = DAP_Data.swd_conf.turnaround;
for (size_t i = 0; i < 33; ++i) { // 32data + parity if (DAP_Data.swd_conf.data_phase && (request & DAP_TRANSFER_RnW)) {
// SWCLK LO; DELAY; SWCLK HI; DELAY num += 33; // 32 bits + parity
}
} }
for (size_t i = 0; i < DAP_Data.swd_conf.turnaround; ++i) { swd_seq(num, SWD_SEQUENCE_DIN, NULL, NULL);
// SWCLK LO; DELAY; SWCLK HI; DELAY
if (DAP_Data.swd_conf.data_phase && !(request & DAP_TRANSFER_RnW)) {
uint64_t swdio = 0;
swd_seq(33, 0, (const uint8_t*)&swdio, NULL); // 32 data bits + parity
} }
break;
// TODO: SWDIO to output!
if (DAP_Data.swd_conf.data_phase && ((request & DAP_TRANSFER_RnW) != 0)) {
swdio = 0;
// SET SWDIO
for (size_t i = 0; i < 33; ++i) { // 32data + parity
// SWCLK LO; DELAY; SWCLK HI; DELAY
}
}
swdio = 1;
// SET SWDIO (no clk!)
return (uint8_t)ack;
default: // protocol error default: // protocol error
for (uint32_t i = 0; i < DAP_Data.swd_conf.turnaround + 33; ++i) { swd_seq(DAP_Data.swd_conf.turnaround + 33, SWD_SEQUENCE_DIN, NULL, NULL);
// SWCLK LO; DELAY; SWCLK HI; DELAY break;
}
// TODO: SWDIO to output!
swdio = 1;
// SET SWDIO (no clk!)
return (uint8_t)ack;
} }
}
#endif PIN_SWDIO_OUT_ENABLE();
// TODO: set SWDIO hi, no clk!
PIN_SWDIO_SET_PIO();
return ack;
}
//#endif

View File

@ -0,0 +1,66 @@
; vim: set et:
.program dap_swd
.side_set 1 opt
; Pin assignments:
; - SWCLK is side-set pin 0
; - SWDIO is OUT pin 0 and IN pin 0
;
; Autopush and autopull must be enabled, and the serial frame size is set by
; configuring the push/pull threshold (32 bits). Shift should be left
;
; data is captured on the leading edge of each SWCLK pulse, and
; transitions on the trailing edge, or some time before the first leading edge.
start_thing:
pull
out y, 1
out x, 31 side 0
jmp !y start_swdo side 0
start_swdi:
loop_swdi:
in pins, 1 side 1
jmp x-- loop_swdi side 0
end_swdi:
push side 0
jmp start_thing side 0
start_swdo:
loop_swdo:
out pins, 1 side 0
jmp x-- loop_swdo side 1
end_swdo:
jmp start_thing side 0
% c-sdk {
static inline void dap_swd_program_init(PIO pio, uint sm, uint offset,
uint16_t freq, uint pin_swclk, uint pin_swdio) {
pio_sm_config c = dap_swd_program_get_default_config(offset);
sm_config_set_out_pins(&c, pin_swdio, 1);
sm_config_set_in_pins(&c, pin_swdio);
sm_config_set_sideset_pins(&c, pin_swclk);
// (shift to left, autopush/pull, threshold=nbits)
sm_config_set_out_shift(&c, false, true, 8); // shift left feature is broken???
sm_config_set_in_shift(&c, false, true, 8);
sm_config_set_clkdiv(&c, (float)clock_get_hz(clk_sys) / (2 * freq));
// SWCLK is high, SWDIO is input (pull hi)
pio_sm_set_pins_with_mask(pio, sm,
(1u << pin_swclk),
(1u << pin_swclk) | (1u << pin_swdio));
pio_sm_set_pindirs_with_mask(pio, sm,
(1u << pin_swclk) | (0u << pin_swdio),
(1u << pin_swclk) | (1u << pin_swdio));
pio_gpio_init(pio, pin_swclk);
pio_gpio_init(pio, pin_swdio);
// swd is synchronous, so bypass input synchroniser to reduce input delay.
hw_set_bits(&pio->input_sync_bypass, 1u << pin_swdio);
gpio_set_pulls(pin_swclk, false, true); // SWDIO is pulled up
pio_sm_init(pio, sm, offset, &c);
pio_sm_set_enabled(pio, sm, true);
}
%}

View File

@ -64,6 +64,7 @@
* PIO: * PIO:
* PIO0: (max. 4) * PIO0: (max. 4)
* JTAG 1 * JTAG 1
* SWD 1
* PIO1: (max. 4) * PIO1: (max. 4)
* SWO 2 * SWO 2
* *