Update PIO program to pass read/write commands through FIFO alongside bit counts. Also, don't return RX data on write commands. These two changes allow the probe code to return early after pushing write commands into the FIFO, which improves throughput.
diff --git a/src/probe.c b/src/probe.c index 825320d..6f544f0 100644 --- a/src/probe.c +++ b/src/probe.c
@@ -75,21 +75,37 @@ #endif } +typedef enum probe_pio_command { + CMD_WRITE = 0, + CMD_SKIP, + CMD_READ +} probe_pio_command_t; + +static inline uint32_t fmt_probe_command(uint bit_count, bool out_en, probe_pio_command_t cmd) { + uint cmd_addr = + cmd == CMD_WRITE ? probe.offset + probe_offset_write_cmd : + cmd == CMD_SKIP ? probe.offset + probe_offset_get_next_cmd : + probe.offset + probe_offset_read_cmd; + return ((bit_count - 1) & 0xff) | ((uint)out_en << 8) | (cmd_addr << 9); +} + void probe_write_bits(uint bit_count, uint32_t data_byte) { DEBUG_PINS_SET(probe_timing, DBG_PIN_WRITE); - pio_sm_put_blocking(pio0, PROBE_SM, bit_count - 1); + pio_sm_put_blocking(pio0, PROBE_SM, fmt_probe_command(bit_count, true, CMD_WRITE)); pio_sm_put_blocking(pio0, PROBE_SM, data_byte); - DEBUG_PINS_SET(probe_timing, DBG_PIN_WRITE_WAIT); picoprobe_dump("Write %d bits 0x%x\n", bit_count, data_byte); - // Wait for pio to push garbage to rx fifo so we know it has finished sending - pio_sm_get_blocking(pio0, PROBE_SM); - DEBUG_PINS_CLR(probe_timing, DBG_PIN_WRITE_WAIT); + // Return immediately so we can cue up the next command whilst this one runs DEBUG_PINS_CLR(probe_timing, DBG_PIN_WRITE); } +void probe_hiz_clocks(uint bit_count) { + pio_sm_put_blocking(pio0, PROBE_SM, fmt_probe_command(bit_count, false, CMD_WRITE)); + pio_sm_put_blocking(pio0, PROBE_SM, 0); +} + uint32_t probe_read_bits(uint bit_count) { DEBUG_PINS_SET(probe_timing, DBG_PIN_READ); - pio_sm_put_blocking(pio0, PROBE_SM, bit_count - 1); + pio_sm_put_blocking(pio0, PROBE_SM, fmt_probe_command(bit_count, false, CMD_READ)); uint32_t data = pio_sm_get_blocking(pio0, PROBE_SM); uint32_t data_shifted = data; if (bit_count < 32) { @@ -101,14 +117,20 @@ return data_shifted; } +static void probe_wait_idle() { + pio0->fdebug = 1u << (PIO_FDEBUG_TXSTALL_LSB + PROBE_SM); + while (!(pio0->fdebug & (1u << (PIO_FDEBUG_TXSTALL_LSB + PROBE_SM)))) + ; +} + void probe_read_mode(void) { - pio_sm_exec(pio0, PROBE_SM, pio_encode_jmp(probe.offset + probe_offset_in_posedge)); - while(pio_sm_get_pc(pio0, PROBE_SM) != probe.offset + probe_offset_in_idle); + pio_sm_put_blocking(pio0, PROBE_SM, fmt_probe_command(0, false, CMD_SKIP)); + probe_wait_idle(); } void probe_write_mode(void) { - pio_sm_exec(pio0, PROBE_SM, pio_encode_jmp(probe.offset + probe_offset_out_negedge)); - while(pio_sm_get_pc(pio0, PROBE_SM) != probe.offset + probe_offset_out_idle); + pio_sm_put_blocking(pio0, PROBE_SM, fmt_probe_command(0, true, CMD_SKIP)); + probe_wait_idle(); } void probe_init() { @@ -123,13 +145,11 @@ // Set up divisor probe_set_swclk_freq(1000); - // Enable SM + // Jump SM to command dispatch routine, and enable it + pio_sm_exec(pio0, PROBE_SM, offset + probe_offset_get_next_cmd); pio_sm_set_enabled(pio0, PROBE_SM, 1); probe.initted = 1; } - - // Jump to write program - probe_write_mode(); } void probe_deinit(void)
diff --git a/src/probe.h b/src/probe.h index a76f5c9..6b57721 100644 --- a/src/probe.h +++ b/src/probe.h
@@ -35,8 +35,11 @@ #endif void probe_set_swclk_freq(uint freq_khz); + +// Bit counts in the range 1..256 void probe_write_bits(uint bit_count, uint32_t data_byte); uint32_t probe_read_bits(uint bit_count); +void probe_hiz_clocks(uint bit_count); void probe_read_mode(void); void probe_write_mode(void);
diff --git a/src/probe.pio b/src/probe.pio index 934d353..b176949 100644 --- a/src/probe.pio +++ b/src/probe.pio
@@ -1,7 +1,7 @@ /* * The MIT License (MIT) * - * Copyright (c) 2021 Raspberry Pi (Trading) Ltd. + * Copyright (c) 2021-2023 Raspberry Pi (Trading) Ltd. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -23,32 +23,46 @@ * */ +// Every TX FIFO entry is either a command, or up to 32 bits of data. +// Command format: +// +// | 13:9 | 8 | 7:0 | +// | Cmd | Dir | Count | +// +// Count is the number of bits to be transferred by this command, minus 1. +// Dir is the output enable for the SWDIO pin. +// Cmd is the address of the write_cmd, read_cmd or get_next_cmd label. +// +// write_cmd expects a FIFO data entry, but read_cmd does not. +// +// read_cmd pushes data to the FIFO, but write_cmd does not. (The lack of RX +// garbage on writes allows the interface code to return early after pushing a +// write command, as there is no need in general to poll for a command's +// completion as long as all commands are executed in order.) + .program probe .side_set 1 opt -public out_negedge: - set pindirs, 1 side 0x0 ; Init OE clock 0 -public out_idle: - pull ; Pull number of bits to shift -1 from tx fifo and put into output shift register - mov x, osr ; mov bits to shift -1 from output shift register into x - pull ; Pull data to shift out -out_negedge_bitloop: - out pins, 1 side 0x0 ; clock data out on falling edge - jmp x-- out_negedge_bitloop side 0x1 ; data is present for posedge - set pins, 1 side 0x0 ; Drive data high (idle bus state) - push ; Push to rx fifo just so processor knows when done - jmp out_negedge ; Wait for next transaction +public write_cmd: + pull +write_bitloop: + out pins, 1 side 0x0 ; Data is output by host on negedge + jmp x-- write_bitloop side 0x1 ; ...and captured by target on posedge + ; Fall through to next command +.wrap_target +public get_next_cmd: + pull side 0x0 ; SWCLK is initially low + out x, 8 ; Get bit count + out pindirs, 1 ; Set SWDIO direction + out pc, 5 ; Go to command routine -public in_posedge: - set pindirs, 0 side 0x0 ; INIT IE clock 0 -public in_idle: - pull ; Pull number of bits to shift -1 from tx fifo and put into output shift register - mov x, osr ; mov bits to shift -1 from output shift register into x into x -in_posedge_bitloop: - in pins, 1 side 0x1 ; Generate posedge and read data - jmp x-- in_posedge_bitloop side 0x0 ; - push ; Push to rx fifo when done - jmp in_posedge ; Jump back to start +public read_cmd: +read_bitloop: + in pins, 1 side 0x1 ; Data is captured by host on posedge + jmp x-- read_bitloop side 0x0 + push +.wrap ; Wrap to next command + ; Implement probe_gpio_init() and probe_sm_init() methods here - set pins, offsets, sidesets etc % c-sdk {
diff --git a/src/sw_dp_pio.c b/src/sw_dp_pio.c index 951b47f..75b277b 100755 --- a/src/sw_dp_pio.c +++ b/src/sw_dp_pio.c
@@ -133,8 +133,6 @@ probe_write_bits(8, prq); /* Turnaround (ignore read bits) */ - probe_read_mode(); - ack = probe_read_bits(DAP_Data.swd_conf.turnaround + 3); ack >>= DAP_Data.swd_conf.turnaround; @@ -154,12 +152,10 @@ picoprobe_debug("Read %02x ack %02x 0x%08x parity %01x\n", prq, ack, val, bit); /* Turnaround for line idle */ - probe_read_bits(DAP_Data.swd_conf.turnaround); - probe_write_mode(); + probe_hiz_clocks(DAP_Data.swd_conf.turnaround); } else { /* Turnaround for write */ - probe_read_bits(DAP_Data.swd_conf.turnaround); - probe_write_mode(); + probe_hiz_clocks(DAP_Data.swd_conf.turnaround); /* Write WDATA[0:31] */ val = *data; @@ -178,9 +174,9 @@ /* Idle cycles - drive 0 for N clocks */ if (DAP_Data.transfer.idle_cycles) { for (n = DAP_Data.transfer.idle_cycles; n; ) { - if (n > 32) { - probe_write_bits(32, 0); - n -= 32; + if (n > 256) { + probe_write_bits(256, 0); + n -= 256; } else { probe_write_bits(n, 0); n -= n; @@ -195,8 +191,7 @@ /* Dummy Read RDATA[0:31] + Parity */ probe_read_bits(33); } - probe_read_bits(DAP_Data.swd_conf.turnaround); - probe_write_mode(); + 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); @@ -209,7 +204,6 @@ n = DAP_Data.swd_conf.turnaround + 32U + 1U; /* Back off data phase */ probe_read_bits(n); - probe_write_mode(); return ((uint8_t)ack); }