Compare commits

..

21 Commits

Author SHA1 Message Date
Triss 334d45ee83 ugh 2021-09-25 14:00:30 +02:00
Triss 22e8f70e30 ftdi code finished-ish, but not working yet 2021-09-25 14:00:28 +02:00
Triss 0c10da762a headache 2021-09-25 14:00:01 +02:00
Triss 3b76c6fb4a ftdi uart stuff (WIP) 2021-09-25 14:00:01 +02:00
Triss 1e9cac1ace ftdi bsp files skeleton 2021-09-25 14:00:01 +02:00
Triss 210f74dbc5 mpsse, mcuhost usb protocol decoding 2021-09-25 14:00:01 +02:00
Triss c5a4a6b3e5 mpsse stuff 2021-09-25 14:00:01 +02:00
Triss ca11c8e1e1 ftdi protocol handling: everything except mpsse and mcuhost 2021-09-25 14:00:01 +02:00
Triss d2860c8e10 forgot this file 2021-09-25 14:00:01 +02:00
Triss 45cce4dfff initial ftdi stuff 2021-09-25 14:00:01 +02:00
Triss a1561ee35a PERSISTENT_STORAGE usage flag 2021-09-25 14:00:00 +02:00
Triss 493be92bd2 better dpctl output for persistent storage 2021-09-25 13:58:06 +02:00
Triss 383dcc4ea9 clear remaining 32 reserved bytes 2021-09-25 13:58:06 +02:00
Triss 07db20ecb6 get storage to work now 2021-09-25 13:58:06 +02:00
Triss 6a4eafb96e dpctl storage stuff (untested) 2021-09-25 13:58:06 +02:00
Triss 9d2a970161 storage impl for modes 2021-09-25 13:58:06 +02:00
Triss 1318e0f9c2 i think thats a bug 2021-09-25 13:58:06 +02:00
Triss 4015b7bf42 PERSISTENT_STORAGE usage flag 2021-09-25 13:58:05 +02:00
Triss befced7132 finish storage base 2021-09-25 13:57:14 +02:00
Triss 36b55483a8 fix compilation, more stuff 2021-09-25 13:57:14 +02:00
Triss 3c3795f8cb initial persistent storage code 2021-09-25 13:57:14 +02:00
64 changed files with 3413 additions and 2246 deletions

3
.gitignore vendored
View File

@ -1,5 +1,6 @@
cmake-build/
build*/
build/
build-flash/
ex/
compile_commands.json
_old/

View File

@ -1642,12 +1642,10 @@ uint32_t DAP_ProcessCommand(const uint8_t *request, uint8_t *response) {
*response++ = *request;
//printf("dap cmd=%02hhx\n", *request);
switch (*request++) {
case ID_DAP_Info:
num = DAP_Info(*request, response+1);
*response = (uint8_t)num;
//printf("cmd info\n");
return ((2U << 16) + 2U + num);
case ID_DAP_HostStatus:
@ -1754,11 +1752,9 @@ uint32_t DAP_ProcessCommand(const uint8_t *request, uint8_t *response) {
default:
*(response-1) = ID_DAP_Invalid;
//printf("cmd invalid\n");
return ((1U << 16) | 1U);
}
//printf("cmd end\n");
return ((1U << 16) + 1U + num);
}

View File

@ -1,4 +1,5 @@
option(USE_USBCDC_FOR_STDIO "Export an extra USB-CDC interface for stdio, instead of echoing it to a UART port (and requiring UART loopback for receiving stdio output on a host computer)." OFF)
option(PERSISTENT_STORAGE "Use persistent storage (usually on-chip/on-board flash) to save the current mode, and settings of modes" ON)
set(FAMILY "rp2040" CACHE STRING "Board/MCU family, decides which drivers to use. Set to RP2040 by default.")
set(BOARD "raspberry_pi_pico" CACHE STRING "Board used, determines the pinout. Defaults to the Raspberry Pi Pico.")
@ -21,18 +22,21 @@ if(FAMILY STREQUAL "rp2040")
option(PICO_NO_FLASH "Disable writing the compiled program to flash, and only load it to RAM. Useful for testing, but not much else (OFF by default)." OFF)
option(PICO_COPY_TO_RAM "Run all code in RAM, while the program is also stored on flash. On bootup, everything will be copied to RAM (OFF by default)." OFF)
include(pico_sdk_import.cmake)
set(TOP "${PICO_SDK_PATH}/lib/tinyusb")
get_filename_component(TOP "${TOP}" REALPATH)
if (USE_SYSTEMWIDE_PICOSDK)
set(TOP "$ENV{PICO_SDK_PATH}/lib/tinyusb")
get_filename_component(TOP "${TOP}" REALPATH)
include(cmake/pico_sdk_import.cmake)
else()
set(PICO_SDK_PATH ${TOP}/hw/mcu/raspberrypi/pico-sdk)
include(${PICO_SDK_PATH}/pico_sdk_init.cmake)
endif()
include(${TOP}/hw/bsp/${FAMILY}/family.cmake) # tinyusb stuff
include(${TOP}/hw/bsp/family_support.cmake) # tinyusb stuff
family_get_project_name(PROJECT ${CMAKE_CURRENT_LIST_DIR})
#set(PROJECT DragonProbe)
project(${PROJECT} C CXX ASM)
#family_initialize_project(PROJECT ${CMAKE_CURRENT_LIST_DIR}) # calls pico_sdk_init()
pico_sdk_init()
project(${PROJECT})
family_initialize_project(PROJECT ${CMAKE_CURRENT_LIST_DIR}) # calls pico_sdk_init()
#pico_sdk_init()
#pico_set_program_name(${PROJECT} "${PROJECT}")
#pico_set_program_version(${PROJECT} "0.1")
@ -42,16 +46,14 @@ if(FAMILY STREQUAL "rp2040")
# need uart stdio, usb is busy doing other stuff
if(USE_USBCDC_FOR_STDIO)
# we're going to manually implement this case
#pico_enable_stdio_uart(${PROJECT} 0)
target_compile_definitions(${PROJECT} PUBLIC USE_USBCDC_FOR_STDIO=1 PICO_STDIO_USB=1)
#pico_enable_stdio_uart(${PROJECT} 0)
target_compile_definitions(${PROJECT} PUBLIC USE_USBCDC_FOR_STDIO=1)
else()
#pico_enable_stdio_uart(${PROJECT} 1)
#set_target_properties(${PROJECT} PROPERTIES PICO_TARGET_STDIO_UART 1)
#pico_enable_stdio_uart(${PROJECT} 1)
endif()
# TODO: separate flag for disabling this one?
pico_enable_stdio_uart(${PROJECT} 1)
pico_enable_stdio_usb(${PROJECT} 0)
#set_target_properties(${PROJECT} PROPERTIES PICO_TARGET_STDIO_UART 1)
#set_target_properties(${PROJECT} PROPERTIES PICO_TARGET_STDIO_USB 0)
else()
message(FATAL_ERROR "Invalid FAMILY '${FAMILY}' specified")
endif()
@ -69,6 +71,8 @@ target_sources(${PROJECT} PUBLIC
${CMAKE_CURRENT_SOURCE_DIR}/src/main.c
${CMAKE_CURRENT_SOURCE_DIR}/src/alloc.c
${CMAKE_CURRENT_SOURCE_DIR}/src/modeset.c
${CMAKE_CURRENT_SOURCE_DIR}/src/storage.c
${CMAKE_CURRENT_SOURCE_DIR}/src/storage_save.c
${CMAKE_CURRENT_SOURCE_DIR}/src/thread.c
${CMAKE_CURRENT_SOURCE_DIR}/src/tusb_plt.S
${CMAKE_CURRENT_SOURCE_DIR}/src/usb_descriptors.c
@ -77,12 +81,12 @@ target_sources(${PROJECT} PUBLIC
${CMAKE_CURRENT_SOURCE_DIR}/src/m_default/cdc_serprog.c
${CMAKE_CURRENT_SOURCE_DIR}/src/m_default/tempsensor.c
${CMAKE_CURRENT_SOURCE_DIR}/src/m_default/vnd_i2ctinyusb.c
${CMAKE_CURRENT_SOURCE_DIR}/src/m_isp/_isp.c
${CMAKE_CURRENT_SOURCE_DIR}/src/m_isp/mehfet.c
${CMAKE_CURRENT_SOURCE_DIR}/src/m_jscan/_jscan.c
${CMAKE_CURRENT_SOURCE_DIR}/src/m_jscan/jscan.c
${CMAKE_CURRENT_SOURCE_DIR}/src/m_sump/_sump.c
${CMAKE_CURRENT_SOURCE_DIR}/src/m_sump/cdc_sump.c
${CMAKE_CURRENT_SOURCE_DIR}/src/m_ftdi/_ftdi.c
${CMAKE_CURRENT_SOURCE_DIR}/src/m_ftdi/ftdi.c
${CMAKE_CURRENT_SOURCE_DIR}/bsp/${FAMILY}/m_default/cdc_uart.c
${CMAKE_CURRENT_SOURCE_DIR}/bsp/${FAMILY}/m_default/dap_jtag.c
${CMAKE_CURRENT_SOURCE_DIR}/bsp/${FAMILY}/m_default/dap_swd.c
@ -91,16 +95,27 @@ target_sources(${PROJECT} PUBLIC
${CMAKE_CURRENT_SOURCE_DIR}/bsp/${FAMILY}/m_default/i2c_tinyusb.c
${CMAKE_CURRENT_SOURCE_DIR}/bsp/${FAMILY}/m_default/spi_serprog.c
${CMAKE_CURRENT_SOURCE_DIR}/bsp/${FAMILY}/m_default/tempsensor.c
${CMAKE_CURRENT_SOURCE_DIR}/bsp/${FAMILY}/m_isp/mehfet_hw.c
${CMAKE_CURRENT_SOURCE_DIR}/bsp/${FAMILY}/m_isp/sbw_hw.c
${CMAKE_CURRENT_SOURCE_DIR}/bsp/${FAMILY}/m_jscan/jscan_hw.c
${CMAKE_CURRENT_SOURCE_DIR}/bsp/${FAMILY}/m_sump/sump_hw.c
${CMAKE_CURRENT_SOURCE_DIR}/bsp/${FAMILY}/m_ftdi/ftdi_hw.c
${CMAKE_CURRENT_SOURCE_DIR}/bsp/${FAMILY}/m_ftdi/ftdi_proto.c
${CMAKE_CURRENT_SOURCE_DIR}/bsp/${FAMILY}/m_ftdi/uart.c
${CMAKE_CURRENT_SOURCE_DIR}/bsp/${FAMILY}/m_ftdi/mpsse.c
${CMAKE_CURRENT_SOURCE_DIR}/bsp/${FAMILY}/m_ftdi/asyncbb.c
${CMAKE_CURRENT_SOURCE_DIR}/bsp/${FAMILY}/m_ftdi/syncbb.c
${CMAKE_CURRENT_SOURCE_DIR}/bsp/${FAMILY}/m_ftdi/mcuhost.c
${CMAKE_CURRENT_SOURCE_DIR}/bsp/${FAMILY}/m_ftdi/fifo.c
${CMAKE_CURRENT_SOURCE_DIR}/bsp/${FAMILY}/m_ftdi/cpufifo.c
)
if(USE_USBCDC_FOR_STDIO)
target_compile_definitions(${PROJECT} PUBLIC USE_USBCDC_FOR_STDIO=1)
target_sources(${PROJECT} PUBLIC
${CMAKE_CURRENT_SOURCE_DIR}/bsp/${FAMILY}/cdc_stdio.c
)
endif()
if (PERSISTENT_STORAGE)
target_compile_definitions(${PROJECT} PUBLIC PERSISTENT_STORAGE=1)
endif()
target_include_directories(${PROJECT} PUBLIC
${CMAKE_CURRENT_SOURCE_DIR}/src/
${CMAKE_CURRENT_SOURCE_DIR}/libco/
@ -112,8 +127,9 @@ target_include_directories(${PROJECT} PUBLIC
${CMAKE_CURRENT_SOURCE_DIR}/bsp/default/
)
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -Wextra -Werror=implicit-function-declaration -Werror=return-type")
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -Wextra -Werror=implicit-function-declaration -Werror=return-type -Werror=maybe-uninitialized")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wextra")
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,--cref")
add_custom_target(fix_db ALL WORKING_DIRECTORY ${OUTPUT_DIR}
COMMAND "${CMAKE_CURRENT_SOURCE_DIR}/scripts/fix_clang_db.py")
@ -144,7 +160,9 @@ if(FAMILY STREQUAL "rp2040")
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_manchester_encoding.pio)
pico_generate_pio_header(${PROJECT} ${CMAKE_CURRENT_SOURCE_DIR}/bsp/${FAMILY}/m_isp/sbw.pio)
pico_generate_pio_header(${PROJECT} ${CMAKE_CURRENT_SOURCE_DIR}/bsp/${FAMILY}/m_ftdi/ftdi_uart_rx.pio)
pico_generate_pio_header(${PROJECT} ${CMAKE_CURRENT_SOURCE_DIR}/bsp/${FAMILY}/m_ftdi/ftdi_uart_tx.pio)
pico_add_extra_outputs(${PROJECT})

View File

@ -9,10 +9,6 @@
/*#define USB_VID 0x1d50*/ /* OpenMoko */
#define USB_PID 0x1312
/* openFPGAloader silliness */
/*#define USB_VID 0x0d28*/ /* NXP */
/*#define USB_PID 0x0204*/ /* ARM mbed */
// TODO: other RP2040 boards
#define INFO_BOARDNAME "RP2040 Pico"
@ -24,6 +20,11 @@
#else
#define CFG_TUD_CDC 2
#endif
#define CFG_TUD_VENDOR 2
#define CFG_TUD_VENDOR 3
/* don't access storage for RAM-only builds */
#if !PICO_NO_FLASH
#define DBOARD_HAS_STORAGE
#endif
#endif

66
bsp/rp2040/bsp-storage.h Normal file
View File

@ -0,0 +1,66 @@
// vim: set et:
#ifndef BSP_STORAGE_H_
#define BSP_STORAGE_H_
#include <stddef.h>
#include <stdint.h>
#include <stdbool.h>
#if !PICO_NO_FLASH
#include <string.h>
#include <hardware/regs/addressmap.h>
#include <hardware/regs/xip.h>
#include <hardware/structs/xip_ctrl.h>
#include <hardware/flash.h>
#include <hardware/sync.h>
#include <pico/bootrom.h>
#ifndef PICO_FLASH_SIZE_BYTES
#error "PICO_FLASH_SIZE_BYTES not defined"
#endif
static inline size_t storage_get_program_size(void) {
extern uint8_t __flash_binary_start, __flash_binary_end;
return (size_t)&__flash_binary_end - (size_t)&__flash_binary_start;
}
static inline size_t storage_get_program_offset(void) {
extern uint8_t __flash_binary_start;
return (size_t)&__flash_binary_start - XIP_BASE;
}
#define STORAGE_SIZE PICO_FLASH_SIZE_BYTES
#define STORAGE_ERASEWRITE_ALIGN FLASH_SECTOR_SIZE
// reads don't require any alignment
// ---
static inline void storage_read(void* dest, size_t offset, size_t size) {
// TODO: XIP/SSI DMA?
// * XIP DMA: used for loading stuff in the background while running code
// * SSI DMA: blocking & fast, code needs to run from RAM. a bit unwieldy
memcpy(dest, (uint8_t*)(XIP_BASE+offset), size);
}
static bool storage_erasewrite(size_t offset, const void* src, size_t size) {
// bad alignment => give an error in advance
if (offset & (FLASH_SECTOR_SIZE - 1)) return false;
if (size & (FLASH_SECTOR_SIZE - 1)) return false;
// memcmp pre: if no changes, don't flash
if (!memcmp(src, (uint8_t*)(XIP_BASE+offset), size)) return true;
flash_range_erase(offset, size);
flash_range_program(offset, src, size);
return !memcmp(src, (uint8_t*)(XIP_BASE+offset), size);
}
#endif
#endif

View File

@ -25,9 +25,8 @@ enum {
CDC_N__NITF
};
enum {
VND_N_CMSISDAP = 0,
#if CFG_TUD_VENDOR > 0
VND_N_CFG,
VND_N_CFG = 0,
#endif
VND_N__NITF

View File

@ -48,10 +48,8 @@ void cdc_uart_init(void) {
gpio_set_function(PINOUT_UART_TX, GPIO_FUNC_UART);
gpio_set_function(PINOUT_UART_RX, GPIO_FUNC_UART);
gpio_set_function(PINOUT_UART_CTS, GPIO_FUNC_SIO);
gpio_set_function(PINOUT_UART_RTS, GPIO_FUNC_SIO);
uart_init(PINOUT_UART_INTERFACE, lc_brate/*PINOUT_UART_BAUDRATE*/);
//uart_set_hw_flow(PINOUT_UART_INTERFACE, hwflow, hwflow);
uart_set_hw_flow(PINOUT_UART_INTERFACE, hwflow, hwflow);
uart_set_format(PINOUT_UART_INTERFACE, lc_data, lc_stop, lc_parity);
bi_decl(bi_2pins_with_func(PINOUT_UART_TX, PINOUT_UART_RX, GPIO_FUNC_UART));
@ -60,8 +58,6 @@ void cdc_uart_deinit(void) {
uart_deinit(PINOUT_UART_INTERFACE);
gpio_set_function(PINOUT_UART_TX, GPIO_FUNC_NULL);
gpio_set_function(PINOUT_UART_RX, GPIO_FUNC_NULL);
gpio_set_function(PINOUT_UART_CTS, GPIO_FUNC_NULL);
gpio_set_function(PINOUT_UART_RTS, GPIO_FUNC_NULL);
}
void cdc_uart_task(void) {
@ -94,8 +90,6 @@ bool cdc_uart_get_hwflow(void) {
bool cdc_uart_set_hwflow(bool enable) {
hwflow = enable;
//uart_set_hw_flow(PINOUT_UART_INTERFACE, enable, enable);
// TODO: CTS
gpio_put(PINOUT_UART_RTS, enable);
return true;
}

View File

@ -4,8 +4,6 @@
#include <hardware/gpio.h>
#include <hardware/pio.h>
#include "util.h"
#include "DAP_config.h"
#include <DAP.h>
@ -17,24 +15,20 @@
int jtagsm = -1, jtagoffset = -1;
void PORT_OFF(void) {
//printf("disable\n");
if (jtagsm >= 0) {
if (jtagsm) {
pio_sm_set_enabled(PINOUT_JTAG_PIO_DEV, jtagsm, false);
pio_sm_unclaim(PINOUT_JTAG_PIO_DEV, jtagsm);
}
if (jtagoffset >= 0) {
if (jtagoffset)
pio_remove_program(PINOUT_JTAG_PIO_DEV, &dap_jtag_program, jtagoffset);
}
jtagoffset = jtagsm = -1;
if (swdsm >= 0) {
if (swdsm) {
pio_sm_set_enabled(PINOUT_JTAG_PIO_DEV, swdsm, false);
pio_sm_unclaim(PINOUT_JTAG_PIO_DEV, swdsm);
}
if (swdoffset >= 0) {
if (swdoffset)
pio_remove_program(PINOUT_JTAG_PIO_DEV, &dap_swd_program, swdoffset);
}
swdoffset = swdsm = -1;
sio_hw->gpio_oe_clr = PINOUT_SWCLK_MASK | PINOUT_SWDIO_MASK |
PINOUT_TDI_MASK //| PINOUT_TDO_MASK
@ -54,22 +48,17 @@ void PORT_JTAG_SETUP(void) {
sio_hw->gpio_oe_clr = PINOUT_TDO_MASK;
hw_write_masked(&padsbank0_hw->io[PINOUT_JTAG_TCK],
PADS_BANK0_GPIO0_IE_BITS // bits to set: input enable
| (GPIO_SLEW_RATE_SLOW << PADS_BANK0_GPIO0_SLEWFAST_LSB),
PADS_BANK0_GPIO0_IE_BITS | PADS_BANK0_GPIO0_OD_BITS // bits to mask out: input enable, output disable
| PADS_BANK0_GPIO0_SLEWFAST_BITS);
hw_write_masked(&padsbank0_hw->io[PINOUT_JTAG_TMS],
PADS_BANK0_GPIO0_IE_BITS | (GPIO_SLEW_RATE_SLOW << PADS_BANK0_GPIO0_SLEWFAST_LSB),
PADS_BANK0_GPIO0_IE_BITS | PADS_BANK0_GPIO0_OD_BITS
| PADS_BANK0_GPIO0_SLEWFAST_BITS);
hw_write_masked(&padsbank0_hw->io[PINOUT_JTAG_TDI],
PADS_BANK0_GPIO0_IE_BITS | (GPIO_SLEW_RATE_SLOW << PADS_BANK0_GPIO0_SLEWFAST_LSB),
PADS_BANK0_GPIO0_IE_BITS | PADS_BANK0_GPIO0_OD_BITS
| PADS_BANK0_GPIO0_SLEWFAST_BITS);
PADS_BANK0_GPIO0_IE_BITS, // bits to set: input enable
PADS_BANK0_GPIO0_IE_BITS |
PADS_BANK0_GPIO0_OD_BITS); // bits to mask out: input enable, output disable
hw_write_masked(&padsbank0_hw->io[PINOUT_JTAG_TMS], PADS_BANK0_GPIO0_IE_BITS,
PADS_BANK0_GPIO0_IE_BITS | PADS_BANK0_GPIO0_OD_BITS);
hw_write_masked(&padsbank0_hw->io[PINOUT_JTAG_TDI], PADS_BANK0_GPIO0_IE_BITS,
PADS_BANK0_GPIO0_IE_BITS | PADS_BANK0_GPIO0_OD_BITS);
hw_write_masked(&padsbank0_hw->io[PINOUT_JTAG_TDO],
PADS_BANK0_GPIO0_IE_BITS | PADS_BANK0_GPIO0_OD_BITS // TDO needs to have its output disabled
| (GPIO_SLEW_RATE_SLOW << PADS_BANK0_GPIO0_SLEWFAST_LSB),
PADS_BANK0_GPIO0_IE_BITS | PADS_BANK0_GPIO0_OD_BITS | PADS_BANK0_GPIO0_SLEWFAST_BITS);
PADS_BANK0_GPIO0_IE_BITS |
PADS_BANK0_GPIO0_OD_BITS, // TDO needs to have its output disabled
PADS_BANK0_GPIO0_IE_BITS | PADS_BANK0_GPIO0_OD_BITS);
hw_write_masked(&padsbank0_hw->io[PINOUT_JTAG_nTRST], PADS_BANK0_GPIO0_IE_BITS,
PADS_BANK0_GPIO0_IE_BITS | PADS_BANK0_GPIO0_OD_BITS);
hw_write_masked(&padsbank0_hw->io[PINOUT_JTAG_nRESET], PADS_BANK0_GPIO0_IE_BITS,
@ -94,16 +83,16 @@ void PORT_JTAG_SETUP(void) {
| PINOUT_nTRST_MASK | PINOUT_nRESET_MASK;
}*/
/*void JTAG_Sequence(uint32_t info, const uint8_t* tdi, uint8_t* tdo) {
void JTAG_Sequence(uint32_t info, const uint8_t* tdi, uint8_t* tdo) {
uint32_t n = info & JTAG_SEQUENCE_TCK;
if (n == 0) n = 64;
// printf("seq hi 0x%lx\n", info);
//
// printf("%s", "tdi: ");
// for (size_t j = 0; j < ((n + 7) >> 3); ++j) {
// printf("0x%x ", ((const uint8_t*)tdi)[j]);
// }
printf("seq hi 0x%lx\n", info);
printf("%s", "tdi: ");
for (size_t j = 0; j < ((n + 7) >> 3); ++j) {
printf("0x%x ", ((const uint8_t*)tdi)[j]);
}
if (info & JTAG_SEQUENCE_TMS) PIN_SWDIO_TMS_SET();
else PIN_SWDIO_TMS_CLR();
@ -126,32 +115,17 @@ void PORT_JTAG_SETUP(void) {
n = info & JTAG_SEQUENCE_TCK;
if (n == 0) n = 64;
// if (info & JTAG_SEQUENCE_TDO) {
// printf("%s", "\ntdo: ");
// for (size_t j = 0; j < ((n + 7) >> 3); ++j) {
// printf("0x%x ", ((const uint8_t*)tdo)[j]);
// }
// printf("%c", '\n');
// } else printf("%s", "\nno tdo\n");
}*/
void jtag_tms_seq(uint32_t count, const uint8_t* data) {
for (size_t i = 0; i < count; ++i) {
uint8_t byte = data[i >> 3],
bit = (byte >> (i & 7)) & 1;
if (bit) PIN_SWDIO_TMS_SET();
else PIN_SWDIO_TMS_CLR();
PIN_SWCLK_TCK_CLR();
PIN_DELAY_SLOW(DAP_Data.clock_delay);
PIN_SWCLK_TCK_SET();
PIN_DELAY_SLOW(DAP_Data.clock_delay);
}
if (info & JTAG_SEQUENCE_TDO) {
printf("%s", "\ntdo: ");
for (size_t j = 0; j < ((n + 7) >> 3); ++j) {
printf("0x%x ", ((const uint8_t*)tdo)[j]);
}
printf("%c", '\n');
} else printf("%s", "\nno tdo\n");
}
#else
void PORT_JTAG_SETUP(void) {
//printf("jtag setup\n");
resets_hw->reset &= ~(RESETS_RESET_IO_BANK0_BITS | RESETS_RESET_PADS_BANK0_BITS);
/* set to default high level */
@ -163,22 +137,17 @@ void PORT_JTAG_SETUP(void) {
sio_hw->gpio_oe_clr = PINOUT_TDO_MASK;
hw_write_masked(&padsbank0_hw->io[PINOUT_JTAG_TCK],
PADS_BANK0_GPIO0_IE_BITS // bits to set: input enable
| (GPIO_SLEW_RATE_SLOW << PADS_BANK0_GPIO0_SLEWFAST_LSB),
PADS_BANK0_GPIO0_IE_BITS | PADS_BANK0_GPIO0_OD_BITS // bits to mask out: input enable, output disable
| PADS_BANK0_GPIO0_SLEWFAST_BITS);
hw_write_masked(&padsbank0_hw->io[PINOUT_JTAG_TMS],
PADS_BANK0_GPIO0_IE_BITS | (GPIO_SLEW_RATE_SLOW << PADS_BANK0_GPIO0_SLEWFAST_LSB),
PADS_BANK0_GPIO0_IE_BITS | PADS_BANK0_GPIO0_OD_BITS
| PADS_BANK0_GPIO0_SLEWFAST_BITS);
hw_write_masked(&padsbank0_hw->io[PINOUT_JTAG_TDI],
PADS_BANK0_GPIO0_IE_BITS | (GPIO_SLEW_RATE_SLOW << PADS_BANK0_GPIO0_SLEWFAST_LSB),
PADS_BANK0_GPIO0_IE_BITS | PADS_BANK0_GPIO0_OD_BITS
| PADS_BANK0_GPIO0_SLEWFAST_BITS);
PADS_BANK0_GPIO0_IE_BITS, // bits to set: input enable
PADS_BANK0_GPIO0_IE_BITS |
PADS_BANK0_GPIO0_OD_BITS); // bits to mask out: input enable, output disable
hw_write_masked(&padsbank0_hw->io[PINOUT_JTAG_TMS], PADS_BANK0_GPIO0_IE_BITS,
PADS_BANK0_GPIO0_IE_BITS | PADS_BANK0_GPIO0_OD_BITS);
hw_write_masked(&padsbank0_hw->io[PINOUT_JTAG_TDI], PADS_BANK0_GPIO0_IE_BITS,
PADS_BANK0_GPIO0_IE_BITS | PADS_BANK0_GPIO0_OD_BITS);
hw_write_masked(&padsbank0_hw->io[PINOUT_JTAG_TDO],
PADS_BANK0_GPIO0_IE_BITS | PADS_BANK0_GPIO0_OD_BITS // TDO needs to have its output disabled
| (GPIO_SLEW_RATE_SLOW << PADS_BANK0_GPIO0_SLEWFAST_LSB),
PADS_BANK0_GPIO0_IE_BITS | PADS_BANK0_GPIO0_OD_BITS | PADS_BANK0_GPIO0_SLEWFAST_BITS);
PADS_BANK0_GPIO0_IE_BITS |
PADS_BANK0_GPIO0_OD_BITS, // TDO needs to have its output disabled
PADS_BANK0_GPIO0_IE_BITS | PADS_BANK0_GPIO0_OD_BITS);
hw_write_masked(&padsbank0_hw->io[PINOUT_JTAG_nTRST], PADS_BANK0_GPIO0_IE_BITS,
PADS_BANK0_GPIO0_IE_BITS | PADS_BANK0_GPIO0_OD_BITS);
hw_write_masked(&padsbank0_hw->io[PINOUT_JTAG_nRESET], PADS_BANK0_GPIO0_IE_BITS,
@ -196,19 +165,23 @@ 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_nRESET].ctrl = GPIO_FUNC_SIO << IO_BANK0_GPIO0_CTRL_FUNCSEL_LSB;
if (jtagsm == -1) jtagsm = pio_claim_unused_sm(PINOUT_JTAG_PIO_DEV, false);
if (jtagsm == -1) jtagsm = pio_claim_unused_sm(PINOUT_JTAG_PIO_DEV, true);
if (jtagoffset == -1)
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);
}
#define JTAG_SEQUENCE_NO_TMS 0x80000u /* should be large enough */
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 JTAG_Sequence(uint32_t info, const uint8_t* tdi, uint8_t* tdo) {
//printf("jtag seq\n");
//pio_sm_set_enabled(PINOUT_JTAG_PIO_DEV, jtagsm, true);
float div = (float)clock_get_hz(clk_sys) / (4 * DAP_Data.clock_freq);
if (div < 2) div = 2;
else if (div > 65536) div = 65536;
@ -219,10 +192,8 @@ void JTAG_Sequence(uint32_t info, const uint8_t* tdi, uint8_t* tdo) {
uint32_t n = info & JTAG_SEQUENCE_TCK;
if (n == 0) n = 64;
//if (!(n & JTAG_SEQUENCE_NO_TMS)) {
if (info & JTAG_SEQUENCE_TMS) PIN_SWDIO_TMS_SET();
else PIN_SWDIO_TMS_CLR();
//}
if (info & JTAG_SEQUENCE_TMS) PIN_SWDIO_TMS_SET();
else PIN_SWDIO_TMS_CLR();
io_wo_8* tx = (io_wo_8*)&PINOUT_JTAG_PIO_DEV->txf[jtagsm];
io_ro_8* rx = (io_ro_8*)&PINOUT_JTAG_PIO_DEV->rxf[jtagsm];
@ -232,9 +203,9 @@ void JTAG_Sequence(uint32_t info, const uint8_t* tdi, uint8_t* tdo) {
//printf("n=%lu bytelen=%lu last_shift=%lu\n", n, bytelen, last_shift);
uint32_t txremain = bytelen,
rxremain = last_shift ? bytelen : (bytelen + 1);
//printf("txremain=%lu rxremain=%lu\n", txremain, rxremain);
/*printf("txremain=%lu rxremain=%lu\n", txremain, rxremain);
/*printf("%s", "tdi: ");
printf("%s", "tdi: ");
for (size_t j = 0; j < ((n + 7) >> 3); ++j) {
printf("0x%x ", ((const uint8_t*)tdi)[j]);
}
@ -274,54 +245,6 @@ void JTAG_Sequence(uint32_t info, const uint8_t* tdi, uint8_t* tdo) {
}
printf("%c", '\n');
} else printf("%s", "no tdo\n");*/
//pio_sm_set_enabled(PINOUT_JTAG_PIO_DEV, jtagsm, false);
}
void jtag_tms_seq(uint32_t count, const uint8_t* data) {
//printf("jtag tms seq\n");
// work around openFPGAloader bug (how did this even get here?)
if (DAP_Data.clock_delay == 0) {
DAP_Data.clock_delay = 8;
}
/*pio_sm_set_out_pins(PINOUT_JTAG_PIO_DEV, jtagsm, PINOUT_JTAG_TMS, 1);
pio_sm_set_set_pins(PINOUT_JTAG_PIO_DEV, jtagsm, PINOUT_JTAG_TMS, 1);
pio_sm_set_pins(PINOUT_JTAG_PIO_DEV, jtagsm, gpio_get(PINOUT_JTAG_TMS)?1:0);
gpio_set_function(PINOUT_JTAG_TMS, GPIO_FUNC_PIO0 + ((PINOUT_JTAG_PIO_DEV == pio0) ? 0 : 1));
for (uint32_t i = 0, n; i < count; i += n) {
n = count - i;
if (n == 0) break;
if (n > 64) n = 64;
n &= JTAG_SEQUENCE_TCK;
JTAG_Sequence(n | JTAG_SEQUENCE_NO_TMS, data, NULL);
}
gpio_put(PINOUT_JTAG_TMS, data[(count >> 3)] & (1 << (count & 7)));
gpio_set_function(PINOUT_JTAG_TMS, GPIO_FUNC_SIO);
pio_sm_set_out_pins(PINOUT_JTAG_PIO_DEV, jtagsm, PINOUT_JTAG_TDI, 1);
pio_sm_set_set_pins(PINOUT_JTAG_PIO_DEV, jtagsm, PINOUT_JTAG_TDI, 1);*/
// FIXME: above doesn't seem to work somehow -- so fall back to bit-banging
const uint8_t tdibit = 0xff;
PIN_SWCLK_TCK_SET();
gpio_set_function(PINOUT_JTAG_TMS, GPIO_FUNC_SIO);
gpio_set_function(PINOUT_JTAG_TCK, GPIO_FUNC_SIO);
for (size_t i = 0; i < count; ++i) {
uint8_t byte = data[i >> 3],
bit = byte & (1 << (i & 7));//(byte >> (i & 7)) & 1;
//JTAG_Sequence(1 | (bit ? JTAG_SEQUENCE_TMS : 0), &tdibit, NULL);
if (bit) PIN_SWDIO_TMS_SET();
else PIN_SWDIO_TMS_CLR();
PIN_SWCLK_TCK_CLR();
PIN_DELAY_SLOW(DAP_Data.clock_delay);
PIN_SWCLK_TCK_SET();
PIN_DELAY_SLOW(DAP_Data.clock_delay);
}
gpio_set_function(PINOUT_JTAG_TCK, GPIO_FUNC_PIO0);
}
#endif
@ -354,7 +277,6 @@ static void jtag_seq(uint32_t num, int tms, const void* tdi, void* tdo) {
}
uint32_t JTAG_ReadIDCode(void) {
//printf("jtag readID\n");
// tdi=NULL: ~~0xff!~~ repeat last-seen bit, ignore otherwise
// tdo=NULL: ignore
jtag_seq(1, JTAG_SEQUENCE_TMS, NULL, NULL);
@ -395,7 +317,6 @@ uint32_t JTAG_ReadIDCode(void) {
}
void JTAG_IR(uint32_t ir) {
//printf("jtag IR\n");
jtag_seq(2,JTAG_SEQUENCE_TMS, NULL, NULL);
jtag_seq(2,0, NULL, NULL);
uint64_t v = ~(uint64_t)0;
@ -497,7 +418,7 @@ static uint8_t xfer_base(uint32_t request, uint32_t* data, bool check_ack) {
exit:
jtag_seq(1,JTAG_SEQUENCE_TMS, NULL, NULL);
jtag_seq(1,0, NULL, NULL);
PIN_TDI_OUT(1); // TDI HI (no clk)
PIN_TDI_OUT(1); // TODO: TDI HI (no clk)
if (request & DAP_TRANSFER_TIMESTAMP) DAP_Data.timestamp = TIMESTAMP_GET();
if (check_ack) jtag_seq(DAP_Data.transfer.idle_cycles, 0, NULL, NULL);
return (uint8_t)ack;
@ -611,12 +532,10 @@ exit:
}
void JTAG_WriteAbort(uint32_t data) {
//printf("jtag wrabort\n");
xfer_base(0 /* write,A2=0,A3=0 */, &data, false);
}
uint8_t JTAG_Transfer(uint32_t request, uint32_t* data) {
//printf("jtag xfer\n");
return xfer_base(request, data, true);
}
//#endif

View File

@ -33,7 +33,6 @@ static inline void dap_jtag_program_init(PIO pio, uint sm, uint offset,
uint16_t freq, uint pin_tck, uint pin_tdi, uint pin_tdo) {
pio_sm_config c = dap_jtag_program_get_default_config(offset);
sm_config_set_out_pins(&c, pin_tdi, 1);
//sm_config_set_set_pins(&c, pin_tdi, 1);
sm_config_set_in_pins(&c, pin_tdo);
sm_config_set_sideset_pins(&c, pin_tck);
// (shift to left, autopush/pull, threshold=nbits)

View File

@ -8,8 +8,6 @@
#include "DAP_config.h"
#include <DAP.h>
#include "util.h"
#include "dap_swd.pio.h"
#define SWD_PIO
@ -24,12 +22,10 @@ void PORT_SWD_SETUP(void) {
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 | (GPIO_SLEW_RATE_SLOW << PADS_BANK0_GPIO0_SLEWFAST_LSB),
PADS_BANK0_GPIO0_IE_BITS | PADS_BANK0_GPIO0_OD_BITS | PADS_BANK0_GPIO0_SLEWFAST_BITS);
hw_write_masked(&padsbank0_hw->io[PINOUT_SWDIO],
PADS_BANK0_GPIO0_IE_BITS | (GPIO_SLEW_RATE_SLOW << PADS_BANK0_GPIO0_SLEWFAST_LSB),
PADS_BANK0_GPIO0_IE_BITS | PADS_BANK0_GPIO0_OD_BITS | PADS_BANK0_GPIO0_SLEWFAST_BITS);
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;
}
@ -103,23 +99,20 @@ void SWD_Sequence (uint32_t info, const uint8_t *swdo, uint8_t *swdi) {
#else
void PORT_SWD_SETUP(void) {
//printf("swd setup\n");
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 | (GPIO_SLEW_RATE_SLOW << PADS_BANK0_GPIO0_SLEWFAST_LSB),
PADS_BANK0_GPIO0_IE_BITS | PADS_BANK0_GPIO0_OD_BITS | PADS_BANK0_GPIO0_SLEWFAST_BITS);
hw_write_masked(&padsbank0_hw->io[PINOUT_SWDIO],
PADS_BANK0_GPIO0_IE_BITS | (GPIO_SLEW_RATE_SLOW << PADS_BANK0_GPIO0_SLEWFAST_LSB),
PADS_BANK0_GPIO0_IE_BITS | PADS_BANK0_GPIO0_OD_BITS | PADS_BANK0_GPIO0_SLEWFAST_BITS);
iobank0_hw->io[PINOUT_SWCLK].ctrl = GPIO_FUNC_PIO0 << IO_BANK0_GPIO0_CTRL_FUNCSEL_LSB;
iobank0_hw->io[PINOUT_SWDIO].ctrl = GPIO_FUNC_PIO0 << IO_BANK0_GPIO0_CTRL_FUNCSEL_LSB;
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, false);
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,
@ -142,8 +135,16 @@ inline static void PIN_SWDIO_SET_PIO(void) {
(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) {
//printf("swd sequence\n");
pio_sm_set_enabled(PINOUT_JTAG_PIO_DEV, swdsm, true);
//busy_wait_us_32(0);
@ -215,8 +216,7 @@ void SWD_Sequence(uint32_t info, const uint8_t* swdo, uint8_t* swdi) {
}
#endif
static void swd_seq(uint32_t count, uint32_t flags, const uint8_t* swdo, uint8_t* swdi) {
//printf("swd seqbase count=%lu\n", count);
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;
@ -252,38 +252,24 @@ static void swd_seq(uint32_t count, uint32_t flags, const uint8_t* swdo, uint8_t
uint8_t lastbyte = swdo[((count + 7) >> 3) - 1];
last_bit = (lastbyte & (1 << (count & 7))) ? ~(uint64_t)0 : (uint64_t)0;
}
//printf("swd seqbase end\n");
}
void jtag_tms_seq(uint32_t count, const uint8_t* data);
void SWJ_Sequence(uint32_t count, const uint8_t* data) {
//printf("swj sequence\n");
swd_seq(count, 0, data, NULL);
// we can't just do a swd_seq() call here, as the debugger might be in JTAG
// instead of SWD mode.
/*for (uint32_t i = 0, k = 0; i < count; ++i) {
if ((i & 7) == 0) {
val = data[k];
++k;
}
if ((swdsm == -1 || swdoffset == -1) && jtagsm >= 0 && jtagoffset >= 0) {
jtag_tms_seq(count, data); // JTAG mode -- handle in JTAG code
} else if (swdsm >= 0 && swdoffset >= 0) {
swd_seq(count, 0, data, NULL); // SWD mode - we can do just this
} else {
//printf("E: SWJ_Sequence while not in JTAG or SBW mode\n");
// welp - can't really report an error to the upper CMSIS-DAP layers
jtag_tms_seq(count, data); // uses only SIO for now so ¯\_(ツ)_/¯
}
// hackier but stabler
/*jtag_tms_seq(count, data);
if (swdsm != -1 && swdoffset != -1 && jtagsm == -1 && jtagoffset == -1) {
gpio_set_function(PINOUT_JTAG_TMS, GPIO_FUNC_PIO0);
swdio = (val >> (i & 7)) & 1;
// SET SWDIO
// SWCLK LO; DELAY; SWCLK HI; DELAY
}*/
}
uint8_t SWD_Transfer(uint32_t request, uint32_t* data) {
//printf("swd xfer request=%08lx\n", request);
uint32_t parity;
uint8_t swdo;
@ -296,13 +282,11 @@ uint8_t SWD_Transfer(uint32_t request, uint32_t* data) {
uint8_t ack = 0;
swd_seq(3, SWD_SEQUENCE_DIN, NULL, &ack);
//printf(" ack=%hhu\n", ack);
uint32_t num;
switch (ack) {
case DAP_TRANSFER_OK:
if (request & DAP_TRANSFER_RnW) {
//printf(" xfer ok, r\n");
uint64_t val = 0;
parity = 0;
// FIXME: this is little-endian-only!
@ -318,7 +302,6 @@ uint8_t SWD_Transfer(uint32_t request, uint32_t* data) {
//PIN_SWDIO_OUT_ENABLE();
} else { // write
//printf(" xfer ok, w\n");
swd_seq(DAP_Data.swd_conf.turnaround, SWD_SEQUENCE_DIN, NULL, NULL);
//PIN_SWDIO_OUT_ENABLE();
@ -333,45 +316,36 @@ uint8_t SWD_Transfer(uint32_t request, uint32_t* data) {
swd_seq(33, 0, (const uint8_t*)&out, NULL);
}
//printf(" set ts\n");
if (request & DAP_TRANSFER_TIMESTAMP) DAP_Data.timestamp = TIMESTAMP_GET();
num = DAP_Data.transfer.idle_cycles;
//printf(" idlecyc=%lu\n", num);
for (uint32_t i = 0; i < num; i += 64) {
for (uint32_t i = 0; i < num; num += 64) {
uint64_t swdio = 0;
uint32_t cyc = num - i;
if (cyc > 64) cyc = 64;
//printf(" sequence of %lu\n", cyc);
SWD_Sequence((cyc & SWD_SEQUENCE_CLK), (const uint8_t*)&swdio, NULL);
}
//printf(" idlecyc end\n");
break;
case DAP_TRANSFER_WAIT: case DAP_TRANSFER_FAULT:
num = DAP_Data.swd_conf.turnaround;
if (DAP_Data.swd_conf.data_phase && (request & DAP_TRANSFER_RnW)) {
num += 33; // 32 bits + parity
}
//printf(" wait/fault: %lu\n", num);
swd_seq(num, SWD_SEQUENCE_DIN, NULL, NULL);
if (DAP_Data.swd_conf.data_phase && !(request & DAP_TRANSFER_RnW)) {
//printf(" w/f dataphase\n");
uint64_t swdio = 0;
swd_seq(33, 0, (const uint8_t*)&swdio, NULL); // 32 data bits + parity
}
break;
default: // protocol error
//printf(" proto error\n");
swd_seq(DAP_Data.swd_conf.turnaround + 33, SWD_SEQUENCE_DIN, NULL, NULL);
break;
}
//printf(" finished\n");
PIN_SWDIO_OUT_ENABLE();
PIN_SWDIO_SET_PIO();
return ack;

View File

@ -61,6 +61,7 @@ uint32_t SWO_Mode_UART(uint32_t enable) {
mode_enabled = false;
if (swo_dmach >= 0) {
dma_channel_abort(swo_dmach);
dma_channel_unclaim(swo_dmach); // ugh why is it "dma_channel_xyz" and "dma_xyz_channel"
swo_dmach = -1;
}
@ -189,6 +190,7 @@ uint32_t SWO_Mode_Manchester(uint32_t enable) {
mode_enabled = false;
if (swo_dmach >= 0) {
dma_channel_abort(swo_dmach);
dma_channel_unclaim(swo_dmach); // ugh why is it "dma_channel_xyz" and "dma_xyz_channel"
swo_dmach = -1;
}

View File

@ -1,7 +1,7 @@
// vim: set et ts=8:
#ifndef PINOUT_H_
#define PINOUT_H_
#ifndef BSP_PINOUT_M_DEFAULT_H_
#define BSP_PINOUT_M_DEFAULT_H_
// UART config
#define PINOUT_UART_TX 8
@ -15,11 +15,11 @@
#define PINOUT_JTAG_TCK 2 // == SWCLK
#define PINOUT_JTAG_TMS 3 // == SWDIO
#define PINOUT_JTAG_TDI 4
#define PINOUT_JTAG_TDO 5 // == SWO
#define PINOUT_JTAG_TDO 5
#define PINOUT_JTAG_nTRST 6
#define PINOUT_JTAG_nRESET 7
#define PINOUT_JTAG_PIO_DEV pio0
#define PINOUT_JTAG_SWO_DEV pio0
#define PINOUT_JTAG_SWO_DEV pio1
// SPI config
#define PINOUT_SPI_DEV spi1
@ -62,17 +62,15 @@
* SWO-MC 1
*
* PIO:
* PIO0: (max. 4 SM, max. 32 insn)
* JTAG 1 6
* SWD 1 11
* SWO 2 6 (manchester) + 9 (uart)
* PIO0: (max. 4)
* JTAG 1
* SWD 1
* PIO1: (max. 4)
* SWO 2
*
* PIO0 IS NOW FULL!
* PIO1: (max. 4 SM, max. 32 insn)
* UART0: stdio
*
* UART: stdio
* 0: stdio
* 1: USB-CDC/DAP-UART
* UART1: USB-CDC/DAP-UART
*
* SPI:
* 1: SPI access
@ -81,7 +79,7 @@
* 0: I2C access
*
* ADC:
* temperature sensor
*
*/
#endif

View File

@ -0,0 +1,28 @@
// vim: set et:
#include "m_ftdi/ftdi.h"
void ftdi_if_asyncbb_init(struct ftdi_interface* itf) {
(void)itf;
}
void ftdi_if_asyncbb_deinit(struct ftdi_interface* itf) {
(void)itf;
}
void ftdi_if_asyncbb_set_baudrate(struct ftdi_interface* itf, uint32_t baudrate) {
(void)itf; (void)baudrate;
}
void ftdi_if_asyncbb_write(struct ftdi_interface* itf, const uint8_t* data, size_t datasize) {
(void)itf; (void)data; (void)datasize;
}
size_t ftdi_if_asyncbb_read(struct ftdi_interface* itf, uint8_t* data, size_t maxsize) {
(void)itf; (void)data; (void)maxsize;
return 0;
}

View File

@ -0,0 +1,34 @@
#ifndef BSP_FEATURE_M_FTDI_H_
#define BSP_FEATURE_M_FTDI_H_
#define DBOARD_HAS_FTDI
/* TODO: more fine-grained FTDI support/not-support stuff? */
#include "bsp-info.h"
// not all that much here
enum {
HID_N__NITF = 0
};
enum {
#ifdef USE_USBCDC_FOR_STDIO
CDC_N_STDIO = 0,
#endif
CDC_N__NITF
};
enum {
/*VND_N_FTDI_IFA = 0,
VND_N_FTDI_IFB,*/
VND_N_CFG = 0,
VND_N__NITF
};
#define VND_N_FTDI_IFA 42
#define VND_N_FTDI_IFB 69
#endif

View File

@ -0,0 +1,23 @@
// vim: set et:
#include "m_ftdi/ftdi.h"
void ftdi_if_cpufifo_init(struct ftdi_interface* itf) {
(void)itf;
}
void ftdi_if_cpufifo_deinit(struct ftdi_interface* itf) {
(void)itf;
}
void ftdi_if_cpufifo_set_baudrate(struct ftdi_interface* itf, uint32_t baudrate) {
(void)itf; (void)baudrate;
}
void ftdi_if_cpufifo_write(struct ftdi_interface* itf, const uint8_t* data, size_t datasize) {
(void)itf; (void)data; (void)datasize;
}
size_t ftdi_if_cpufifo_read(struct ftdi_interface* itf, uint8_t* data, size_t maxsize) {
(void)itf; (void)data; (void)maxsize;
return 0;
}

22
bsp/rp2040/m_ftdi/fifo.c Normal file
View File

@ -0,0 +1,22 @@
// vim: set et:
#include "m_ftdi/ftdi.h"
void ftdi_if_fifo_init(struct ftdi_interface* itf) {
(void)itf;
}
void ftdi_if_fifo_deinit(struct ftdi_interface* itf) {
(void)itf;
}
void ftdi_if_fifo_set_baudrate(struct ftdi_interface* itf, uint32_t baudrate) {
(void)itf; (void)baudrate;
}
void ftdi_if_fifo_write(struct ftdi_interface* itf, const uint8_t* data, size_t datasize) {
(void)itf; (void)data; (void)datasize;
}
size_t ftdi_if_fifo_read(struct ftdi_interface* itf, uint8_t* data, size_t maxsize) {
(void)itf; (void)data; (void)maxsize;
return 0;
}

315
bsp/rp2040/m_ftdi/ftdi_hw.c Normal file
View File

@ -0,0 +1,315 @@
// vim: set et:
/* include order matters here */
#include <hardware/dma.h>
#include <hardware/gpio.h>
#include <hardware/irq.h>
#include <hardware/pio.h>
#include <hardware/structs/dma.h>
#include "m_ftdi/ftdi_hw.h"
struct ftdi_hw itf_bsp_data[2];
static int piosm_to_dreq(int pio, int sm) {
return DREQ_PIO0_TX0 + pio * 8 + sm;
}
static void ftdihw_dma_isr();
void ftdihw_init(struct ftdi_hw* fr, struct ftdi_interface* itf) {
memset(fr, 0, sizeof *fr);
fr->itf = itf;
fr->pio = ftdihw_itf_to_pio(itf);
fr->pinbase = ftdihw_itf_to_base(itf);
fr->rx.prg_off = 0xff;
fr->rx.piosm = 0xff;
fr->rx.dmach = 0xff;
fr->tx.prg_off = 0xff;
fr->tx.piosm = 0xff;
fr->tx.dmach = 0xff;
// we start with nothing to write out, but we can start filling a buffer already
fr->rx.dmabuf_dend = sizeof(fr->dma_in_buf) - 1;
irq_set_enabled(DMA_IRQ_0 + (fr->itf->index & 1), false);
irq_set_exclusive_handler(DMA_IRQ_0 + (fr->itf->index & 1), ftdihw_dma_isr);
irq_set_enabled(DMA_IRQ_0 + (fr->itf->index & 1), true);
}
void ftdihw_deinit(struct ftdi_hw* hw) {
irq_set_enabled(DMA_IRQ_0 + (hw->itf->index & 1), false);
irq_remove_handler(DMA_IRQ_0 + (hw->itf->index & 1), ftdihw_dma_isr);
}
bool ftdihw_dma_ch_init(struct ftdi_hw_ch* ch, struct ftdi_hw* hw, const void* prg) {
int off, sm, dmach;
sm = pio_claim_unused_sm(hw->pio, false);
if (sm == -1) return false;
dmach = dma_claim_unused_channel(false);
if (dmach == -1) {
pio_sm_unclaim(hw->pio, sm);
return false;
}
if (!pio_can_add_program(hw->pio, prg)) {
dma_channel_unclaim(dmach);
pio_sm_unclaim(hw->pio, sm);
return false;
}
off = pio_add_program(hw->pio, prg);
ch->prg = prg;
ch->prg_off = off;
ch->piosm = sm;
ch->dmach = dmach;
return true;
}
void ftdihw_dma_ch_deinit(struct ftdi_hw_ch* ch, struct ftdi_hw* hw) {
dma_channel_unclaim(ch->dmach);
pio_sm_set_enabled(hw->pio, ch->piosm, false);
pio_sm_unclaim(hw->pio, ch->piosm);
pio_remove_program(hw->pio, ch->prg, ch->prg_off);
ch->dmach = 0xff;
ch->piosm = 0xff;
ch->prg_off = 0xff;
ch->prg = NULL;
}
void ftdihw_dma_rx_setup(struct ftdi_hw* hw, bool start) {
dma_irqn_set_channel_enabled(hw->itf->index & 1, hw->rx.dmach, false);
if (hw->rx.dmabuf_dend == hw->rx.dmabuf_dstart)
--hw->rx.dmabuf_dend; // mod 256 automatically
dma_channel_config dcfg = dma_channel_get_default_config(hw->rx.dmach);
channel_config_set_read_increment(&dcfg, false);
channel_config_set_write_increment(&dcfg, true);
channel_config_set_dreq(&dcfg, piosm_to_dreq(hw->itf->index, hw->rx.piosm));
channel_config_set_transfer_data_size(&dcfg, DMA_SIZE_8);
channel_config_set_ring(&dcfg, true, 8); // 1<<8 -sized ring buffer on write end
dma_channel_configure(hw->rx.dmach, &dcfg,
&hw->dma_in_buf[hw->rx.dmabuf_dstart], &hw->pio->rxf[hw->rx.piosm],
(hw->rx.dmabuf_dend - hw->rx.dmabuf_dstart) % sizeof(hw->dma_in_buf), start);
dma_irqn_set_channel_enabled(hw->itf->index & 1, hw->rx.dmach, true);
}
void ftdihw_dma_rx_stop(struct ftdi_hw* hw) {
dma_irqn_set_channel_enabled(hw->itf->index & 1, hw->rx.dmach, false);
dma_channel_abort(hw->rx.dmach);
}
void ftdihw_dma_tx_setup(struct ftdi_hw* hw, bool start) {
dma_irqn_set_channel_enabled(hw->itf->index & 1, hw->tx.dmach, false);
if (hw->tx.dmabuf_dend == hw->tx.dmabuf_dstart)
--hw->tx.dmabuf_dend; // mod 256 automatically
dma_channel_config dcfg = dma_channel_get_default_config(hw->tx.dmach);
channel_config_set_read_increment(&dcfg, true);
channel_config_set_write_increment(&dcfg, false);
channel_config_set_dreq(&dcfg, piosm_to_dreq(hw->itf->index, hw->tx.piosm));
channel_config_set_transfer_data_size(&dcfg, DMA_SIZE_8);
channel_config_set_ring(&dcfg, false, 8); // 1<<8 -sized ring buffer on read end
dma_channel_configure(hw->tx.dmach, &dcfg,
&hw->pio->txf[hw->tx.piosm], &hw->dma_out_buf[hw->tx.dmabuf_dstart],
(hw->tx.dmabuf_dend - hw->tx.dmabuf_dstart) % sizeof(hw->dma_out_buf), start);
dma_irqn_set_channel_enabled(hw->itf->index & 1, hw->tx.dmach, true);
}
void ftdihw_dma_tx_stop(struct ftdi_hw* hw) {
dma_irqn_set_channel_enabled(hw->itf->index & 1, hw->tx.dmach, false);
dma_channel_abort(hw->tx.dmach);
}
size_t ftdihw_dma_read(struct ftdi_hw* hw, uint8_t* dest, size_t maxsize) {
// DMA is sending data from PIO to between dstart and dend, and is planned
// to continue up to dataend. when it finishes an xfer (between dstart and
// dend), it looks at dataend to see if it should continue sending more. if
// not, it pauses itself as tehre's no more work to be done, and sets a
// FIFO overrun flag in the FTDI error things
//
// here we read from dataend to dstart, careful not to touch the area the
// DMA is currently writing to (while holding the channel paused), and then
// moving the dataend marker. the channel is always reenabled, as either it
// was busy and now it has more space to put data in, or needs to be
// restarted as there is space now
//
// pausing the DMA channel is ok as it'll pause the PIO FIFO a bit, but
// nothing drastic
if (maxsize == 0) return 0;
size_t rv = 0;
// TODO: make time between pause & resume shorter by moving stuff around?
bool wasbusy = dma_hw->ch[hw->rx.dmach].ctrl_trig & DMA_CH0_CTRL_TRIG_BUSY_BITS;
hw_clear_bits(&dma_hw->ch[hw->rx.dmach].ctrl_trig, DMA_CH0_CTRL_TRIG_EN_BITS); // pause (also prevents IRQ)
// dstart can get modified by the IRQ; IRQ reads dataend
uint8_t dstart = hw->rx.dmabuf_dstart, dataend = hw->rx.dmabuf_dataend;
if (dataend > dstart) dstart += sizeof(hw->dma_in_buf);
// nothing ready yet - bail out
if (dstart == (dataend + 1) % sizeof(hw->dma_in_buf)) {
goto END;
}
__compiler_memory_barrier();
// copy from data in ringbuffer that was read in by PIO
rv = (size_t)dstart - (size_t)dataend - 1;
if (rv > maxsize) rv = maxsize;
for (size_t i = 0; i < rv; ++i) {
dest[i] = hw->dma_in_buf[(dataend + i) % sizeof(hw->dma_in_buf)];
}
uint8_t dataend_new = (dataend + rv) % sizeof(hw->dma_in_buf);
hw->rx.dmabuf_dataend = dataend_new;
hw->itf->modemstat &= ~sio_modem_fifoerr;
END:
if (!wasbusy) {
hw->rx.dmabuf_dstart = hw->rx.dmabuf_dend; // not required to set in DMA hw, but need to keep track of it in code
hw->rx.dmabuf_dend = hw->rx.dmabuf_dataend;
ftdihw_dma_rx_setup(hw, true);
} else // if already busy easlier, simply reenable
hw_set_bits(&dma_hw->ch[hw->rx.dmach].ctrl_trig, DMA_CH0_CTRL_TRIG_EN_BITS); // resume (also reenables IRQ)
return rv;
return rv;
}
bool ftdihw_dma_write(struct ftdi_hw* hw, const uint8_t* src, size_t size) {
// DMA is sending data between dstart and dend to PIO, and is planned to
// continue up to dataend. when it finishes an xfer (between dstart and
// dend), it looks at dataned to see if it should continue sending more. if
// not, it pauses itself as there's no more work to be done
//
// here we insert some data in the ring buffer, careful to not overwrite
// what the DMA is currently transferring (while holding the channel
// paused), and then moving the dataend marker. the channel is always
// reenabled, as either it was busy and now has more work to do, or needs
// to be restarted as there's data now
//
// pausing the DMA channel is ok as it'll pause the PIO FIFO a bit, but
// nothing drastic
if (size >= 255) return false; // don't even bother
bool rv = true;
// TODO: make time between pause & resume shorter by moving stuff around?
bool wasbusy = dma_hw->ch[hw->tx.dmach].ctrl_trig & DMA_CH0_CTRL_TRIG_BUSY_BITS;
hw_clear_bits(&dma_hw->ch[hw->tx.dmach].ctrl_trig, DMA_CH0_CTRL_TRIG_EN_BITS); // pause (also prevents IRQ)
// dstart can get modified by the IRQ; IRQ reads dataend
uint8_t dstart = hw->tx.dmabuf_dstart, dataend = hw->tx.dmabuf_dataend;
if (dataend > dstart) dstart += sizeof(hw->dma_out_buf);
// no space to put it in - overrun error
if ((size_t)dstart - (size_t)dataend < size + 1) {
hw->itf->modemstat |= sio_modem_fifoerr;
rv = false;
goto END;
}
__compiler_memory_barrier();
// copy data to buffer, to be copied next
for (size_t i = 0; i < size; ++i) {
hw->dma_out_buf[(dataend + i) % sizeof(hw->dma_out_buf)] = src[i];
}
uint8_t dataend_new = (dataend + size) % sizeof(hw->dma_out_buf);
hw->tx.dmabuf_dataend = dataend_new;
hw->itf->modemstat &= ~sio_modem_temt;
END:
if (!wasbusy) {
hw->tx.dmabuf_dstart = hw->tx.dmabuf_dend; // not required to set in DMA hw, but need to keep track of it in code
hw->tx.dmabuf_dend = hw->tx.dmabuf_dataend;
ftdihw_dma_tx_setup(hw, true);
} else // if already busy easlier, simply reenable
hw_set_bits(&dma_hw->ch[hw->tx.dmach].ctrl_trig, DMA_CH0_CTRL_TRIG_EN_BITS); // resume (also reenables IRQ)
return rv;
}
void ftdihw_dma_rx_flush(struct ftdi_hw* hw) {
ftdihw_dma_rx_stop(hw);
hw->rx.dmabuf_dstart = 0;
hw->rx.dmabuf_dend = 0;
hw->rx.dmabuf_dataend = 0;
}
void ftdihw_dma_tx_flush(struct ftdi_hw* hw) {
ftdihw_dma_tx_stop(hw);
hw->tx.dmabuf_dstart = 0;
hw->tx.dmabuf_dend = 0;
hw->tx.dmabuf_dataend = 0;
}
static void ftdihw_dma_rx_irq(struct ftdi_hw* hw) {
// an rx DMA transfer has finished. if in the meantime (between DMA xfer
// start and now) more room has become available, restart the DMA channel
// to write to the new space instead. otherwise, if there's no more space
// left, we're in a data overrun condition, so don't restart, and set the
// relevant FTDI error flags
uint8_t dend = hw->rx.dmabuf_dend, dataend = hw->rx.dmabuf_dataend;
if (dend == dataend) {
// data overrun, stop stuff (until read() restarts the DMA)
hw->itf->modemstat |= sio_modem_fifoerr;
} else {
hw->rx.dmabuf_dstart = dend;
hw->rx.dmabuf_dend = dataend;
ftdihw_dma_rx_setup(hw, true);
}
}
static void ftdihw_dma_tx_irq(struct ftdi_hw* hw) {
// a tx DMA transfer has finished. if in the meantile (between DMA xfer
// start and now) more data has become available, restart the DMA channel
// to read from the new data instead. otherwise, if there's no more data to
// be read, we have nothing to do and we can leave the DMA channel in its
// idle state.
uint8_t dend = hw->tx.dmabuf_dend, dataend = hw->tx.dmabuf_dataend;
if (dend == dataend) {
// nothing to do
hw->itf->modemstat |= sio_modem_temt;
} else {
hw->tx.dmabuf_dstart = dend;
hw->tx.dmabuf_dend = dataend;
ftdihw_dma_tx_setup(hw, true);
}
}
static void ftdihw_dma_isr() {
// interrupt service routine: dispatch to functions above depending on INTS0
uint32_t flags = dma_hw->ints0;
uint32_t allflg = 1u << itf_bsp_data[0].rx.dmach;
allflg |= 1u << itf_bsp_data[0].tx.dmach;
allflg |= 1u << itf_bsp_data[1].rx.dmach;
allflg |= 1u << itf_bsp_data[1].tx.dmach;
for (size_t i = 0; (i < 2) && (flags & allflg); ++i) {
uint32_t flg = 1u << itf_bsp_data[i].rx.dmach;
if (flags & flg) {
flags &= ~flg;
dma_hw->ints0 = flg; // ack int
ftdihw_dma_rx_irq(&itf_bsp_data[i]);
}
flg = 1u << itf_bsp_data[i].tx.dmach;
if (flags & flg) {
flags &= ~flg;
dma_hw->ints0 = flg; // ack int
ftdihw_dma_tx_irq(&itf_bsp_data[i]);
}
}
}

View File

@ -0,0 +1,73 @@
// vim: set et:
#ifndef FTDI_BASE_H_
#define FTDI_BASE_H_
#include <stdint.h>
#include <stdbool.h>
#include "m_ftdi/pinout.h"
#include "m_ftdi/ftdi.h"
struct ftdi_hw_ch {
const void* prg;
uint8_t prg_off, piosm, dmach;
volatile uint8_t dmabuf_dstart, dmabuf_dend, dmabuf_dataend;
};
struct ftdi_hw {
struct ftdi_interface* itf;
PIO pio;
struct ftdi_hw_ch rx, tx;
uint8_t pinbase;
volatile uint8_t dma_in_buf[256/*CFG_TUD_VENDOR_TX_BUFSIZE*/];
volatile uint8_t dma_out_buf[256/*CFG_TUD_VENDOR_RX_BUFSIZE*/];
};
extern struct ftdi_hw itf_bsp_data[2];
void ftdihw_init(struct ftdi_hw* fr, struct ftdi_interface* itf);
void ftdihw_deinit(struct ftdi_hw* fr);
bool ftdihw_dma_ch_init(struct ftdi_hw_ch* ch, struct ftdi_hw* fr, const void* prg);
void ftdihw_dma_ch_deinit(struct ftdi_hw_ch* fr, struct ftdi_hw* hw);
void ftdihw_dma_rx_setup(struct ftdi_hw* fr, bool start);
void ftdihw_dma_rx_stop(struct ftdi_hw* fr);
void ftdihw_dma_tx_setup(struct ftdi_hw* fr, bool start);
void ftdihw_dma_tx_stop(struct ftdi_hw* fr);
size_t ftdihw_dma_read(struct ftdi_hw* fr, uint8_t* dest, size_t maxsize);
bool ftdihw_dma_write(struct ftdi_hw* fr, const uint8_t* src, size_t size);
void ftdihw_dma_rx_flush(struct ftdi_hw* fr);
void ftdihw_dma_tx_flush(struct ftdi_hw* fr);
static inline int ftdihw_idx_to_base(int itf_idx) {
return itf_idx ? PINOUT_ITF_B_BASE : PINOUT_ITF_A_BASE;
}
static inline int ftdihw_itf_to_base(struct ftdi_interface* itf) {
return ftdihw_idx_to_base(itf->index);
}
static inline PIO ftdihw_idx_to_pio(int itf_idx) {
return itf_idx ? PINOUT_ITF_A_PIO : PINOUT_ITF_B_PIO;
}
static inline PIO ftdihw_itf_to_pio(struct ftdi_interface* itf) {
return ftdihw_idx_to_pio(itf->index);
}
static inline struct ftdi_hw* ftdihw_idx_to_hw(int itf_idx) {
return &itf_bsp_data[itf_idx & 1];
}
static inline struct ftdi_hw* ftdihw_itf_to_hw(struct ftdi_interface* itf) {
return ftdihw_idx_to_hw(itf->index);
}
#endif

View File

@ -0,0 +1,100 @@
// vim: set et:
#include <hardware/pio.h>
#include "m_ftdi/ftdi.h"
#include "m_ftdi/ftdi_hw.h"
static void init_mode(struct ftdi_interface* itf, enum ftdi_mode mode) {
switch (mode) {
case ftmode_uart: ftdi_if_uart_init(itf); ftdi_if_uart_set_baudrate(itf, itf->baudrate); break;
case ftmode_mpsse: ftdi_if_mpsse_init(itf); ftdi_if_mpsse_set_baudrate(itf, itf->baudrate); break;
case ftmode_asyncbb: ftdi_if_asyncbb_init(itf); ftdi_if_asyncbb_set_baudrate(itf, itf->baudrate); break;
case ftmode_syncbb: ftdi_if_syncbb_init(itf); ftdi_if_syncbb_set_baudrate(itf, itf->baudrate); break;
case ftmode_mcuhost: ftdi_if_mcuhost_init(itf); ftdi_if_mcuhost_set_baudrate(itf, itf->baudrate); break;
case ftmode_fifo: ftdi_if_fifo_init(itf); ftdi_if_fifo_set_baudrate(itf, itf->baudrate); break;
case ftmode_cpufifo: ftdi_if_cpufifo_init(itf); ftdi_if_cpufifo_set_baudrate(itf, itf->baudrate); break;
default: break;
}
}
static void deinit_mode(struct ftdi_interface* itf, enum ftdi_mode mode) {
switch (mode) {
case ftmode_uart: ftdi_if_uart_deinit(itf); break;
case ftmode_mpsse: ftdi_if_mpsse_deinit(itf); break;
case ftmode_asyncbb: ftdi_if_asyncbb_deinit(itf); break;
case ftmode_syncbb: ftdi_if_syncbb_deinit(itf); break;
case ftmode_mcuhost: ftdi_if_mcuhost_deinit(itf); break;
case ftmode_fifo: ftdi_if_fifo_deinit(itf); break;
case ftmode_cpufifo: ftdi_if_cpufifo_deinit(itf); break;
default: break;
}
}
void ftdi_if_init(struct ftdi_interface* itf) {
struct ftdi_hw* hw = ftdihw_itf_to_hw(itf);
ftdihw_init(hw, itf);
init_mode(itf, ftdi_if_get_mode(itf));
}
void ftdi_if_deinit(struct ftdi_interface* itf) {
deinit_mode(itf, ftdi_if_get_mode(itf));
struct ftdi_hw* hw = ftdihw_itf_to_hw(itf);
ftdihw_deinit(hw);
}
void ftdi_if_set_modemctrl(struct ftdi_interface* itf, uint8_t mask, uint8_t data) {
(void)itf; (void)mask; (void)data;
// TODO: what's this?
}
void ftdi_if_set_baudrate(struct ftdi_interface* itf, uint32_t baudrate) {
switch (ftdi_if_get_mode(itf)) {
case ftmode_uart: ftdi_if_uart_set_baudrate(itf, baudrate); break;
case ftmode_mpsse: ftdi_if_mpsse_set_baudrate(itf, baudrate); break;
case ftmode_asyncbb: ftdi_if_asyncbb_set_baudrate(itf, baudrate); break;
case ftmode_syncbb: ftdi_if_syncbb_set_baudrate(itf, baudrate); break;
case ftmode_mcuhost: ftdi_if_mcuhost_set_baudrate(itf, baudrate); break;
case ftmode_fifo: ftdi_if_fifo_set_baudrate(itf, baudrate); break;
case ftmode_cpufifo: ftdi_if_cpufifo_set_baudrate(itf, baudrate); break;
default: break;
}
}
enum ftdi_sio_modemstat ftdi_if_poll_modemstat(struct ftdi_interface* itf) {
(void)itf;
return sio_modem_cts | sio_modem_dts; // TODO: use this to read part of UART flow ctrl?
}
void ftdi_if_set_eventchar(struct ftdi_interface* itf, bool enable, uint8_t evchar) {
(void)itf; (void)enable; (void)evchar;
// TODO: when is this used? bitmode0-only? also ftd2xx headers make this look like its not just an "event on char" thing
}
void ftdi_if_set_errorchar(struct ftdi_interface* itf, bool enable, uint8_t erchar) {
(void)itf; (void)enable; (void)erchar;
// TODO: when is this used? bitmode0-only? also ftd2xx headers make this look like its not just an "error on char" thing
}
uint8_t ftdi_if_read_pins(struct ftdi_interface* itf) {
(void)itf;
return 0; // TODO: which pins does this return?
}
void ftdi_if_set_bitbang(struct ftdi_interface* itf, uint8_t dirmask,
enum ftdi_sio_bitmode bitmode, uint8_t olddir, enum ftdi_sio_bitmode oldmode) {
if (bitmode == oldmode && dirmask == olddir) return; // nothing to do
deinit_mode(itf, ftdi_get_mode_of(oldmode, itf->index ? FTDI_EEP_IFB_MODE : FTDI_EEP_IFA_MODE));
init_mode(itf, ftdi_if_get_mode(itf));
}
void ftdi_if_sio_reset(struct ftdi_interface* itf) { (void)itf; /* TODO: ? */ }
void ftdi_if_sio_tciflush(struct ftdi_interface* itf) {
(void)itf; /* TODO: ? */
}
void ftdi_if_sio_tcoflush(struct ftdi_interface* itf) {
(void)itf; /* TODO: ? */
}
void ftdi_if_set_latency(struct ftdi_interface* itf, uint8_t latency) { (void)itf; (void)latency; /* TODO: ? */ }
uint8_t ftdi_if_get_latency(struct ftdi_interface* itf) { return itf->latency; /* TODO: ? */ }

View File

@ -0,0 +1,94 @@
;
; Copyright (c) 2020 Raspberry Pi (Trading) Ltd.
;
; SPDX-License-Identifier: BSD-3-Clause
;
.program ftdi_uart_rx_mini
; Minimum viable 8n1 UART receiver. Wait for the start bit, then sample 8 bits
; with the correct timing.
; IN pin 0 is mapped to the GPIO used as UART RX.
; Autopush must be enabled, with a threshold of 8.
wait 0 pin 0 ; Wait for start bit
set x, 7 [10] ; Preload bit counter, delay until eye of first data bit
bitloop: ; Loop 8 times
in pins, 1 ; Sample data
jmp x-- bitloop [6] ; Each iteration is 8 cycles
% c-sdk {
#include "hardware/clocks.h"
#include "hardware/gpio.h"
static inline void ftdi_uart_rx_mini_program_init(PIO pio, uint sm, uint offset, uint pin, uint baud) {
pio_sm_set_consecutive_pindirs(pio, sm, pin, 1, false);
pio_gpio_init(pio, pin);
gpio_pull_up(pin);
pio_sm_config c = ftdi_uart_rx_mini_program_get_default_config(offset);
sm_config_set_in_pins(&c, pin); // for WAIT, IN
// Shift to right, autopush enabled
sm_config_set_in_shift(&c, true, true, 8);
sm_config_set_fifo_join(&c, PIO_FIFO_JOIN_RX);
// SM transmits 1 bit per 8 execution cycles.
float div = (float)clock_get_hz(clk_sys) / (8 * baud);
sm_config_set_clkdiv(&c, div);
pio_sm_init(pio, sm, offset, &c);
pio_sm_set_enabled(pio, sm, true);
}
%}
.program ftdi_uart_rx
; Slightly more fleshed-out 8n1 UART receiver which handles framing errors and
; break conditions more gracefully.
; IN pin 0 and JMP pin are both mapped to the GPIO used as UART RX.
start:
wait 0 pin 0 ; Stall until start bit is asserted
set x, 7 [10] ; Preload bit counter, then delay until halfway through
bitloop: ; the first data bit (12 cycles incl wait, set).
in pins, 1 ; Shift data bit into ISR
jmp x-- bitloop [6] ; Loop 8 times, each loop iteration is 8 cycles
jmp pin good_stop ; Check stop bit (should be high)
irq 4 rel ; Either a framing error or a break. Set a sticky flag,
wait 1 pin 0 ; and wait for line to return to idle state.
jmp start ; Don't push data if we didn't see good framing.
good_stop: ; No delay before returning to start; a little slack is
push ; important in case the TX clock is slightly too fast.
% c-sdk {
static inline void ftdi_uart_rx_program_init(PIO pio, uint sm, uint offset, uint pin, uint baud) {
pio_sm_set_consecutive_pindirs(pio, sm, pin, 1, false);
pio_gpio_init(pio, pin);
gpio_pull_up(pin);
pio_sm_config c = ftdi_uart_rx_program_get_default_config(offset);
sm_config_set_in_pins(&c, pin); // for WAIT, IN
sm_config_set_jmp_pin(&c, pin); // for JMP
// Shift to right, autopull disabled
sm_config_set_in_shift(&c, true, false, 32);
// Deeper FIFO as we're not doing any TX
sm_config_set_fifo_join(&c, PIO_FIFO_JOIN_RX);
// SM transmits 1 bit per 8 execution cycles.
float div = (float)clock_get_hz(clk_sys) / (8 * baud);
sm_config_set_clkdiv(&c, div);
pio_sm_init(pio, sm, offset, &c);
pio_sm_set_enabled(pio, sm, true);
}
static inline char ftdi_uart_rx_program_getc(PIO pio, uint sm) {
// 8-bit read from the uppermost byte of the FIFO, as data is left-justified
io_rw_8 *rxfifo_shift = (io_rw_8*)&pio->rxf[sm] + 3;
while (pio_sm_is_rx_fifo_empty(pio, sm))
tight_loop_contents();
return (char)*rxfifo_shift;
}
%}

View File

@ -0,0 +1,61 @@
;
; Copyright (c) 2020 Raspberry Pi (Trading) Ltd.
;
; SPDX-License-Identifier: BSD-3-Clause
;
.program ftdi_uart_tx
.side_set 1 opt
; An 8n1 UART transmit program.
; OUT pin 0 and side-set pin 0 are both mapped to UART TX pin.
pull side 1 [7] ; Assert stop bit, or stall with line in idle state
set x, 7 side 0 [7] ; Preload bit counter, assert start bit for 8 clocks
bitloop: ; This loop will run 8 times (8n1 UART)
out pins, 1 ; Shift 1 bit from OSR to the first OUT pin
jmp x-- bitloop [6] ; Each loop iteration is 8 cycles.
% c-sdk {
#include "hardware/clocks.h"
static inline void ftdi_uart_tx_program_init(PIO pio, uint sm, uint offset, uint pin_tx, uint baud) {
// Tell PIO to initially drive output-high on the selected pin, then map PIO
// onto that pin with the IO muxes.
pio_sm_set_pins_with_mask(pio, sm, 1u << pin_tx, 1u << pin_tx);
pio_sm_set_pindirs_with_mask(pio, sm, 1u << pin_tx, 1u << pin_tx);
pio_gpio_init(pio, pin_tx);
pio_sm_config c = ftdi_uart_tx_program_get_default_config(offset);
// OUT shifts to right, no autopull
sm_config_set_out_shift(&c, true, false, 32);
// We are mapping both OUT and side-set to the same pin, because sometimes
// we need to assert user data onto the pin (with OUT) and sometimes
// assert constant values (start/stop bit)
sm_config_set_out_pins(&c, pin_tx, 1);
sm_config_set_sideset_pins(&c, pin_tx);
// We only need TX, so get an 8-deep FIFO!
sm_config_set_fifo_join(&c, PIO_FIFO_JOIN_TX);
// SM transmits 1 bit per 8 execution cycles.
float div = (float)clock_get_hz(clk_sys) / (8 * baud);
sm_config_set_clkdiv(&c, div);
pio_sm_init(pio, sm, offset, &c);
pio_sm_set_enabled(pio, sm, true);
}
static inline void ftdi_uart_tx_program_putc(PIO pio, uint sm, char c) {
pio_sm_put_blocking(pio, sm, (uint32_t)c);
}
static inline void ftdi_uart_tx_program_puts(PIO pio, uint sm, const char *s) {
while (*s)
ftdi_uart_tx_program_putc(pio, sm, *s++);
}
%}

View File

@ -0,0 +1,38 @@
// vim: set et:
#include "m_ftdi/ftdi.h"
void ftdi_if_mcuhost_init(struct ftdi_interface* itf) {
(void)itf;
}
void ftdi_if_mcuhost_deinit(struct ftdi_interface* itf) {
(void)itf;
}
void ftdi_if_mcuhost_set_baudrate(struct ftdi_interface* itf, uint32_t baudrate) {
(void)itf; (void)baudrate;
}
void ftdi_if_mcuhost_flush(struct ftdi_interface* itf) {
(void)itf;
}
void ftdi_if_mcuhost_wait_io(struct ftdi_interface* itf, bool level) {
(void)itf; (void)level;
}
uint8_t ftdi_if_mcuhost_read8(struct ftdi_interface* itf, uint8_t addr) {
(void)itf; (void)addr;
return 0;
}
uint8_t ftdi_if_mcuhost_read16(struct ftdi_interface* itf, uint16_t addr) {
(void)itf; (void)addr;
return 0;
}
void ftdi_if_mcuhost_write8(struct ftdi_interface* itf, uint8_t addr, uint8_t value) {
(void)itf; (void)addr; (void)value;
}
void ftdi_if_mcuhost_write16(struct ftdi_interface* itf, uint16_t addr, uint8_t value) {
(void)itf; (void)addr; (void)value;
}

82
bsp/rp2040/m_ftdi/mpsse.c Normal file
View File

@ -0,0 +1,82 @@
// vim: set et:
#include "m_ftdi/ftdi.h"
void ftdi_if_mpsse_init(struct ftdi_interface* itf) {
(void)itf;
}
void ftdi_if_mpsse_deinit(struct ftdi_interface* itf) {
(void)itf;
}
void ftdi_if_mpsse_set_baudrate(struct ftdi_interface* itf, uint32_t baudrate) {
(void)itf; (void)baudrate;
}
void ftdi_if_mpsse_flush(struct ftdi_interface* itf) {
(void)itf;
}
void ftdi_if_mpsse_wait_io(struct ftdi_interface* itf, bool level) {
(void)itf; (void)level;
}
void ftdi_if_mpsse_set_dirval_lo(struct ftdi_interface* itf, uint8_t dir, uint8_t val) {
(void)itf; (void)dir; (void)val;
}
void ftdi_if_mpsse_set_dirval_hi(struct ftdi_interface* itf, uint8_t dir, uint8_t val) {
(void)itf; (void)dir; (void)val;
}
uint8_t ftdi_if_mpsse_read_lo(struct ftdi_interface* itf) {
(void)itf;
return 0;
}
uint8_t ftdi_if_mpsse_read_hi(struct ftdi_interface* itf) {
(void)itf;
return 0;
}
void ftdi_if_mpsse_loopback(struct ftdi_interface* itf, bool enable) {
(void)itf; (void)enable;
}
void ftdi_if_mpsse_set_clkdiv(struct ftdi_interface* itf, uint16_t div) {
(void)itf; (void)div;
}
uint8_t ftdi_if_mpsse_xfer_bits(struct ftdi_interface* itf, int flags, size_t nbits, uint8_t value) {
(void)itf; (void)flags; (void)nbits; (void)value;
return 0;
}
void ftdi_if_mpsse_xfer_bytes(struct ftdi_interface* itf, int flags, size_t nbytes, uint8_t* dst, const uint8_t* src) {
(void)itf; (void)flags; (void)nbytes; (void)dst; (void)src;
}
uint8_t ftdi_if_mpsse_tms_xfer(struct ftdi_interface* itf, int flags, size_t nbits, uint8_t value) {
(void)itf; (void)flags; (void)nbits; (void)value;
return 0;
}
void ftdi_if_mpsse_div5(struct ftdi_interface* itf, bool enable) {
(void)itf; (void)enable;
}
void ftdi_if_mpsse_data_3ph(struct ftdi_interface* itf, bool enable) {
(void)itf; (void)enable;
}
void ftdi_if_mpsse_adaptive(struct ftdi_interface* itf, bool enable) {
(void)itf; (void)enable;
}
void ftdi_if_mpsse_clockonly(struct ftdi_interface* itf, uint32_t cycles) {
(void)itf; (void)cycles;
}
void ftdi_if_mpsse_clock_wait_io(struct ftdi_interface* itf, bool level) {
(void)itf; (void)level;
}
void ftdi_if_mpsse_clockonly_wait_io(struct ftdi_interface* itf, bool level, uint32_t cycles) {
(void)itf; (void)level; (void)cycles;
}
void ftdi_if_mpsse_hi_is_tristate(struct ftdi_interface* itf, uint16_t pinmask) {
(void)itf; (void)pinmask;
}

119
bsp/rp2040/m_ftdi/pinout.h Normal file
View File

@ -0,0 +1,119 @@
#ifndef BSP_PINOUT_M_FTDI_H_
#define BSP_PINOUT_M_FTDI_H_
/* NOTE: no C code here! needs to be include-able from PIO asm! */
#define PINOUT_ITF_A_BASE 2
#define PINOUT_ITF_B_BASE 14
// TODO: ? or just different SMs on the same PIO? would complicate things tho
#define PINOUT_ITF_A_PIO pio0
#define PINOUT_ITF_B_PIO pio1
#define PINOUT_DBUS0_OFF 0
#define PINOUT_DBUS1_OFF 1
#define PINOUT_DBUS2_OFF 2
#define PINOUT_DBUS3_OFF 3
#define PINOUT_DBUS4_OFF 4
#define PINOUT_DBUS5_OFF 5
#define PINOUT_DBUS6_OFF 6
#define PINOUT_DBUS7_OFF 7
#define PINOUT_CBUS0_OFF 8
#define PINOUT_CBUS1_OFF 9
#define PINOUT_CBUS2_OFF 10
#define PINOUT_CBUS3_OFF 11
#define PINOUT_UART_TXD_OFF 0
#define PINOUT_UART_RXD_OFF 1
#define PINOUT_UART_nRTS_OFF 2
#define PINOUT_UART_nCTS_OFF 3
#define PINOUT_UART_nDTR_OFF 4
#define PINOUT_UART_nDSR_OFF 5
#define PINOUT_UART_nDCD_OFF 6
#define PINOUT_UART_nRI_OFF 7
#define PINOUT_UART_TXDEN_OFF 8
#define PINOUT_UART_nSLEEP_OFF 9
#define PINOUT_UART_nRXLED_OFF 10
#define PINOUT_UART_nTXLED_OFF 11
#define PINOUT_FIFO_D0_OFF 0
#define PINOUT_FIFO_D1_OFF 1
#define PINOUT_FIFO_D2_OFF 2
#define PINOUT_FIFO_D3_OFF 3
#define PINOUT_FIFO_D4_OFF 4
#define PINOUT_FIFO_D5_OFF 5
#define PINOUT_FIFO_D6_OFF 6
#define PINOUT_FIFO_D7_OFF 7
#define PINOUT_FIFO_nRXF_OFF 8
#define PINOUT_FIFO_nTXE_OFF 9
#define PINOUT_FIFO_nRD_OFF 10
#define PINOUT_FIFO_WR_OFF 11
#define PINOUT_BBANG_D0_OFF 0
#define PINOUT_BBANG_D1_OFF 1
#define PINOUT_BBANG_D2_OFF 2
#define PINOUT_BBANG_D3_OFF 3
#define PINOUT_BBANG_D4_OFF 4
#define PINOUT_BBANG_D5_OFF 5
#define PINOUT_BBANG_D6_OFF 6
#define PINOUT_BBANG_D7_OFF 7
#define PINOUT_BBANG_nWR0_OFF 8
#define PINOUT_BBANG_nRD0_OFF 9
#define PINOUT_BBANG_nWR1_OFF 10
#define PINOUT_BBANG_nRD1_OFF 11
#define PINOUT_MPSSE_TCK_CK_OFF 0
#define PINOUT_MPSSE_TDI_DO_OFF 1
#define PINOUT_MPSSE_TDO_DI_OFF 2
#define PINOUT_MPSSE_TMS_CS_OFF 3
#define PINOUT_MPSSE_GPIOL0_OFF 4
#define PINOUT_MPSSE_GPIOL1_OFF 5
#define PINOUT_MPSSE_GPIOL2_OFF 6
#define PINOUT_MPSSE_GPIOL3_OFF 7
#define PINOUT_MPSSE_GPIOH0_OFF 8
#define PINOUT_MPSSE_GPIOH1_OFF 9
#define PINOUT_MPSSE_GPIOH2_OFF 10
#define PINOUT_MPSSE_GPIOH3_OFF 11
#define PINOUT_MCUHOST_AD0_OFF 0
#define PINOUT_MCUHOST_AD1_OFF 1
#define PINOUT_MCUHOST_AD2_OFF 2
#define PINOUT_MCUHOST_AD3_OFF 3
#define PINOUT_MCUHOST_AD4_OFF 4
#define PINOUT_MCUHOST_AD5_OFF 5
#define PINOUT_MCUHOST_AD6_OFF 6
#define PINOUT_MCUHOST_AD7_OFF 7
#define PINOUT_MCUHOST_IO0_OFF 8
#define PINOUT_MCUHOST_IO1_OFF 9
#define PINOUT_MCUHOST_IORDY_OFF 10
#define PINOUT_MCUHOST_OSC_OFF 11
// ---
#define PINOUT_MCUHOST_A8_OFF 0
#define PINOUT_MCUHOST_A9_OFF 1
#define PINOUT_MCUHOST_AA_OFF 2
#define PINOUT_MCUHOST_AB_OFF 3
#define PINOUT_MCUHOST_AC_OFF 4
#define PINOUT_MCUHOST_AD_OFF 5
#define PINOUT_MCUHOST_AE_OFF 6
#define PINOUT_MCUHOST_AF_OFF 7
#define PINOUT_MCUHOST_nCS_OFF 8
#define PINOUT_MCUHOST_ALE_OFF 9
#define PINOUT_MCUHOST_nRD_OFF 10
#define PINOUT_MCUHOST_nWR_OFF 11
#define PINOUT_CPUFIFO_D0_OFF 0
#define PINOUT_CPUFIFO_D1_OFF 1
#define PINOUT_CPUFIFO_D2_OFF 2
#define PINOUT_CPUFIFO_D3_OFF 3
#define PINOUT_CPUFIFO_D4_OFF 4
#define PINOUT_CPUFIFO_D5_OFF 5
#define PINOUT_CPUFIFO_D6_OFF 6
#define PINOUT_CPUFIFO_D7_OFF 7
#define PINOUT_CPUFIFO_nCS_OFF 8
#define PINOUT_CPUFIFO_A0_OFF 9
#define PINOUT_CPUFIFO_nRD_OFF 10
#define PINOUT_CPUFIFO_nWR_OFF 11
#endif

View File

@ -0,0 +1,22 @@
// vim: set et:
#include "m_ftdi/ftdi.h"
void ftdi_if_syncbb_init(struct ftdi_interface* itf) {
(void)itf;
}
void ftdi_if_syncbb_deinit(struct ftdi_interface* itf) {
(void)itf;
}
void ftdi_if_syncbb_set_baudrate(struct ftdi_interface* itf, uint32_t baudrate) {
(void)itf; (void)baudrate;
}
void ftdi_if_syncbb_write(struct ftdi_interface* itf, const uint8_t* data, size_t datasize) {
}
size_t ftdi_if_syncbb_read(struct ftdi_interface* itf, uint8_t* data, size_t maxsize) {
return 0;
}

143
bsp/rp2040/m_ftdi/uart.c Normal file
View File

@ -0,0 +1,143 @@
// vim: set et:
#include <hardware/dma.h>
#include <hardware/gpio.h>
#include <hardware/pio.h>
#include <hardware/structs/dma.h>
#include "m_ftdi/pinout.h"
#include "m_ftdi/ftdi_hw.h"
#include "ftdi_uart_rx.pio.h"
#include "ftdi_uart_tx.pio.h"
struct uart_state {
uint32_t baudrate;
bool enabled;
};
static struct uart_state state[2] = {
(struct uart_state){ .baudrate = 115200, .enabled = false },
(struct uart_state){ .baudrate = 115200, .enabled = false },
};
#define STATEOF(itf) (state[(itf)->index & 1])
// set up PIO->dma_in_buf DMA:
// * src=pio dst=buf 8bit 256words pacing=pio
// * IRQ: set overrun bit
// * start it
// * SETUP AT MODE ENTER, STOP AT MODE EXIT
//
// set up dma_out_buf->PIO DMA:
// * src=buf dst=pio 8bit <num>words pacing=pio
// * ~~no IRQ I think?~~ IRQ: next ringbuffer part (see below)
// * DO NOT start it on mode enter!
// * STOP AT MODE EXIT
//
// read routine:
// * abort DMA
// * copy data from dma_in_buf (read xfer len? == remaining of 256 bytes)
// * resetup & start DMA:
//
// write routine:
// * if DMA running: set overrun bit, bail out?
// * should use ringbuffer-like structure
// * pointers: dma start, dma end, data end (after dma, contiguous)
// * dma end can be calculated from DMA MMIO, but, race conditions so no
// * use DMA IRQ for next block (and wraparound: dma cannot do wraparound manually)
// * do not start next block if data end == dma start
// * can we set DMA xfer len while in-flight? datasheet p92 2.5.1: nope. sad
// * only bail out when data end == dma start - 1
// * copy data to dma_out_buf
// * set up & start DMA
//
// * what with buffers larger than 256 bytes?
// * just drop for now ig
// * what is the actual FTDI buffer size??
// * device-dependent so aaaa
// * which bits get set on errors?
// * do TCIFLUSH/TCOFLUSH influence these buffers, or only the USB proto
// handling buffers?
void ftdi_if_uart_init(struct ftdi_interface* itf) {
if (STATEOF(itf).enabled) return; // shrug
struct ftdi_hw* hw = ftdihw_itf_to_hw(itf);
if (!ftdihw_dma_ch_init(&hw->rx, hw, &ftdi_uart_rx_program)) return;
if (!ftdihw_dma_ch_init(&hw->tx, hw, &ftdi_uart_tx_program)) {
ftdihw_dma_ch_deinit(&hw->rx, hw);
return;
}
int pin_rx = hw->pinbase + PINOUT_UART_RXD_OFF,
pin_tx = hw->pinbase + PINOUT_UART_TXD_OFF;
ftdi_uart_rx_program_init(hw->pio, hw->rx.piosm, hw->rx.prg_off,
pin_rx, STATEOF(itf).baudrate);
ftdi_uart_tx_program_init(hw->pio, hw->tx.piosm, hw->tx.prg_off,
pin_tx, STATEOF(itf).baudrate);
gpio_set_function(pin_rx, GPIO_FUNC_PIO0 + itf->index);
gpio_set_function(pin_tx, GPIO_FUNC_PIO0 + itf->index);
ftdihw_dma_rx_setup(hw, true);
ftdihw_dma_tx_setup(hw, false);
STATEOF(itf).enabled = true;
}
void ftdi_if_uart_deinit(struct ftdi_interface* itf) {
if (!STATEOF(itf).enabled) return; // shrug
struct ftdi_hw* hw = ftdihw_itf_to_hw(itf);
int pin_rx = hw->pinbase + PINOUT_UART_RXD_OFF,
pin_tx = hw->pinbase + PINOUT_UART_TXD_OFF;
ftdihw_dma_rx_flush(hw);
ftdihw_dma_tx_flush(hw);
ftdihw_dma_ch_deinit(&hw->rx, hw);
ftdihw_dma_ch_deinit(&hw->tx, hw);
gpio_set_function(pin_rx, GPIO_FUNC_NULL);
gpio_set_function(pin_tx, GPIO_FUNC_NULL);
gpio_set_pulls(pin_rx, false, false);
gpio_set_pulls(pin_tx, false, false);
STATEOF(itf).enabled = false;
}
void ftdi_if_uart_set_baudrate(struct ftdi_interface* itf, uint32_t baudrate) {
if (!STATEOF(itf).enabled) return;
struct ftdi_hw* hw = ftdihw_itf_to_hw(itf);
int pin_rx = ftdihw_itf_to_base(itf) + PINOUT_UART_RXD_OFF,
pin_tx = ftdihw_itf_to_base(itf) + PINOUT_UART_TXD_OFF;
ftdi_uart_rx_program_init(hw->pio, hw->rx.piosm, hw->rx.prg_off, pin_rx, baudrate);
ftdi_uart_tx_program_init(hw->pio, hw->tx.piosm, hw->tx.prg_off, pin_tx, baudrate);
STATEOF(itf).baudrate = baudrate;
}
void ftdi_if_set_flowctrl(struct ftdi_interface* itf, enum ftdi_flowctrl flow) {
(void)itf; (void)flow; // TODO: bluh
}
void ftdi_if_set_lineprop(struct ftdi_interface* itf, enum ftdi_sio_lineprop lineprop) {
(void)itf; (void)lineprop; // TODO: break, stop, parity, #bits
}
void ftdi_if_uart_write(struct ftdi_interface* itf, const uint8_t* data, size_t datasize) {
struct ftdi_hw* hw = ftdihw_itf_to_hw(itf);
ftdihw_dma_write(hw, data, datasize);
}
size_t ftdi_if_uart_read(struct ftdi_interface* itf, uint8_t* data, size_t maxsize) {
struct ftdi_hw* hw = ftdihw_itf_to_hw(itf);
return ftdihw_dma_read(hw, data, maxsize);
}

View File

@ -1,35 +0,0 @@
#ifndef BSP_FEATURE_M_ISP_H_
#define BSP_FEATURE_M_ISP_H_
#define DBOARD_HAS_UART
#define DBOARD_HAS_CMSISDAP
#define DBOARD_HAS_MEHFET
#include "bsp-info.h"
enum {
HID_N_CMSISDAP = 0,
HID_N__NITF
};
enum {
CDC_N_UART = 0,
#ifdef USE_USBCDC_FOR_STDIO
CDC_N_STDIO,
#endif
CDC_N__NITF
};
enum {
VND_N_CMSISDAP = 0,
#if CFG_TUD_VENDOR > 0
VND_N_CFG,
#endif
VND_N_MEHFET,
VND_N__NITF
};
#endif

View File

@ -1,205 +0,0 @@
// vim: set et:
#include <hardware/gpio.h>
#include <hardware/timer.h>
#include <pico/time.h>
#include "util.h"
#include "m_isp/pinout.h"
#include "m_isp/sbw_hw.h"
#include "m_isp/mehfet.h"
void mehfet_hw_delay_ms(uint32_t t) { busy_wait_ms (t); }
void mehfet_hw_delay_us(uint32_t t) { busy_wait_us_32(t); }
static absolute_time_t target;
void mehfet_hw_timer_start(bool us, uint32_t to_reach) {
target = us ? make_timeout_time_us(to_reach) : make_timeout_time_ms(to_reach);
}
bool mehfet_hw_timer_reached(void) { return time_reached(target); }
void mehfet_hw_init(void) {
// don't init things just yet: PIO SM1 probably needs to be shared between
// multiple ISP/ICE/... protocols, so we only init stuff once actually starting
// TODO: init pin gpio mux stuff (to GPIO and not PIO first!)
// ^: or not: this will probably keep the target into reset, maybe not
// desired...
}
void mehfet_hw_deinit(void) {
// shrug
sbw_deinit(); // can't hurt
gpio_set_function(PINOUT_SBW_TCK , GPIO_FUNC_NULL);
gpio_set_function(PINOUT_SBW_TDIO, GPIO_FUNC_NULL);
}
__attribute__((__const__))
enum mehfet_caps mehfet_hw_get_caps(void) {
// only support SBW for now. we could add JTAG, but this will probably
// become messy very quickly, as it's behind CMSIS-DAP. implement later
// if someone needs it.
return mehfet_cap_sbw_entryseq |
mehfet_cap_has_reset_tap | mehfet_cap_has_irshift | mehfet_cap_has_drshift;
}
const char* /*error string, NULL if no error*/ mehfet_hw_connect(enum mehfet_conn conn) {
sbw_preinit(conn & mehfet_conn_nrstmask);
if (!sbw_init()) {
mehfet_hw_deinit();
return "SBW PIO init failed";
}
return NULL;
}
void mehfet_hw_disconnect(void) {
sbw_deinit();
}
void mehfet_hw_set_clkspeed(bool fast) {
if (fast) {
sbw_set_freq(true, 350e3);
} else {
sbw_set_freq(false, 50e3);
}
}
uint8_t mehfet_hw_get_old_lines(void) {
return (sbw_get_last_tclk() ? 1 : 0)
| (sbw_get_last_tms () ? 2 : 0)
| (sbw_get_last_tdi () ? 4 : 0);
}
void mehfet_hw_tdio_seq(uint32_t ncyc, bool tmslvl, const uint8_t* tdi, uint8_t* tdo) {
sbw_sequence(ncyc, tmslvl, tdi, tdo);
}
void mehfet_hw_tms_seq(uint32_t ncyc, bool tdilvl, const uint8_t* tms) {
sbw_tms_sequence(ncyc, tdilvl, tms);
}
void mehfet_hw_tclk_edge(bool newtclk) {
sbw_clrset_tclk(newtclk);
}
void mehfet_hw_tclk_burst(uint32_t ncyc) {
sbw_tclk_burst(ncyc);
}
enum mehfet_resettap_status mehfet_hw_reset_tap(enum mehfet_resettap_flags flags) {
enum mehfet_resettap_status rv = 0;
if (flags & mehfet_rsttap_do_reset) {
// TDI always 1
// TMS=1,1,1,1,1,1 -- reset TAP state to initial
// TMS=0 -- test-logic-reset to run-test/idle
// TMS=1,0,1,0,1 -- perform fuse check
// TMS=1,0 -- back to run-test/idle (needed for SBW only)
//const uint16_t tms_seq = 0x1abf;//0x3f | (0<<6) | (0x15 << 7) | (0x1 << 12);
const uint8_t tms_seq[2] = {0xbf,0x1a};
sbw_tms_sequence(14, true, tms_seq);
}
if (flags & mehfet_rsttap_fuse_do) {
// TDI always 1
// TMS=01010110 // same sequence as above, but without TAP reset
const uint8_t tms_seq = 0x6a;
sbw_tms_sequence(8, true, &tms_seq);
}
if (flags & mehfet_rsttap_fuse_read) {
for (size_t i = 0; i < 3; ++i) {
mehfet_hw_shift_ir(0x14); // CNTRL_SIG_CAPTURE
uint16_t dr = mehfet_hw_shift_dr16(0xaaaa);
if (dr == 0x5555) {
rv |= mehfet_rsttap_fuse_blown;
break;
}
}
}
return rv;
}
uint8_t mehfet_hw_shift_ir(uint8_t newir) {
// 1100: run-test/idle -> select-dr-scan -> select-ir-scan -> capture-ir -> shift-ir
const uint8_t tms_seqa = 0x03;
const uint8_t tms_seqb = 0x03 >> 1;
sbw_tms_sequence(1, sbw_get_last_tclk(), &tms_seqa);
sbw_tms_sequence(3, true, &tms_seqb);
// 7 data bits with TMS=0
// 1 data bit with TMS=1 (to exit1-ir)
uint8_t res = 0, resb = 0, newir2 = newir >> 7;
sbw_sequence(7, false, &newir , &res );
sbw_sequence(1, true , &newir2, &resb);
res |= resb << 7;
// TMS=1 (to update-ir)
// TMS=0 (to run-test/idle)
const uint8_t tms_seq_2a = 0x01;
const uint8_t tms_seq_2b = 0x01 >> 1;
sbw_tms_sequence(1, true, &tms_seq_2a);
sbw_tms_sequence(1, sbw_get_last_tclk(), &tms_seq_2b);
return bitswap(res); // fsr also needed here
}
static void bitswap_n(uint32_t nbytes, uint8_t* data) {
for (uint32_t i = 0, j = nbytes - 1; i < nbytes; ++i, --j) {
if (i == j) data[i] = bitswap(data[i]);
else {
uint8_t tmp = bitswap(data[i]);
data[i] = bitswap(data[j]);
data[j] = tmp;
}
}
}
void mehfet_hw_shift_dr(uint32_t nbits, uint8_t* drin, uint8_t* drout) {
// 100: run-test/idle -> select-dr-scan -> capture-dr -> shift-dr
const uint8_t tms_seqa = 0x01;
const uint8_t tms_seqb = 0x01 >> 1;
sbw_tms_sequence(1, sbw_get_last_tclk(), &tms_seqa);
sbw_tms_sequence(2, true, &tms_seqb);
if (nbits == 16) { // fast path: DR is often 16 bits wide
// DR content is MSB-first instead of LSB-first (IR is the latter)
uint16_t newdr = bitswap(drin[1]) | ((uint16_t)bitswap(drin[0]) << 8);
// 15 data bits with TMS=0
// 1 data bit with TMS=1 (to exit1-dr)
uint16_t res = 0;
uint8_t newdr2 = newdr >> 15, resb = 0;
// this is little-endian-only, but that's fine on the rp2040
sbw_sequence(15, false, (const uint8_t*)&newdr, (uint8_t*)&res);
sbw_sequence( 1, true , &newdr2, &resb);
res |= (uint16_t)resb << 15;
drout[0] = bitswap(res >> 8);
drout[1] = bitswap(res & 0xff);
} else {
uint32_t nbytes = (nbits + 7) >> 3;
// DR content is MSB-first instead of LSB-first (IR is the latter)
bitswap_n(nbytes, drin);
// n-1 data bits with TMS=0
// 1 data bit with TMS=1 (to exit1-dr)
uint8_t newdr2, resb = 0;
newdr2 = drin[nbytes - 1] >> ((nbits - 1) & 7);
sbw_sequence(nbits - 1, false, drin, drout);
sbw_sequence(1, true , &newdr2, &resb);
drout[nbytes - 1] |= resb << ((nbits - 1) & 7);
bitswap_n(nbytes, drout);
}
// TMS=1 (to update-dr)
// TMS=0 (to run-test/idle)
const uint8_t tms_seq_2a = 0x01;
const uint8_t tms_seq_2b = 0x01 >> 1;
sbw_tms_sequence(1, true, &tms_seq_2a);
sbw_tms_sequence(1, sbw_get_last_tclk(), &tms_seq_2b);
}

View File

@ -1,83 +0,0 @@
// vim: set et ts=8:
#ifndef PINOUT_H_
#define PINOUT_H_
// NOTE NOTE NOTE: as mode2 shares some stuff with mode1 (CMSIS-DAP, UART),
// make sure that pinouts are compatible etc.
// UART config
#define PINOUT_UART_TX 8
#define PINOUT_UART_RX 9
/*#define PINOUT_UART_CTS 10
#define PINOUT_UART_RTS 11*/
#define PINOUT_UART_INTERFACE uart1
#define PINOUT_UART_BAUDRATE 115200
// JTAG config
#define PINOUT_JTAG_TCK 2 // == SWCLK
#define PINOUT_JTAG_TMS 3 // == SWDIO
#define PINOUT_JTAG_TDI 4
#define PINOUT_JTAG_TDO 5
#define PINOUT_JTAG_nTRST 6
#define PINOUT_JTAG_nRESET 7
#define PINOUT_JTAG_PIO_DEV pio0
#define PINOUT_JTAG_SWO_DEV pio0
// SBW config
#define PINOUT_SBW_PIO pio1
#define PINOUT_SBW_TCK 10
#define PINOUT_SBW_TDIO 11
// LED config
// you can change these two as you like
#define PINOUT_LED_CONNECTED 1
#define PINOUT_LED_RUNNING 0
#ifndef PINOUT_LED
#ifndef PICO_DEFAULT_LED_PIN
#error "PICO_DEFAULT_LED_PIN is not defined, run PICOPROBE_LED=<led_pin> cmake"
#elif PICO_DEFAULT_LED_PIN == -1
#error "PICO_DEFAULT_LED_PIN is defined as -1, run PICOPROBE_LED=<led_pin> cmake"
#else
#define PINOUT_LED PICO_DEFAULT_LED_PIN
#endif
#endif /* PICOPROBE_LED */
/*
* HARDWARE RESOURCE USAGE:
*
* IRQ:
* DMA0 DAP-UART
* UART1 DAP-UART
*
* DMA: (max. 12)
* DAP-UART 2
* SWO-UART 1
* SWO-MC 1
*
* PIO:
* PIO0: (max. 4 SM, max. 32 insn)
* JTAG 1 6
* SWD 1 11
* SWO 2 6 (manchester) + 9 (uart)
*
* PIO0 IS NOW FULL!
* PIO1: (max. 4 SM, max. 32 insn)
* SBW 1 32
*
* UART: stdio
* 0: stdio
* 1: USB-CDC/DAP-UART
*
* SPI:
*
* I2C:
*
* ADC:
*
*/
#endif

View File

@ -1,206 +0,0 @@
.program sbw
.side_set 1
; Pin assignments:
; - SBWTCK is side-set pin 0
; - SBWTDIO is OUT/IN pin 0
;
; Autopush and autopull must be enabled, set to 8
; SBWTDIO input should not be guarded with sync flipflops, as TDO xfers are synchronous
PUBLIC start:
; SBWTCK hi to not lose/reset debug mode
pull side 1 ; clear leftover OSR bits, pull in new data
startloop:
out exec, 16 side 1 ; use for set y, 0/1 ; in x, num ; jmp addr
jmp startloop side 1
; "subroutine" "calling convention"
; * set y, 0/1 : initial TMS (sbw_seq) / TDI (sbw_tms_seq) / TCLK (sbw_tclk_burst) value
; * in x, num : number of JTAG cycles (sbw_seq/sbw_tms_seq) / TCLK half-cycles (sbw_tclk_burst)
; * jmp subroutine
; * ^ all 'side 1'
; y: static TMS value to use
; x: number of JTAG clock cycles minus one
; TDI output gets sourced bit by bit from the TX FIFO
; TDO input gets sent bit by bit to the RX FIFO
PUBLIC sbw_seq:
sbw_seq_iter:
; tms slot:
set pindirs, 1 side 1 ; SBWTDIO is now output
mov pins, y side 1 ; output static TMS value
nop side 0 [1] ; target reads TMS at falling edge
; tdi slot:
out pins, 1 side 1 [1] ; output TDI from FIFO
nop side 0 [1] ; target reads TDI at falling edge
; tdo slot:
set pindirs, 0 side 1 [1] ; we need some clock pulse
nop side 0 ; give target some time to drive IO
in pins, 1 side 0 ; input TDO
jmp x--, sbw_seq_iter side 1 ; also gives target some time to stop driving IO
; NOTE: there's a glitch: if TMS=TDO!=TDI and !=TDI_next,
; then there's a short glitch (wrong voltage level) in the
; TDO->TMS high clock phase. it's benign.
push side 1 ; flush ISR
jmp start side 1
; y: static TDI value to use
; x: number of JTAG clock cycles minus one
; TMS output gets sourced bit by bit from the TX FIFO
PUBLIC sbw_tms_seq:
; tms slot
set pindirs, 1 side 1 ; SBWTDIO is now output
out pins, 1 side 1 ; output TMS from FIFO
nop side 0 [1] ; target reads TMS at falling edge
; tdi slot
mov pins, y side 1 [1] ; output static TDI value
nop side 0 [1] ; target reads TDI at falling edge
; tdo slot
set pindirs, 0 side 1 [1]
jmp x--, sbw_tms_seq side 0 [1] ; ignore returned TDO, go back
jmp start side 1
; stationary SBWTCK values are:
; jmp
; TMS TDI TDO |TMS TDI TDO
; | | | || | |
; 1100110011001110011001100 ...
;
; a full cycle takes 13 cycles, a single slot 4
; the SM divider should be sysclk/(4*baudrate)
; baudrate shouldn't exceed 20 MHz (typical value is 18 MHz?)
; SBWTCK LOW phases shouldn't exceed 7us, so the baudrate should be at least 40 kHz
; y: initial/previous TCLK value
; x: number of TCLK *half*-cycles minus one!
; TCLK values get sourced from the TX FIFO. best is to use a DMA with fixed
; source address, with value 0x55/0xaa (depending on y) for strobes, or
; 0x00/0xff (and x=0) for a single set/clear.
; alternatively, one could set the "load-bearing instruction" to a
; "set pins, 0/1 side 1 [12]" for a fixed value useful for single sets/clears
PUBLIC sbw_tclk_burst:
set pindirs, 1 side 1 ; SBWTDIO is now output
; tms slot:
set pins, 0 side 1 ; stay in run-test/idle TAP state
nop side 0 ; target reads TMS at falling edge
mov pins, y side 0 ; during low phase, prepare TCLK
; tdi slot:
nop side 1 [6] ; wait a bit
burst_loop:
;PUBLIC sbw_tclk_burst_loadbearing_insn:
out pins, 1 side 1 [6] ; in the middle of TDI hiphase: do TCLK
jmp x--, burst_loop side 1 [6]
nop side 0 [1] ; need a low clock edge for TDO
; tdo slot:
set pindirs, 0 side 1 [1]
nop side 0 [1]
;jmp start side 1 ; not needed because of wrapping
; 32 insns -- filling one entire PIO instruction memory
; TODO: update this paragraph
; a full TCLK cycle in this burst mode takes 24 PIOSM cycles. at a "standard"
; baudrate of 18 MHz, this ends up being 375 kHz, which is in the required
; range when doing eg. flash programming. at the max baudrate (20 MHz), the
; TCLK speed is 417 kHz, which is still ok. max TCLK would be, if PIOSM freq is
; 125 MHz, 2.6 MHz, which is good enough imo
; TODO: determine minimum baudrate for stable TCLK (12 MHz?)
% c-sdk {
static inline void sbw_pio_init(PIO pio, uint sm, uint prog_offs,
float freq, uint pin_sbwclk, uint pin_sbwio) {
if (freq < 72e3) freq = 72e3;
if (freq > 20e6) freq = 20e6;
pio_sm_set_enabled(pio, sm, false);
pio_sm_config c = sbw_program_get_default_config(prog_offs);
sm_config_set_out_pins(&c, pin_sbwio, 1);
sm_config_set_set_pins(&c, pin_sbwio, 1);
sm_config_set_in_pins(&c, pin_sbwio);
sm_config_set_sideset_pins(&c, pin_sbwclk);
sm_config_set_out_shift(&c, false, true, 8);
sm_config_set_in_shift(&c, false, true, 8);
sm_config_set_clkdiv(&c, (float)clock_get_hz(clk_sys) / (4 * freq));
pio_sm_init(pio, sm, prog_offs, &c);
// SBWTDIO is low, SBWTCK is high, SBWTDIO is input by default
pio_sm_set_pindirs_with_mask(pio, sm,
(1u << pin_sbwclk) | (1u << pin_sbwio), (1u << pin_sbwclk) | (1u << pin_sbwio));
// need to do an extra set with sideset to make sure clk stays high and
// there's no glitch
pio_sm_exec(pio, sm, pio_encode_set(pio_pins, 1) | pio_encode_sideset(1, 1));
// SBW is mostly synchronous, bypass input synchroniser to reduce delay
hw_set_bits(&pio->input_sync_bypass, 1u << pin_sbwio);
gpio_set_pulls(pin_sbwio, false, true); // SBWTDIO is pulldn
pio_sm_set_enabled(pio, sm, true);
// set padsbank func to PIO *after* initing PIO, otherwise a glitch occurs
pio_gpio_init(pio, pin_sbwclk);
pio_gpio_init(pio, pin_sbwio );
}
// meant for sbw_seq/sbw_tms_seq
static inline void sbw_pio_set_baudrate(PIO pio, uint sm, float freq) {
if (freq < 72e3) freq = 72e3;
if (freq > 20e6) freq = 20e6;
pio_sm_set_clkdiv(pio, sm, (float)clock_get_hz(clk_sys) / (4 * freq));
}
// meant for sbw_tclk_burst
static inline void sbw_pio_set_tclkfreq(PIO pio, uint sm, float freq) {
if (freq < 250e3) freq = 250e3;
if (freq > 450e3) freq = 450e3;
pio_sm_set_clkdiv(pio, sm, (float)clock_get_hz(clk_sys) / (24 * freq));
}
static inline uint16_t sbw_pio_gen_setx(uint x) {
return pio_encode_set(pio_x, x) | pio_encode_sideset(1, 1) | (1<<12);
}
static inline uint16_t sbw_pio_gen_sety(uint y) {
return pio_encode_set(pio_y, y) | pio_encode_sideset(1, 1) | (1<<12);
}
static inline uint16_t sbw_pio_gen_outx(uint bits) {
return pio_encode_out(pio_x, bits) | pio_encode_sideset(1, 1) | (1<<12);
}
// subroutine is one of "sbw_offset_sbw_seq", "sbw_offset_sbw_tms_seq", "sbw_offset_sbw_tclk_burst"
static inline uint16_t sbw_pio_gen_jmp(uint subroutine) {
return pio_encode_jmp(subroutine) | pio_encode_sideset(1, 1) | (1<<12);
}
/*static inline uint16_t sbw_pio_loadbearing_gen_outpins(void) {
return pio_encode_out(pio_pins, 1) | pio_encode_sideset(1, 1) | pio_encode_delay(12) | (1<<12);
}
static inline uint16_t sbw_pio_loadbearing_gen_setpins(uint value) {
return pio_encode_set(pio_pins, value) | pio_encode_sideset(1, 1) | pio_encode_delay(12) | (1<<12);
}
static inline void sbw_pio_loadbearing_set_outpins(PIO pio) {
pio->instr_mem[sbw_offset_sbw_tclk_burst_loadbearing_insn] =
sbw_pio_loadbearing_gen_outpins();
}
static inline void sbw_pio_loadbearing_set_setpins(PIO pio, uint value) {
pio->instr_mem[sbw_offset_sbw_tclk_burst_loadbearing_insn] =
sbw_pio_loadbearing_gen_setpins(value);
}*/
/*static inline bool sbw_pio_is_idle(PIO pio, uint sm, uint offset) {
return pio_sm_get_pc(pio, sm) < sbw_offset_sbw_seq + offset;
}*/
%}

View File

@ -1,335 +0,0 @@
// vim: set et:
#include <stdio.h>
#include <hardware/clocks.h>
#include <hardware/dma.h>
#include <hardware/gpio.h>
#include <hardware/pio.h>
#include <hardware/pio_instructions.h>
#include <hardware/timer.h>
#include "m_isp/pinout.h"
#include "m_isp/sbw_hw.h"
#include "sbw.pio.h"
void sbw_preinit(bool nrst) {
//SLAU320AJ 2.3.1.1 ; SLAS722G p36:
// TEST/SBWTCK low for >100 us: reset debug state
// set nRST/NMI/SBWTDIO low: avoid sending an NMI when the debugger detaches
// TEST/SBWTCK high for >1 us : signal that we want a debugger
// nRST/NMI/SBWTDIO high: we want SBW
// TEST low for >0.025us <7us: latch on "we want SBW" signal
// TEST high again for >1 us: ready to SBW
// old impl, doesn't work, don't use
/*// TCK, TDIO now low
busy_wait_ms(4);//busy_wait_us_32(150); // reset debug state while keeping CPU in reset
gpio_put(PINOUT_SBW_TCK , true ); // we want a debugger
busy_wait_us_32(1);
gpio_put(PINOUT_SBW_TDIO, true ); // we want SBW
busy_wait_ms(20);//busy_wait_us_32(1);
gpio_put(PINOUT_SBW_TCK , false); // latch "we want SBW"
busy_wait_us_32(3);
gpio_put(PINOUT_SBW_TCK , true ); // start SBW stuff
busy_wait_ms(5);//busy_wait_us_32(100); // wait a bit more*/
#if 1
(void)nrst; // always assumed nrst=false here :/
// from slau320 sources
//gpio_put(PINOUT_SBW_TCK , false);
gpio_put(PINOUT_SBW_TDIO, true ); // FIXME: ummmm TCK ???
gpio_put(PINOUT_SBW_TDIO, false);
gpio_set_dir(PINOUT_SBW_TCK , true);
gpio_set_dir(PINOUT_SBW_TDIO, true);
gpio_set_function(PINOUT_SBW_TCK , GPIO_FUNC_SIO);
gpio_set_function(PINOUT_SBW_TDIO, GPIO_FUNC_SIO);
gpio_put(PINOUT_SBW_TCK , false);
gpio_put(PINOUT_SBW_TDIO, true);
busy_wait_ms(4); // reset TEST logic
gpio_put(PINOUT_SBW_TDIO, true);
busy_wait_us_32(1);
gpio_put(PINOUT_SBW_TCK , true);
busy_wait_ms(20); // activate TEST logic
// "phase 1"
gpio_put(PINOUT_SBW_TDIO, true);
busy_wait_us_32(60);
// "phase 2"
gpio_put(PINOUT_SBW_TCK , false);
// "phase 3"
busy_wait_us_32(1);
// "phase 4"
gpio_put(PINOUT_SBW_TCK , true);
busy_wait_us_32(60);
// "phase 5"
busy_wait_ms(5);
//// new impl:
#else
// from MSP430.DLL 'BIOS' (FETUIF?) sources
// can handle SBW/JTAG selection and nRST stuff
// ... but it doesn't seem to work
// TEST = TCK
// nRESET = TDIO = NMI
gpio_put(PINOUT_SBW_TCK , true/*false*/); // tck = test
gpio_put(PINOUT_SBW_TDIO, nrst/*true*/);
gpio_set_dir(PINOUT_SBW_TCK , true);
gpio_set_dir(PINOUT_SBW_TDIO, true);
gpio_set_function(PINOUT_SBW_TCK , GPIO_FUNC_SIO);
gpio_set_function(PINOUT_SBW_TDIO, GPIO_FUNC_SIO);
busy_wait_ms(4/*1*/); // 4?
gpio_put(PINOUT_SBW_TDIO, nrst);
busy_wait_us_32(1);
gpio_put(PINOUT_SBW_TCK , true);
// activate test logic
busy_wait_ms(20/*100*/); // 20 should be ok here I think?
// "phase 1"
gpio_put(PINOUT_SBW_TDIO, true); // false here if you want JTAG
busy_wait_us_32(40); // 60?
// "phase 2"
gpio_put(PINOUT_SBW_TCK, false); // ??? // true for JTAG?
// "phase 3"
// something (TDIO hi?) to do if RSTLOW & JTAG?
busy_wait_us_32(1);
// "phase 4"
gpio_put(PINOUT_SBW_TCK , true); // ??? // false for JTAG?
busy_wait_us_32(40/*60*/); // 40 should be ok here I think?
// phase 5
// something (TDIO hi?) to do if RSTHIGH & JTAG?
busy_wait_ms(5);
#endif
}
static int sbw_piosm = -1, sbw_offset = -1;
static bool last_tclk = true;
static uint8_t last_tdi = 0xff, last_tms = 0xff;
bool sbw_init(void) {
if (sbw_piosm >= 0 || sbw_offset >= 0) return false;
if (!pio_can_add_program(PINOUT_SBW_PIO, &sbw_program)) return false;
sbw_offset = pio_add_program(PINOUT_SBW_PIO, &sbw_program);
sbw_piosm = pio_claim_unused_sm(PINOUT_SBW_PIO, false);
if (sbw_piosm < 0) {
pio_remove_program(PINOUT_SBW_PIO, &sbw_program, sbw_offset);
sbw_offset = -1;
return false;
}
// need to start at 50 kHz: fuse check needs TMS cycles with a low phase
// of at least 5us. 50 kHz is below the required time (the actual maximum
// frequency would be around 80 kHz), but the exact frequency doesn't
// matter much as we'll switch to a higher one once the check has been
// completed
sbw_pio_init(PINOUT_SBW_PIO, sbw_piosm, sbw_offset, 50e3,
PINOUT_SBW_TCK, PINOUT_SBW_TDIO);
last_tdi = last_tms = 0xff;
last_tclk = true;
return true;
}
void sbw_deinit(void) {
if (sbw_piosm >= 0) {
pio_sm_set_enabled(PINOUT_SBW_PIO, sbw_piosm, false);
pio_sm_unclaim(PINOUT_SBW_PIO, sbw_piosm);
sbw_piosm = -1;
}
if (sbw_offset >= 0) {
pio_remove_program(PINOUT_SBW_PIO, &sbw_program, sbw_offset);
sbw_offset = -1;
}
}
void sbw_set_freq(bool tclk, float freq) {
if (tclk) {
sbw_pio_set_tclkfreq(PINOUT_SBW_PIO, sbw_piosm, freq);
} else {
sbw_pio_set_baudrate(PINOUT_SBW_PIO, sbw_piosm, freq);
}
}
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];
}
#define piosm_txf(width) (*(io_wo_##width *)&PINOUT_SBW_PIO->txf[sbw_piosm])
#define piosm_rxf(width) (*(io_ro_##width *)&PINOUT_SBW_PIO->rxf[sbw_piosm])
#define piosm_txf_wait() while (pio_sm_is_tx_fifo_full(PINOUT_SBW_PIO, sbw_piosm)) tight_loop_contents()
bool sbw_get_last_tms(void) { return last_tms; }
bool sbw_get_last_tdi(void) { return last_tdi; }
bool sbw_get_last_tclk(void) { return last_tclk; }
void sbw_sequence(uint32_t ncyc, bool tms, const uint8_t* tdi, uint8_t* tdo) {
if (ncyc == 0) return;
uint32_t nbytes = (ncyc + 7) >> 3;
uint32_t last_shift = (8 - ncyc) & 7;
uint32_t txremain = nbytes,
rxremain = last_shift ? nbytes : (nbytes + 1);
// initial TMS value in y
piosm_txf_wait();
piosm_txf(16) = sbw_pio_gen_sety(tms ? 1 : 0);
// number of cycles in x
piosm_txf_wait();
piosm_txf(16) = sbw_pio_gen_outx(32);
piosm_txf_wait();
piosm_txf(32) = ncyc - 1;
// jmp to correct subroutine
piosm_txf_wait();
piosm_txf(16) = sbw_pio_gen_jmp(sbw_offset_sbw_seq + sbw_offset);
for (size_t oi = 0, ii = 0; txremain || rxremain; tight_loop_contents()) {
if (txremain && !pio_sm_is_tx_fifo_full(PINOUT_SBW_PIO, sbw_piosm)) {
piosm_txf(8) = bitswap(tdi ? tdi[ii] : last_tdi);
--txremain;
++ii;
}
if (rxremain && !pio_sm_is_rx_fifo_empty(PINOUT_SBW_PIO, sbw_piosm)) {
uint8_t ov = piosm_rxf(8);
--rxremain;
if (tdo && oi < nbytes) {
if (last_shift && oi == nbytes - 1) {
tdo[oi] = bitswap(ov) >> last_shift;
} else {
tdo[oi] = bitswap(ov);
}
++oi;
}
}
}
//while (!sbw_pio_is_idle(PINOUT_SBW_PIO, sbw_piosm, sbw_offset)) tight_loop_contents();
if (tdi) last_tdi = (tdi[nbytes - 1] & (1 << (ncyc & 7))) ? 0xff : 0;
last_tms = tms ? 0xff : 0;
}
void sbw_tms_sequence(uint32_t ncyc, bool tdi, const uint8_t* tms) {
if (ncyc == 0 || !tms) return;
uint32_t nbytes = (ncyc + 7) >> 3;
uint32_t txremain = nbytes;
// initial TDI value in y
piosm_txf_wait();
piosm_txf(16) = sbw_pio_gen_sety(tdi ? 1 : 0);
// number of cycles in x
piosm_txf_wait();
piosm_txf(16) = sbw_pio_gen_outx(32);
piosm_txf_wait();
piosm_txf(32) = ncyc - 1;
// jmp to correct subroutine
piosm_txf_wait();
piosm_txf(16) = sbw_pio_gen_jmp(sbw_offset_sbw_tms_seq + sbw_offset);
for (size_t ii = 0; txremain; tight_loop_contents()) {
if (txremain && !pio_sm_is_tx_fifo_full(PINOUT_SBW_PIO, sbw_piosm)) {
piosm_txf(8) = bitswap(tms[ii]);
--txremain;
++ii;
}
}
//while (!sbw_pio_is_idle(PINOUT_SBW_PIO, sbw_piosm, sbw_offset)) tight_loop_contents();
last_tdi = tdi ? 0xff : 0;
last_tms = (tms[nbytes - 1] & (1 << (ncyc & 7))) ? 0xff : 0;
}
void sbw_clrset_tclk(bool value) {
//sbw_pio_loadbearing_set_setpins(PINOUT_SBW_PIO, value ? 1 : 0); // new value
// pre-TCLK value
piosm_txf_wait();
piosm_txf(16) = sbw_pio_gen_sety(last_tclk ? 1 : 0);
// only one TCLK
piosm_txf_wait();
piosm_txf(16) = sbw_pio_gen_setx(0);
// jmp to subroutine
piosm_txf_wait();
piosm_txf(16) = sbw_pio_gen_jmp(sbw_offset_sbw_tclk_burst + sbw_offset);
// always use out pins, 1
piosm_txf_wait();
piosm_txf(8) = value ? 0xff : 0;
// wait until done
/*while ( sbw_pio_is_idle(PINOUT_SBW_PIO, sbw_piosm, sbw_offset)) tight_loop_contents();
while (!sbw_pio_is_idle(PINOUT_SBW_PIO, sbw_piosm, sbw_offset)) tight_loop_contents();*/
last_tclk = value;
}
void sbw_tclk_burst(uint32_t ncyc) {
//sbw_pio_loadbearing_set_outpins(PINOUT_SBW_PIO);
uint32_t txremain = ((ncyc + 7) >> 3) * 2 - 1;
// MSB-first
uint8_t pattern = last_tclk ? 0x55 : 0xaa;
// pre-TCLK value
piosm_txf_wait();
piosm_txf(16) = sbw_pio_gen_sety(last_tclk ? 1 : 0);
// number of TCLK half-cycles in x
piosm_txf_wait();
piosm_txf(16) = sbw_pio_gen_outx(32);
piosm_txf_wait();
piosm_txf(32) = ncyc*2 - 1;
// jmp to subroutine
piosm_txf_wait();
piosm_txf(16) = sbw_pio_gen_jmp(sbw_offset_sbw_tclk_burst + sbw_offset);
for (; txremain; tight_loop_contents()) {
if (txremain && !pio_sm_is_tx_fifo_full(PINOUT_SBW_PIO, sbw_piosm)) {
piosm_txf(8) = pattern;
--txremain;
}
}
// wait until done
/*while ( sbw_pio_is_idle(PINOUT_SBW_PIO, sbw_piosm, sbw_offset)) tight_loop_contents();
while (!sbw_pio_is_idle(PINOUT_SBW_PIO, sbw_piosm, sbw_offset)) tight_loop_contents();*/
// last_tclk doesn't change - always an even number of TCLK half-cycles
}

View File

@ -1,38 +0,0 @@
#ifndef BSP_RP2040_SBW_HW_H
#define BSP_RP2040_SBW_HW_H
#include <stdint.h>
#include <stdbool.h>
/*#include <hardware/pio.h>*/
/*#define PINOUT_SBW_PIO pio0
#define PINOUT_SBW_TCK 8
#define PINOUT_SBW_TDIO 9*/
//extern int sbw_piosm, sbw_offset;
// does the debug handshake/SBW setup thingy, call before sbw_init()
void sbw_preinit(bool nrst);
bool sbw_init(void);
void sbw_deinit(void);
void sbw_set_freq(bool tclk, float freq);
bool sbw_get_last_tms(void);
bool sbw_get_last_tdi(void);
bool sbw_get_last_tclk(void);
void sbw_sequence(uint32_t ncyc, bool tms, const uint8_t* tdi, uint8_t* tdo);
void sbw_tms_sequence(uint32_t ncyc, bool tdi, const uint8_t* tms);
void sbw_clrset_tclk(bool tclk);
static inline void sbw_clr_tclk(void) { sbw_clrset_tclk(false); }
static inline void sbw_set_tclk(void) { sbw_clrset_tclk(true ); }
void sbw_tclk_burst(uint32_t ncyc);
#endif

52
bsp/rp2040/pico/assert.h Normal file
View File

@ -0,0 +1,52 @@
/*
* Copyright (c) 2020 Raspberry Pi (Trading) Ltd.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#ifndef _PICO_ASSERT_H
#define _PICO_ASSERT_H
#include <stdbool.h>
#ifdef __cplusplus
#include <cassert>
extern "C" {
#else
#include <assert.h>
#endif
// PICO_CONFIG: PARAM_ASSERTIONS_ENABLE_ALL, Global assert enable, type=bool, default=0, group=pico_base
// PICO_CONFIG: PARAM_ASSERTIONS_DISABLE_ALL, Global assert disable, type=bool, default=0, group=pico_base
#ifndef PARAM_ASSERTIONS_ENABLE_ALL
#define PARAM_ASSERTIONS_ENABLE_ALL 0
#endif
#ifndef PARAM_ASSERTIONS_DISABLE_ALL
#define PARAM_ASSERTIONS_DISABLE_ALL 0
#endif
#define PARAM_ASSERTIONS_ENABLED(x) ((PARAM_ASSERTIONS_ENABLED_ ## x || PARAM_ASSERTIONS_ENABLE_ALL) && !PARAM_ASSERTIONS_DISABLE_ALL)
#define invalid_params_if(x, test) ({if (PARAM_ASSERTIONS_ENABLED(x)) assert(!(test));})
#define valid_params_if(x, test) ({if (PARAM_ASSERTIONS_ENABLED(x)) assert(test);})
#define hard_assert_if(x, test) ({if (PARAM_ASSERTIONS_ENABLED(x)) hard_assert(!(test), #x);})
#ifdef NDEBUG
/*extern void hard_assertion_failure(void);
static inline void hard_assert(bool condition, ...) {
if (!condition)
hard_assertion_failure();
}*/
#define hard_assert(cond, ...) do { if (!(cond)) panic("Hard assert: " __FILE__); } while (0)
#else
#define hard_assert assert
#endif
#ifdef __cplusplus
}
#endif
#endif

View File

@ -52,6 +52,12 @@ def dpctl_do(args: Any) -> int:
return 1
return devcmds.sump_overclock_set(conn, oven)
def sto_info(conn, args):
return devcmds.storage_info(conn)
def sto_flush(conn, args):
return devcmds.storage_flush(conn)
def sto_get(conn, args):
return devcmds.storage_get(conn, args.mode)
#print(repr(args))
cmds = {
@ -59,6 +65,9 @@ def dpctl_do(args: Any) -> int:
'get-mode-info': get_mode_info,
'set-mode': set_mode,
'bootloader': bootloader,
'storage-info': sto_info,
'storage-flush': sto_flush,
'storage-get': sto_get,
'uart-cts-rts': uart_hw_flowctl,
'tempsensor': tempsensor,
@ -142,6 +151,15 @@ def main() -> int:
bootloader = subcmds.add_parser("bootloader", help="Set the device in bootloader mode")
# persistent storage commands
storagehdr = subcmds.add_parser("storage-info", help="Get persistent storage info")
storageflush = subcmds.add_parser("storage-flush", help="Flush persistent storage data to storage medium")
storageget = subcmds.add_parser("storage-get", help="Get data of a particular mode")
storageget.add_argument('mode', default=None, nargs='?',
help="Mode to get data of. Defaults to the current mode, 'all' means all modes.")
# mode 1 commands
usbhwfctl = subcmds.add_parser("uart-cts-rts", help="Get, enable/disable"+\
" UART hardware flow control")

View File

@ -10,7 +10,6 @@ from .protocol import *
FEATURES_OF_MODE = {
1: ["UART", "CMSIS-DAP", "SPI", "I2C", "temperature sensor", "1-wire"],
2: ["UART", "CMSIS-DAP", "MehFET"],
3: ["JTAG", "SWD"],
4: ["SUMP"]
}
@ -86,6 +85,56 @@ def set_mode(dev: DPDevice, mode: int) -> int:
# ---
def storage_info(dev: DPDevice) -> int:
try:
res = dev.storage_info()
print("magic: %s, version=%04x, current mode=%d, #modes=%d, DJB2(table)=%d" \
% (('OK' if res.magic == b'\xf0\x9f\x8f\xb3\xef\xb8\x8f\xe2\x80\x8d\xe2\x9a\xa7\xef\xb8\x8f' else 'BAD!'),
res.version, res.curmode, res.nmodes, res.table_djb2))
for md in res.mode_data:
print("\tmode %d version %04x: 0x%x..+0x%x, DJB2=%d" % \
(md.mode, md.version, md.offset, md.datasize, md.data_djb2))
if len(res.mode_data) == 0:
print("No mode data");
return 0
except Exception as e:
print("Could not get storage info: %s" % str(e))
traceback.print_exc()
return 1
def storage_flush(dev: DPDevice) -> int:
try:
res = dev.storage_flush()
print("storage saved" if res else "no write needed")
return 0
except Exception as e:
print("Could not flush persistent storage: %s" % str(e))
return 1
def storage_get(dev: DPDevice, mode: str) -> int:
try:
if mode == 'all':
for m in dev.mode_info.keys():
res = dev.storage_get(mode)
print(repr(res)) # TODO
return 0
elif mode is None:
mode = dev.current_mode
else: mode = int(mode,0)
res = dev.storage_get(mode)
print(repr(res)) # TODO
return 0
except Exception as e:
print("Could not get storage data of mode %d: %s" % (mode, str(e)))
return 1
# ---
def uart_hw_flowctl_get(dev: DPDevice) -> int:
try:
res = dev.m1_usb_hw_flowctl_get()

View File

@ -32,10 +32,12 @@ class UsbConn(DevConn):
import usb, usb.core
cfg = dev.get_active_configuration()
#print("get active", cfg)
if cfg is None: # should be configured already, but eh
dev.set_configuration()
cfg = dev.get_active_configuration()
#print("set active", cfg)
if cfg is None:
return "Couldn't get or set device configuration, aaaaa"
@ -45,6 +47,7 @@ class UsbConn(DevConn):
if i.bInterfaceClass == usb.CLASS_VENDOR_SPEC and
i.bInterfaceSubClass == UsbConn._SUBCLASS and
i.bInterfaceProtocol == UsbConn._PROTOCOL]
#print("vnd itf", itf)
if len(itf) == 0:
return "No vendor control interface found for device"
@ -58,12 +61,18 @@ class UsbConn(DevConn):
epin = usb.util.find_descriptor(itf, custom_match =
lambda e: usb.util.endpoint_direction(e.bEndpointAddress) == usb.util.ENDPOINT_IN)
#print("epout", epout, "epin", epin)
try:
# try to read the version number. if it throws, it means the usbdev
# is in use by something else
#print("write")
epout.write(b'\x00')
#print("wrote")
resp = epin.read(4)
except usb.core.USBError:
#print("resp", resp)
except usb.core.USBError as e:
print("eep", e)
return "Device is busy, already used by something else? (If you use "+\
"the kernel module, use a character device from /dev instead.)"
@ -77,6 +86,7 @@ class UsbConn(DevConn):
if verno > DevConn._VER_MAX:
return "Version of device (%04x) too new, must be max. %04x" \
% (hex(verno, DevConn._VER_MAX))
#print("verno", verno)
return UsbConn(dev, cfg, itf, epin, epout)
@ -89,6 +99,8 @@ class UsbConn(DevConn):
if dev is None or len(dev) != 1:
return None
#print("devs", dev)
rv = UsbConn._open_dev(dev[0])
return None if isinstance(rv, str) else rv
@ -127,6 +139,8 @@ class UsbConn(DevConn):
if conntup is None:
return "Could not open USB device '%s': not recognised" % conn
#print("conntup", conntup)
dev = None
if conn_busdev:
if len(conntup) == 2:
@ -148,9 +162,12 @@ class UsbConn(DevConn):
return UsbConn._open_dev(dev[0])
def read_raw(self, arr) -> int:
return self._epin.read(arr)
rv = self._epin.read(arr)
print("read", arr[:rv])
return rv
def write_raw(self, b: bytes) -> int:
print("write", b)
return self._epout.write(b)
def __init__(self, dev, cfg, itf, epin, epout):
@ -168,6 +185,7 @@ class UsbConn(DevConn):
usb.util.release_interface(self._dev, self._itf)
usb.util.dispose_resources(self._dev)
#print("released & disposed")
self._epout = None
self._epin = None
self._itf = None
@ -205,6 +223,7 @@ class ChardevConn(DevConn):
"a Linux kernel module" % sys.platform
try:
#print("try char", conn)
fd = os.open(conn, os.O_RDWR)
if fd < 0:
raise OSError("Negative file descriptor returned")

View File

@ -18,6 +18,56 @@ STAT_BADARG = 0x04
STAT_ILLSTATE = 0x05
class StorageInfoMode(NamedTuple):
version: int
datasize: int
offset: int
mode: int
data_djb2: int
def from_bytes(b: bytes) -> StorageInfoMode:
assert len(b) == 12
v, ds, oam, d = struct.unpack('<HHII', b)
return StorageInfoMode(v, ds, oam & ((1<<28)-1), (oam >> 28) & 15, d)
def list_from_bytes(b: bytes) -> List[StorageInfoMode]:
nelem = len(b) // 12
assert nelem * 12 == len(b)
r = [None]*nelem
for i in range(nelem): r[i] = StorageInfoMode.from_bytes(b[(i*12):((i+1)*12)])
return [re for re in r if re.version != 0xffff and re.datasize != 0xffff]
class StorageInfo(NamedTuple):
magic: bytes
version: int
curmode: int
nmodes: int
reserved: bytes
table_djb2: int
mode_data: List[StorageInfoMode]
def from_bytes(b: bytes) -> StorageInfo:
assert len(b) == 256
mag = b[:16]
ver, cm, nm = struct.unpack('<HBB', b[16:20])
res = b[20:28] + b[245-32:]
d2tab = struct.unpack('<I', b[28:32])[0]
mdat = StorageInfoMode.list_from_bytes(b[32:256-32])
assert len(mdat) == nm
return StorageInfo(mag, ver, cm, nm, res, d2tab, mdat)
def __str__(self):
return "StorageInfo(magic=%s, version=%d, curmode=%d, nmodes=%d, table_djb2=%d, mode_data=%s)" \
% (' '.join(hex(b) for b in self.magic), self.version,
self.curmode, self.nmodes, self.table_djb2, repr(self.mode_data))
class JtagMatch(NamedTuple):
tck: int
tms: int
@ -144,11 +194,11 @@ class DPDevice:
if (plen & 0x80) != 0:
plen &= 0x7f
plen |= self.read(1) << 7
plen |= self.read(1)[0] << 7
if (plen & 0x4000) != 0:
plen &= 0x3fff
plen |= self.read(1) << 14
plen |= self.read(1)[0] << 14
bs = self.read(plen)
#print("==> got resp %d res %s" % (resp, repr(bs)))
@ -236,6 +286,32 @@ class DPDevice:
return { i for i in range(0, 8) if (pl[0] & (1<<i)) != 0 }
# persistent storage
def storage_info(self) -> StorageInfo:
cmd = bytearray(b'\x0c')
self.write(b'\x0c')
stat, pl = self.read_resp()
check_statpl(stat, pl, "get storage info", 256, 256)
return StorageInfo.from_bytes(pl)
def storage_flush(self) -> bool:
self.write(b'\x0e')
stat, pl = self.read_resp()
check_statpl(stat, pl, "flush storage", 1, 1)
return pl[0]
def storage_get(self, mode: int) -> bytes:
cmd = bytearray(b'\x0d\x00')
cmd[1] = mode
self.write(cmd)
stat, pl = self.read_resp()
check_statpl(stat, pl, "get storage data", None, None)
return pl # TODO: parse
# mode 1 commands
def m1_usb_hw_flowctl_get(self) -> bool:

View File

@ -1,62 +0,0 @@
# This is a copy of <PICO_SDK_PATH>/external/pico_sdk_import.cmake
# This can be dropped into an external project to help locate this SDK
# It should be include()ed prior to project()
if (DEFINED ENV{PICO_SDK_PATH} AND (NOT PICO_SDK_PATH))
set(PICO_SDK_PATH $ENV{PICO_SDK_PATH})
message("Using PICO_SDK_PATH from environment ('${PICO_SDK_PATH}')")
endif ()
if (DEFINED ENV{PICO_SDK_FETCH_FROM_GIT} AND (NOT PICO_SDK_FETCH_FROM_GIT))
set(PICO_SDK_FETCH_FROM_GIT $ENV{PICO_SDK_FETCH_FROM_GIT})
message("Using PICO_SDK_FETCH_FROM_GIT from environment ('${PICO_SDK_FETCH_FROM_GIT}')")
endif ()
if (DEFINED ENV{PICO_SDK_FETCH_FROM_GIT_PATH} AND (NOT PICO_SDK_FETCH_FROM_GIT_PATH))
set(PICO_SDK_FETCH_FROM_GIT_PATH $ENV{PICO_SDK_FETCH_FROM_GIT_PATH})
message("Using PICO_SDK_FETCH_FROM_GIT_PATH from environment ('${PICO_SDK_FETCH_FROM_GIT_PATH}')")
endif ()
set(PICO_SDK_PATH "${PICO_SDK_PATH}" CACHE PATH "Path to the Raspberry Pi Pico SDK")
set(PICO_SDK_FETCH_FROM_GIT "${PICO_SDK_FETCH_FROM_GIT}" CACHE BOOL "Set to ON to fetch copy of SDK from git if not otherwise locatable")
set(PICO_SDK_FETCH_FROM_GIT_PATH "${PICO_SDK_FETCH_FROM_GIT_PATH}" CACHE FILEPATH "location to download SDK")
if (NOT PICO_SDK_PATH)
if (PICO_SDK_FETCH_FROM_GIT)
include(FetchContent)
set(FETCHCONTENT_BASE_DIR_SAVE ${FETCHCONTENT_BASE_DIR})
if (PICO_SDK_FETCH_FROM_GIT_PATH)
get_filename_component(FETCHCONTENT_BASE_DIR "${PICO_SDK_FETCH_FROM_GIT_PATH}" REALPATH BASE_DIR "${CMAKE_SOURCE_DIR}")
endif ()
FetchContent_Declare(
pico_sdk
GIT_REPOSITORY https://github.com/raspberrypi/pico-sdk
GIT_TAG master
)
if (NOT pico_sdk)
message("Downloading Raspberry Pi Pico SDK")
FetchContent_Populate(pico_sdk)
set(PICO_SDK_PATH ${pico_sdk_SOURCE_DIR})
endif ()
set(FETCHCONTENT_BASE_DIR ${FETCHCONTENT_BASE_DIR_SAVE})
else ()
message(FATAL_ERROR
"SDK location was not specified. Please set PICO_SDK_PATH or set PICO_SDK_FETCH_FROM_GIT to on to fetch from git."
)
endif ()
endif ()
get_filename_component(PICO_SDK_PATH "${PICO_SDK_PATH}" REALPATH BASE_DIR "${CMAKE_BINARY_DIR}")
if (NOT EXISTS ${PICO_SDK_PATH})
message(FATAL_ERROR "Directory '${PICO_SDK_PATH}' not found")
endif ()
set(PICO_SDK_INIT_CMAKE_FILE ${PICO_SDK_PATH}/pico_sdk_init.cmake)
if (NOT EXISTS ${PICO_SDK_INIT_CMAKE_FILE})
message(FATAL_ERROR "Directory '${PICO_SDK_PATH}' does not appear to contain the Raspberry Pi Pico SDK")
endif ()
set(PICO_SDK_PATH ${PICO_SDK_PATH} CACHE PATH "Path to the Raspberry Pi Pico SDK" FORCE)
include(${PICO_SDK_INIT_CMAKE_FILE})

2
src/.gitignore vendored
View File

@ -1 +1 @@
_*_test/
_storage_test/

View File

@ -3,6 +3,7 @@
#include <tusb.h>
#include "mode.h"
#include "storage.h"
#include "thread.h"
#include "usbstdio.h"
#include "vnd_cfg.h"
@ -21,10 +22,6 @@
/* temperature sensor */
#include "m_default/tempsensor.h"
// TODO: CMSIS-DAP USB bulk:
// * DAP_ExecuteCommand (returns response size)
// * interface: vendor, 0.0 subclass/proto, EP1, CMSIS-DAP in name
enum m_default_cmds {
mdef_cmd_spi = mode_cmd__specific,
mdef_cmd_i2c,
@ -39,13 +36,13 @@ enum m_default_feature {
mdef_feat_tempsense = 1<<4,
};
#ifdef DBOARD_HAS_UART
extern cothread_t m_def_uartthread;
extern uint8_t m_def_uartstack[THREAD_STACK_SIZE];
__attribute__((__weak__)) cothread_t m_def_uartthread;
__attribute__((__weak__)) uint8_t m_def_uartstack[THREAD_STACK_SIZE];
static bool data_dirty = false;
__attribute__((__weak__)) void m_def_uart_thread_fn(void) {
#ifdef DBOARD_HAS_UART
static cothread_t uartthread;
static uint8_t uartstack[THREAD_STACK_SIZE];
static void uart_thread_fn(void) {
cdc_uart_init();
thread_yield();
while (1) {
@ -53,9 +50,6 @@ __attribute__((__weak__)) void m_def_uart_thread_fn(void) {
thread_yield();
}
}
#define uartthread m_def_uartthread
#define uartstack m_def_uartstack
#define uart_thread_fn m_def_uart_thread_fn
#endif
#ifdef DBOARD_HAS_SPI
@ -90,6 +84,17 @@ static void enter_cb(void) {
serprogthread = co_derive(serprogstack, sizeof serprogstack, serprog_thread_fn);
thread_enter(serprogthread); // will call cdc_serprog_init() on correct thread
#endif
if (!data_dirty) { // only read when not read yet
struct mode_info mi = storage_mode_get_info(1);
if (mi.size != 0 && mi.version == 0x0010 /* TODO: version migration? */) {
uint8_t dst[2];
storage_mode_read(1, dst, 0, 2);
cdc_uart_set_hwflow(dst[0]);
tempsense_set_addr(dst[1]);
}
}
}
static void leave_cb(void) {
// TODO: CMSISDAP?
@ -104,41 +109,6 @@ static void leave_cb(void) {
#endif
}
void dap_do_bulk_stuff(int itf) {
// FIXME: move to a separate file, maybe
/*static*/ uint8_t rx_buf[DAP_PACKET_SIZE];
/*static*/ uint8_t tx_buf[DAP_PACKET_SIZE];
//static uint32_t rxpos = 0;
const uint32_t rxpos=0;
if (tud_vendor_n_mounted(itf) && tud_vendor_n_available(itf)) {
memset(rx_buf, 0, DAP_PACKET_SIZE);
uint32_t avail = tud_vendor_n_read(itf, &rx_buf[rxpos], sizeof rx_buf - rxpos);
uint32_t pos2 = rxpos + avail;
if (avail) {
memset(tx_buf, 0, DAP_PACKET_SIZE);
uint32_t res = DAP_ExecuteCommand(&rx_buf[rxpos], tx_buf);
uint16_t respcount = (uint16_t)res,
reqcount = (uint16_t)(res>>16);
//printf("avail=%04lx resp=%04x req=%04x rxpos=%lx\n", avail, respcount, reqcount, rxpos);
if (reqcount > pos2) { // command requires more data than available, so, welp
tx_buf[0] = rx_buf[0]; // something
tx_buf[1] = 0xff;
tud_vendor_n_write(itf, tx_buf, 2);
//rxpos=0;//pos2;
} else {
tud_vendor_n_write(itf, tx_buf, respcount);
//memmove(rx_buf, &rx_buf[rxpos+reqcount], DAP_PACKET_SIZE - reqcount);
//rxpos = 0;
}
//printf("->rxpos=%lx\n", rxpos);
}
}
}
static void task_cb(void) {
#ifdef DBOARD_HAS_UART
tud_task();
@ -148,8 +118,6 @@ static void task_cb(void) {
tud_task();
thread_enter(serprogthread);
#endif
dap_do_bulk_stuff(VND_N_CMSISDAP);
}
static void handle_cmd_cb(uint8_t cmd) {
@ -190,7 +158,12 @@ static void handle_cmd_cb(uint8_t cmd) {
break;
case mdef_cmd_tempsense:
#ifdef DBOARD_HAS_TEMPSENSOR
tempsense_bulk_cmd();
{
uint8_t addra = tempsense_get_addr();
tempsense_bulk_cmd();
uint8_t addrb = tempsense_get_addr();
data_dirty |= addra != addrb;
}
#else
vnd_cfg_write_str(cfg_resp_illcmd, "temperature sensor not implemented on this device");
#endif
@ -202,9 +175,10 @@ static void handle_cmd_cb(uint8_t cmd) {
resp = cdc_uart_get_hwflow() ? 1 : 0;
vnd_cfg_write_resp(cfg_resp_ok, 1, &resp);
} else {
if (cdc_uart_set_hwflow(resp != 0))
if (cdc_uart_set_hwflow(resp != 0)) {
vnd_cfg_write_resp(cfg_resp_ok, 0, NULL);
else
data_dirty = true;
} else
vnd_cfg_write_str(cfg_resp_illcmd, "UART flow control setting not supported on this device");
}
#else
@ -231,16 +205,12 @@ enum {
STRID_IF_VND_CFG,
STRID_IF_HID_CMSISDAP,
STRID_IF_VND_CMSISDAP,
STRID_IF_VND_I2CTINYUSB,
STRID_IF_CDC_UART,
STRID_IF_CDC_SERPROG,
STRID_IF_CDC_STDIO,
};
enum {
#ifdef DBOARD_HAS_CMSISDAP
ITF_NUM_VND_CMSISDAP,
#endif
#if CFG_TUD_VENDOR > 0
ITF_NUM_VND_CFG,
#endif
@ -275,7 +245,6 @@ enum {
+ TUD_I2CTINYUSB_LEN
#endif
#ifdef DBOARD_HAS_CMSISDAP
+ TUD_VENDOR_DESC_LEN
+ TUD_HID_INOUT_DESC_LEN
#endif
#ifdef DBOARD_HAS_UART
@ -289,20 +258,29 @@ enum {
#endif
};
#define EPNUM_VND_DAP_OUT 0x01
#define EPNUM_VND_DAP_IN 0x81
#define EPNUM_VND_CFG_OUT 0x02/*-1*/
#define EPNUM_VND_CFG_IN 0x82/*-1*/
#define EPNUM_HID_CMSISDAP 0x03/*-1*/
#define EPNUM_CDC_UART_OUT 0x04/*-1*/
#define EPNUM_CDC_UART_IN 0x84/*-1*/
#define EPNUM_CDC_UART_NOTIF 0x85/*-1*/
#define EPNUM_CDC_SERPROG_OUT 0x06/*-1*/
#define EPNUM_CDC_SERPROG_IN 0x86/*-1*/
#define EPNUM_CDC_SERPROG_NOTIF 0x87/*-1*/
#define EPNUM_CDC_STDIO_OUT 0x08/*-1*/
#define EPNUM_CDC_STDIO_IN 0x88/*-1*/
#define EPNUM_CDC_STDIO_NOTIF 0x89/*-1*/
#define EPNUM_VND_CFG_OUT 0x01
#define EPNUM_VND_CFG_IN 0x81
#define EPNUM_HID_CMSISDAP 0x02
#define EPNUM_CDC_UART_OUT 0x03
#define EPNUM_CDC_UART_IN 0x83
#define EPNUM_CDC_UART_NOTIF 0x84
#define EPNUM_CDC_SERPROG_OUT 0x05
#define EPNUM_CDC_SERPROG_IN 0x85
#define EPNUM_CDC_SERPROG_NOTIF 0x86
#define EPNUM_CDC_STDIO_OUT 0x07
#define EPNUM_CDC_STDIO_IN 0x87
#define EPNUM_CDC_STDIO_NOTIF 0x88
/*#define EPNUM_CDC_UART_OUT 0x02
#define EPNUM_CDC_UART_IN 0x82
#define EPNUM_CDC_UART_NOTIF 0x83
#define EPNUM_HID_CMSISDAP 0x04
#define EPNUM_CDC_SERPROG_OUT 0x05
#define EPNUM_CDC_SERPROG_IN 0x85
#define EPNUM_CDC_SERPROG_NOTIF 0x86
#define EPNUM_CDC_STDIO_OUT 0x07
#define EPNUM_CDC_STDIO_IN 0x87
#define EPNUM_CDC_STDIO_NOTIF 0x88*/
// clang-format off
#if CFG_TUD_HID > 0
@ -315,11 +293,6 @@ static const uint8_t desc_configuration[] = {
TUD_CONFIG_DESCRIPTOR(1, ITF_NUM__TOTAL, STRID_CONFIG, CONFIG_TOTAL_LEN,
TUSB_DESC_CONFIG_ATT_REMOTE_WAKEUP, 100),
#ifdef DBOARD_HAS_CMSISDAP
TUD_VENDOR_DESCRIPTOR_EX(ITF_NUM_VND_CMSISDAP, STRID_IF_VND_CMSISDAP, EPNUM_VND_DAP_OUT,
EPNUM_VND_DAP_IN, CFG_TUD_VENDOR_RX_BUFSIZE, 0, 0),
#endif
#if CFG_TUD_VENDOR > 0
TUD_VENDOR_DESCRIPTOR_EX(ITF_NUM_VND_CFG, STRID_IF_VND_CFG, EPNUM_VND_CFG_OUT,
EPNUM_VND_CFG_IN, CFG_TUD_VENDOR_RX_BUFSIZE, VND_CFG_SUBCLASS, VND_CFG_PROTOCOL),
@ -363,7 +336,6 @@ static const char* string_desc_arr[] = {
// max string length check: |||||||||||||||||||||||||||||||
[STRID_IF_VND_CFG ] = "Device cfg/ctl interface",
[STRID_IF_HID_CMSISDAP] = "CMSIS-DAP HID interface",
[STRID_IF_VND_CMSISDAP] = "CMSIS-DAP bulk interface",
[STRID_IF_VND_I2CTINYUSB] = "I2C-Tiny-USB interface",
[STRID_IF_CDC_UART] = "UART CDC interface",
[STRID_IF_CDC_SERPROG] = "Serprog CDC interface",
@ -400,7 +372,6 @@ static void my_hid_set_report_cb(uint8_t instance, uint8_t report_id,
(void)report_id;
(void)report_type;
memset(tx_buffer, 0, CFG_TUD_HID_EP_BUFSIZE);
DAP_ProcessCommand(rx_buffer, tx_buffer);
tud_hid_report(0, tx_buffer, response_size);
@ -436,6 +407,19 @@ static bool my_vendor_control_xfer_cb(uint8_t rhport, uint8_t ep_addr,
}
#endif
static uint16_t my_get_size(void) { return 2; }
static void my_get_data(void* dst, size_t offset, size_t maxsize) {
(void)offset; (void)maxsize;
uint8_t* d = dst;
d[0] = cdc_uart_get_hwflow() ? 1 : 0;
d[1] = tempsense_get_addr();
data_dirty = false;
}
static bool my_is_dirty(void) { return data_dirty; }
extern struct mode m_01_default;
// clang-format off
struct mode m_01_default = {
@ -451,6 +435,13 @@ struct mode m_01_default = {
.task = task_cb,
.handle_cmd = handle_cmd_cb,
.storage = {
.stclass = mode_storage_32b,
.get_size = my_get_size,
.get_data = my_get_data,
.is_dirty = my_is_dirty
},
#if defined(DBOARD_HAS_CMSISDAP) && CFG_TUD_HID > 0
#if 0
.tud_hid_get_report_cb = my_hid_get_report_cb,
@ -469,16 +460,3 @@ struct mode m_01_default = {
};
// clang-format on
#if defined(DBOARD_HAS_UART)
// FIXME: use mode-specific callback here?
void tud_cdc_line_state_cb(uint8_t itf, bool dtr, bool rts) {
(void)dtr;
if (mode_current != &m_01_default) return;
if (itf == CDC_N_UART) {
cdc_uart_set_hwflow(rts);
}
}
#endif

View File

@ -2,7 +2,7 @@
#ifndef CDC_H_
#define CDC_H_
/*#include "m_default/bsp-feature.h"*/
#include "m_default/bsp-feature.h"
/* BSP function prototypes for various USB-CDC interfaces */
#ifdef DBOARD_HAS_UART

View File

@ -2,7 +2,6 @@
#include <stdio.h>
#include "tusb_config.h"
#include <tusb.h>
#include "m_default/bsp-feature.h"
@ -58,7 +57,6 @@ uint32_t sp_spi_get_buf_limit(void) {
return sizeof(rx_buf) - 1;
}
// TODO: this is duplicated several times over the codebase, maybe reduce this
static uint8_t read_byte_cdc(void) {
while (rxavail <= 0) {
if (!tud_cdc_n_connected(CDC_N_SERPROG) || !tud_cdc_n_available(CDC_N_SERPROG)) {

View File

@ -11,7 +11,6 @@
#include <hardware/i2c.h>
#include "tusb_config.h"
#include <tusb.h>
#include <device/usbd_pvt.h>

265
src/m_ftdi/_ftdi.c Normal file
View File

@ -0,0 +1,265 @@
// vim: set et:
#include "tusb_config.h"
#include <tusb.h>
#include "mode.h"
#include "thread.h"
#include "usbstdio.h"
#include "vnd_cfg.h"
#include "m_ftdi/bsp-feature.h"
#include "m_ftdi/ftdi.h"
static bool data_dirty = false;
void ftdi_eeprom_dirty_set(bool v) { (void)v; data_dirty = true; }
bool ftdi_eeprom_dirty_get(void) { return data_dirty; }
#ifdef DBOARD_HAS_FTDI
static cothread_t ftdithread_ifa, ftdithread_ifb;
static uint8_t ftdistack_ifa[THREAD_STACK_SIZE>>1], ftdistack_ifb[THREAD_STACK_SIZE>>1];
static void ftdi_thread_fn_ifa(void) {
while (1) {
printf("fn ifa thread!\n");
//ftdi_task_ifa();
thread_yield();
}
}
static void ftdi_thread_fn_ifb(void) {
while (1) {
printf("fn ifb thread!\n");
//ftdi_task_ifb();
thread_yield();
}
}
#endif
static void enter_cb(void) {
printf("mode5 enter begin VND_N_CONFIG=%d\n", VND_N_CFG);
#ifdef USE_USBCDC_FOR_STDIO
stdio_usb_set_itf_num(CDC_N_STDIO);
#endif
//vnd_cfg_set_itf_num(VND_N_CFG);
#ifdef DBOARD_HAS_FTDI
/*ftdithread_ifa = co_derive(ftdistack_ifa, sizeof ftdistack_ifa, ftdi_thread_fn_ifa);
ftdithread_ifb = co_derive(ftdistack_ifb, sizeof ftdistack_ifb, ftdi_thread_fn_ifb);*/
#endif
/*if (!data_dirty) {
struct mode_info mi = storage_mode_get_info(5);
if (mi.size != 0 && mi.version == 0x0010) {
storage_mode_read(5, ftdi_eeprom, 0, sizeof ftdi_eeprom);
}
}*/
//ftdi_init();
printf("mode5 enter end\n");
}
static void leave_cb(void) {
printf("mode5 leave\n");
#ifdef DBOARD_HAS_FTDI
//ftdi_deinit();
#endif
}
static void task_cb(void) {
printf("mode5 task\n");
#ifdef DBOARD_HAS_FTDI
//tud_task();
//thread_enter(ftdithread_ifa);
//tud_task();
//thread_enter(ftdithread_ifb);
#endif
}
static void handle_cmd_cb(uint8_t cmd) {
printf("mode5 handlecmd %02x\n", cmd);
uint8_t resp = 0;
switch (cmd) {
case mode_cmd_get_features:
resp = 0; // TODO: what do we put here?
vnd_cfg_write_resp(cfg_resp_ok, 1, &resp);
break;
default:
vnd_cfg_write_strf(cfg_resp_illcmd, "unknown mode5 command %02x", cmd);
break;
}
}
enum {
STRID_LANGID = 0,
STRID_MANUFACTURER,
STRID_PRODUCT,
STRID_SERIAL,
STRID_CONFIG,
STRID_IF_VND_CFG,
STRID_IF_VND_FTDI_IFA,
STRID_IF_VND_FTDI_IFB,
STRID_IF_CDC_STDIO,
};
enum {
/*ITF_NUM_VND_FTDI_IFA,
ITF_NUM_VND_FTDI_IFB,*/
#if CFG_TUD_VENDOR > 0
ITF_NUM_VND_CFG,
#endif
#ifdef USE_USBCDC_FOR_STDIO
ITF_NUM_CDC_STDIO_COM,
ITF_NUM_CDC_STDIO_DATA,
#endif
ITF_NUM__TOTAL
};
enum {
CONFIG_TOTAL_LEN
= TUD_CONFIG_DESC_LEN
/*+ TUD_VENDOR_DESC_LEN
+ TUD_VENDOR_DESC_LEN*/
#if CFG_TUD_VENDOR > 0
+ TUD_VENDOR_DESC_LEN
#endif
#ifdef USE_USBCDC_FOR_STDIO
+ TUD_CDC_DESC_LEN
#endif
};
/*#define EPNUM_VND_FTDI_IFA_OUT 0x02
#define EPNUM_VND_FTDI_IFA_IN 0x81
#define EPNUM_VND_FTDI_IFB_OUT 0x04
#define EPNUM_VND_FTDI_IFB_IN 0x83
#define EPNUM_VND_CFG_OUT 0x05
#define EPNUM_VND_CFG_IN 0x85
#define EPNUM_CDC_STDIO_OUT 0x06
#define EPNUM_CDC_STDIO_IN 0x86
#define EPNUM_CDC_STDIO_NOTIF 0x87*/
#define EPNUM_VND_CFG_OUT 0x01
#define EPNUM_VND_CFG_IN 0x81
#define EPNUM_CDC_STDIO_OUT 0x02
#define EPNUM_CDC_STDIO_IN 0x82
#define EPNUM_CDC_STDIO_NOTIF 0x83
// clang-format off
static const uint8_t desc_configuration[] = {
TUD_CONFIG_DESCRIPTOR(1, ITF_NUM__TOTAL, STRID_CONFIG, CONFIG_TOTAL_LEN,
TUSB_DESC_CONFIG_ATT_REMOTE_WAKEUP, 100),
/*TUD_VENDOR_DESCRIPTOR(ITF_NUM_VND_FTDI_IFA, STRID_IF_VND_FTDI_IFA,
EPNUM_VND_FTDI_IFA_OUT, EPNUM_VND_FTDI_IFA_IN, CFG_TUD_VENDOR_RX_BUFSIZE),
TUD_VENDOR_DESCRIPTOR(ITF_NUM_VND_FTDI_IFB, STRID_IF_VND_FTDI_IFB,
EPNUM_VND_FTDI_IFB_OUT, EPNUM_VND_FTDI_IFB_IN, CFG_TUD_VENDOR_RX_BUFSIZE),*/
#if CFG_TUD_VENDOR > 0
TUD_VENDOR_DESCRIPTOR_EX(ITF_NUM_VND_CFG, STRID_IF_VND_CFG, EPNUM_VND_CFG_OUT,
EPNUM_VND_CFG_IN, CFG_TUD_VENDOR_RX_BUFSIZE, VND_CFG_SUBCLASS, VND_CFG_PROTOCOL),
#endif
#ifdef USE_USBCDC_FOR_STDIO
TUD_CDC_DESCRIPTOR(ITF_NUM_CDC_STDIO_COM, STRID_IF_CDC_STDIO, EPNUM_CDC_STDIO_NOTIF,
CFG_TUD_CDC_RX_BUFSIZE, EPNUM_CDC_STDIO_OUT, EPNUM_CDC_STDIO_IN, CFG_TUD_CDC_RX_BUFSIZE),
#endif
};
static const char* string_desc_arr[] = {
NULL,
[STRID_CONFIG] = "Configuration descriptor",
// max string length check: |||||||||||||||||||||||||||||||
[STRID_IF_VND_CFG ] = "Device cfg/ctl interface",
[STRID_IF_VND_FTDI_IFA] = "DragonProbe FT2232D interface A",
[STRID_IF_VND_FTDI_IFB] = "DragonProbe FT2232D interface B",
#ifdef USE_USBCDC_FOR_STDIO
[STRID_IF_CDC_STDIO] = "stdio CDC interface (debug)",
#endif
};
// clang-format on
static tusb_desc_device_t desc_device = {
.bLength = sizeof(tusb_desc_device_t),
.bDescriptorType = TUSB_DESC_DEVICE,
.bcdUSB = 0x0110, // TODO: 0x0200 ? is an eeprom option
.bDeviceClass = 0x00,
.bDeviceSubClass = 0x00,
.bDeviceProtocol = 0x00,
.bMaxPacketSize0 = CFG_TUD_ENDPOINT0_SIZE,
.idVendor = 0x0403, // ?
.idProduct = 0x6010, // ?
.bcdDevice = 0x0500, // required!
.iManufacturer = STRID_MANUFACTURER,
.iProduct = STRID_PRODUCT,
.iSerialNumber = STRID_SERIAL,
.bNumConfigurations = 0x01
};
static const uint8_t* my_descriptor_device_cb(void) {
return (const uint8_t*)&desc_device;
}
#if CFG_TUD_CDC > 0
static void my_cdc_line_coding_cb(uint8_t itf, cdc_line_coding_t const* line_coding) {
printf("mode5 linecoding %02x, %lu\n", itf, line_coding->bit_rate);
switch (itf) {
#ifdef USE_USBCDC_FOR_STDIO
case CDC_N_STDIO:
stdio_usb_line_coding_cb(line_coding);
break;
#endif
}
}
#endif
static uint16_t my_get_size(void) { return sizeof ftdi_eeprom; }
static void my_get_data(void* dst, size_t offset, size_t maxsize) {
memcpy(dst, (const uint8_t*)ftdi_eeprom + offset, maxsize);
data_dirty = false;
}
static bool my_is_dirty(void) { return data_dirty; }
extern struct mode m_05_ftdi;
// clang-format off
struct mode m_05_ftdi = {
.name = "FTDI FT2232D emulation mode",
.version = 0x0010,
.n_string_desc = sizeof(string_desc_arr)/sizeof(string_desc_arr[0]),
.usb_desc = desc_configuration,
.string_desc = string_desc_arr,
.enter = enter_cb,
.leave = leave_cb,
.task = task_cb,
.handle_cmd = handle_cmd_cb,
/*.storage = {
.stclass = mode_storage_512b,
.get_size = my_get_size,
.get_data = my_get_data,
.is_dirty = my_is_dirty
},*/
#ifdef DBOARD_HAS_FTDI
//.tud_descriptor_device_cb = my_descriptor_device_cb,
#endif
#if CFG_TUD_CDC > 0
.tud_cdc_line_coding_cb = my_cdc_line_coding_cb,
#endif
#ifdef DBOARD_HAS_FTDI
//.tud_vendor_control_xfer_cb = ftdi_control_xfer_cb,
#endif
};
// clang-format on

486
src/m_ftdi/ftdi.c Normal file
View File

@ -0,0 +1,486 @@
// vim: set et:
#include "tusb_config.h"
#include <tusb.h>
#include "thread.h"
#include "m_ftdi/bsp-feature.h"
#include "m_ftdi/ftdi.h"
// index = interface number (A/B)
// out/in bRequest wValue wIndex data wLength
// RESET: out 0 0 index
// TCIFLUSH: out 0 2 index
// TCOFLUSH: out 0 1 index
// SETMODEMCTRL:out 1 (mask:8<<8 | data:8) // bit0=dtr bit1=rts
// SETFLOWCTRL: out 2 xon?1:0 (flowctrl | index) // flowctrl: 0=disable, 1=ctsrts, 2=dtrdsr 4=xonxoff FROM VALUE, not set/reset!
// SETBAUDRATE: out 3 brate brate24|index // 48 MHz clocks, /16? , baudrate is 24bit, highest byte in index MSB
// SETLINEPROP: out 4 (break:1<<14 | stop:2<<11 | parity:3<<8 | bits:8) ; break: off/on, stop=1/15/2, parity=none/odd/even/mark/space; bits=7/8
// POLLMODEMSTAT:in 5 0 index &modemstat len=2 // first byte: bit0..3=0 bit4=cts bit5=dts bit6=ri bit7=rlsd ; second byte: bit0=dr bit1=oe bit2=pe bit3=fe bit4=bi bit5=thre bit6=temt bit7=fifoerr
// SETEVENTCHAR:out 6 (endis:1<<8 | char)
// SETERRORCHAR:out 7 (endir:1<<8 | char)
// <there is no bReqest 8>
// SETLATENCY: out 9 latency(1..255)
// GETLATENCY: in 0xa 0 index &latency len=1
// SETBITBANG: out 0xb dirmask:8<<8 | mode:8 // mode: MPSSE mode: 0=serial/fifo, 1=bitbang, 2=mpsse, 4=syncbb, 8=mcu, 16=opto
// READPINS: in 0xc 0 index &pins len=1
// READEEPROM: in 0x90 0 eepaddr &val 2 // eepaddr in 16-bit units(?)
// WRITEEEPROM: out 0x91 value eepaddr
// ERASEEEPROM: out 0x92 0 0
// eeprom layout:
// max size is 128 words (256 bytes), little-endian
// 00: type, driver, .. stuff (chanA byte, chanB byte) -> TODO: what does this mean exactly?
// 0=UART 1=FIFO 2=opto 4=CPUFIFO 8=FT1284
// 01: VID
// 02: PID
// 03: bcdDevice
// 04: byte08: flags: bit7=1 bit6=1=selfpowered bit5=1=remotewakeup ; byte09=max power, 2mA units
// 05: bit0=epin isochr, bit1=epout isochr, bit2=suspend pulldn, bit3=serialno use, bit4=usbver change, bit5..7=0
// 06: bcdUSB
// 07: byte0E=manufstr.off-0x80, byte0F=manufstr.len (in bytes, but 16-bit ascii)
// 08: ^ but product
// 09: ^ but serial
// 0a: chip type (66 etc -> we emulate '00', internal)
// "strings start at 0x96 for ft2232c" (byte addr)
// checksum:
// initial: 0xaaaa
// digest 1 byte:
// checksum = (checksum ^ word) <<< 1;
// placed at last word of EEPROM
// ftdi_write_data(), ftdi_read_data(): bulk xfer
// input/tristate: 200K pullup
// SI/WU: let's ignore this
// UART mode: standard stuff
// FIFO mode: RDF#=0: enable output. RD# rising when RXF#=0: fetch next byte
// TXF#=0: enable input. WR falling when TXF#=0: write byte
// bitbang mode: ^ similar, no RDF#/TXF#, RD#/WR# (now WR# jenai WR) pos depends on UART mode std (UART vs FIFO)
// sync bitbang: doesn't use RD#/WR#, clocked by baudrate control
// MPSSE: lots of magic. TCK/SK, TDI/DO, TDO/DI, TMS/CS, GPIOL0..3, GPIOH0..3(7?)
// bulk xfer formats: (interface index <-> bulk epno)
// * UART: ok I guess
// * FIFO: same
// * bitbang, sync bitbang: ???? (just data or also clock stuff?? ?)
// * MPSSE, MCU: see separate PDF
// * CPUFIFIO: same as FIFO ig
// * opto, FT1284: not supported
#ifdef DBOARD_HAS_FTDI
int __builtin_ctz(unsigned int v);
uint16_t ftdi_eeprom[128];
struct ftdi_interface ftdi_ifa, ftdi_ifb;
void ftdi_init(void) {
// init eeprom defaults
memset(ftdi_eeprom, 0xff, sizeof ftdi_eeprom);
ftdi_eeprom[ 0] = 0x0000; // both default to UART
ftdi_eeprom[ 1] = 0x0403; // idVendor
ftdi_eeprom[ 2] = 0x6010; // idProduct
ftdi_eeprom[ 3] = 0x0500; // bcdDevice
ftdi_eeprom[ 4] = 0x5080; // 100 mA, no flags
ftdi_eeprom[ 5] = 0x0000; // more flags
ftdi_eeprom[ 6] = 0x0110; // bcdUSB
ftdi_eeprom[ 7] = 0x0000; // no manuf. str (TODO?)
ftdi_eeprom[ 8] = 0x0000; // no prod. str (TODO?)
ftdi_eeprom[ 9] = 0x0000; // no serial str (TODO?)
ftdi_eeprom[10] = 0x0000; // internal chip
ftdi_eeprom[0x7f] = ftdi_eeprom_checksum_calc(ftdi_eeprom, 0x7f);
memset(&ftdi_ifa, 0, sizeof ftdi_ifa);
memset(&ftdi_ifb, 0, sizeof ftdi_ifb);
ftdi_ifa.lineprop = sio_bits_8 | sio_stop_1; // 8n1
ftdi_ifb.lineprop = sio_bits_8 | sio_stop_1; // 8n1
ftdi_ifa.index = 0;
ftdi_ifb.index = 1;
ftdi_ifa.modemstat = sio_modem_temt;
ftdi_ifb.modemstat = sio_modem_temt;
ftdi_if_init(&ftdi_ifa);
ftdi_if_init(&ftdi_ifb);
}
void ftdi_deinit(void) {
ftdi_if_deinit(&ftdi_ifa);
ftdi_if_deinit(&ftdi_ifb);
}
static uint8_t vnd_read_byte(struct ftdi_interface* itf, int itfnum) {
while (itf->rxavail <= 0) {
if (!tud_vendor_n_mounted(itfnum) || !tud_vendor_n_available(itfnum)) {
thread_yield();
continue;
}
itf->rxpos = 0;
itf->rxavail = tud_vendor_n_read(itfnum, itf->bufbuf, sizeof itf->bufbuf);
if (itf->rxavail == 0) thread_yield();
}
uint8_t rv = itf->bufbuf[itf->rxpos];
++itf->rxpos;
--itf->rxavail;
return rv;
}
typedef void (*ftfifo_write_fn)(struct ftdi_interface*, const uint8_t*, size_t);
typedef size_t (*ftfifo_read_fn)(struct ftdi_interface*, uint8_t*, size_t);
struct ftfifo_fns {
ftfifo_write_fn write;
ftfifo_read_fn read ;
};
static const struct ftfifo_fns fifocbs[] = {
{ ftdi_if_uart_write, ftdi_if_uart_read }, // technically mpsse
{ ftdi_if_asyncbb_write, ftdi_if_asyncbb_read },
{ ftdi_if_syncbb_write, ftdi_if_syncbb_read },
{ NULL, NULL }, // mcuhost
{ ftdi_if_fifo_write, ftdi_if_fifo_read },
{ NULL, NULL }, // opto
{ ftdi_if_cpufifo_write, ftdi_if_cpufifo_read },
{ NULL, NULL }, // ft1284
};
// for handling USB bulk commands
static void ftdi_task(int itfnum) {
if (!tud_vendor_n_mounted(itfnum)) return; // can't do much in this case
struct ftdi_interface* itf;
if (itfnum == VND_N_FTDI_IFA) itf = &ftdi_ifa;
else if (itfnum == VND_N_FTDI_IFB) itf = &ftdi_ifb;
else return;
// for UART, FIFO, asyncbb, syncbb, and CPUfifo modes, operation is
// relatively straightforward: the device acts like some sort of FIFO, so
// it's just shoving bytes to the output pins. MPSSE and MCU host emulation
// modes are more difficult, as the bulk data actually has some form of
// protocol.
enum ftdi_mode mode = ftdi_if_get_mode(itf);
struct ftfifo_fns fifocb = fifocbs[(mode == 0) ? 0 : __builtin_ctz(mode)];
uint32_t avail;
uint8_t cmdbyte = 0;
switch (mode) {
case ftmode_uart : case ftmode_fifo : case ftmode_cpufifo:
case ftmode_asyncbb: case ftmode_syncbb:
if (fifocb.read == NULL || fifocb.write == NULL) goto CASE_DEFAULT; // welp
avail = tud_vendor_n_available(itfnum);
if (avail) {
tud_vendor_n_read(itfnum, itf->writebuf, avail);
fifocb.write(itf, itf->writebuf, avail);
}
do {
avail = fifocb.read(itf, itf->readbuf, sizeof itf->readbuf);
if (avail) tud_vendor_n_write(itfnum, itf->readbuf, avail);
} while (avail == sizeof itf->readbuf);
break;
case ftmode_mpsse:
avail = 0;
switch ((cmdbyte = vnd_read_byte(itf, itfnum))) {
case ftmpsse_set_dirval_lo: // low byte of output gpio, not to low level
itf->writebuf[0] = vnd_read_byte(itf, itfnum); // val
itf->writebuf[1] = vnd_read_byte(itf, itfnum); // dir
ftdi_if_mpsse_set_dirval_lo(itf, itf->writebuf[1], itf->writebuf[0]);
break;
case ftmpsse_set_dirval_hi:
itf->writebuf[0] = vnd_read_byte(itf, itfnum); // val
itf->writebuf[1] = vnd_read_byte(itf, itfnum); // dir
ftdi_if_mpsse_set_dirval_hi(itf, itf->writebuf[1], itf->writebuf[0]);
break;
case ftmpsse_read_lo:
itf->readbuf[0] = ftdi_if_mpsse_read_lo(itf);
avail = 1;
break;
case ftmpsse_read_hi:
itf->readbuf[0] = ftdi_if_mpsse_read_hi(itf);
avail = 1;
break;
case ftmpsse_loopback_on : ftdi_if_mpsse_loopback(itf, true ); break;
case ftmpsse_loopback_off: ftdi_if_mpsse_loopback(itf, false); break;
case ftmpsse_set_clkdiv:
avail = vnd_read_byte(itf, itfnum);
avail |= (uint32_t)vnd_read_byte(itf, itfnum) << 8;
ftdi_if_mpsse_set_clkdiv(itf, (uint16_t)avail);
avail = 0;
break;
case ftmpsse_flush: ftdi_if_mpsse_flush(itf); break;
case ftmpsse_wait_io_hi: ftdi_if_mpsse_wait_io(itf, true ); break;
case ftmpsse_wait_io_lo: ftdi_if_mpsse_wait_io(itf, false); break;
case ftmpsse_div5_disable: ftdi_if_mpsse_div5(itf, false); break;
case ftmpsse_div5_enable : ftdi_if_mpsse_div5(itf, true ); break;
case ftmpsse_data_3ph_en : ftdi_if_mpsse_data_3ph(itf, true ); break;
case ftmpsse_data_3ph_dis: ftdi_if_mpsse_data_3ph(itf, false); break;
case ftmpsse_clockonly_bits: ftdi_if_mpsse_clockonly(itf, vnd_read_byte(itf, itfnum)); break;
case ftmpsse_clockonly_bytes:
avail = vnd_read_byte(itf, itfnum);
avail |= (uint32_t)vnd_read_byte(itf, itfnum) << 8;
ftdi_if_mpsse_clockonly(itf, avail);
avail = 0;
break;
case ftmpsse_clock_wait_io_hi: ftdi_if_mpsse_clock_wait_io(itf, true ); break;
case ftmpsse_clock_wait_io_lo: ftdi_if_mpsse_clock_wait_io(itf, false); break;
case ftmpsse_adapclk_enable : ftdi_if_mpsse_adaptive(itf, true ); break;
case ftmpsse_adapclk_disable: ftdi_if_mpsse_adaptive(itf, false); break;
case ftmpsse_clock_bits_wait_io_hi:
avail = vnd_read_byte(itf, itfnum);
avail |= (uint32_t)vnd_read_byte(itf, itfnum) << 8;
ftdi_if_mpsse_clockonly_wait_io(itf, true, avail);
avail = 0;
break;
case ftmpsse_clock_bits_wait_io_lo:
avail = vnd_read_byte(itf, itfnum);
avail |= (uint32_t)vnd_read_byte(itf, itfnum) << 8;
ftdi_if_mpsse_clockonly_wait_io(itf, false, avail);
avail = 0;
break;
case ftmpsse_hi_is_tristate:
avail = vnd_read_byte(itf, itfnum);
avail |= (uint32_t)vnd_read_byte(itf, itfnum) << 8;
ftdi_if_mpsse_hi_is_tristate(itf, avail);
avail = 0;
break;
default: CASE_DEFAULT:
if (!(cmdbyte & ftmpsse_specialcmd)) {
if (cmdbyte & ftmpsse_tmswrite) {
if (cmdbyte & ftmpsse_bitmode) {
itf->writebuf[0] = vnd_read_byte(itf, itfnum); // number of bits
itf->writebuf[1] = vnd_read_byte(itf, itfnum); // data bits to output
itf->readbuf[0] = ftdi_if_mpsse_tms_xfer(itf, cmdbyte, itf->writebuf[0], itf->writebuf[1]);
if (cmdbyte & ftmpsse_tdoread) avail = 1;
break;
}
// else: fallthru to error code
} else {
if (cmdbyte & ftmpsse_bitmode) {
itf->writebuf[0] = vnd_read_byte(itf, itfnum); // number of bits
if (cmdbyte & ftmpsse_tdiwrite)
itf->writebuf[1] = vnd_read_byte(itf, itfnum); // data bits to output
itf->readbuf[0] = ftdi_if_mpsse_xfer_bits(itf, cmdbyte, itf->writebuf[0], itf->writebuf[1]);
if (cmdbyte & ftmpsse_tdoread) avail = 1;
break;
} else {
avail = vnd_read_byte(itf, itfnum);
avail |= (uint32_t)vnd_read_byte(itf, itfnum) << 8;
for (size_t i = 0; i < avail; i += 64) {
uint32_t thisbatch = avail - i;
if (thisbatch > 64) thisbatch = 64;
for (size_t j = 0; j < thisbatch; ++j)
itf->writebuf[j] = vnd_read_byte(itf, itfnum);
ftdi_if_mpsse_xfer_bytes(itf, cmdbyte, thisbatch, itf->readbuf, itf->writebuf);
tud_vendor_n_write(itfnum, itf->readbuf, thisbatch);
}
avail = 0;
break;
}
}
}
itf->readbuf[0] = 0xfa;
itf->readbuf[1] = cmdbyte;
avail = 2;
break;
}
if (avail) tud_vendor_n_write(itfnum, itf->readbuf, avail);
break;
case ftmode_mcuhost:
avail = 0;
switch ((cmdbyte = vnd_read_byte(itf, itfnum))) {
case ftmcu_flush: ftdi_if_mcuhost_flush(itf); break;
case ftmcu_wait_io_hi: ftdi_if_mcuhost_wait_io(itf, true ); break;
case ftmcu_wait_io_lo: ftdi_if_mcuhost_wait_io(itf, false); break;
case ftmcu_read8:
itf->readbuf[0] = ftdi_if_mcuhost_read8(itf, vnd_read_byte(itf, itfnum));
avail = 1;
break;
case ftmcu_read16:
avail = (uint32_t)vnd_read_byte(itf, itfnum) << 8;
avail |= vnd_read_byte(itf, itfnum);
itf->readbuf[0] = ftdi_if_mcuhost_read16(itf, (uint16_t)avail);
avail = 1;
break;
case ftmcu_write8:
itf->writebuf[0] = vnd_read_byte(itf, itfnum);
itf->writebuf[1] = vnd_read_byte(itf, itfnum);
ftdi_if_mcuhost_write8(itf, itf->writebuf[0], itf->writebuf[1]);
break;
case ftmcu_write16:
avail = (uint32_t)vnd_read_byte(itf, itfnum) << 8;
avail |= vnd_read_byte(itf, itfnum);
itf->writebuf[0] = vnd_read_byte(itf, itfnum);
ftdi_if_mcuhost_write8(itf, avail, itf->writebuf[0]);
avail = 0;
break;
default: // send error response when command doesn't exist
itf->readbuf[0] = 0xfa;
itf->readbuf[1] = cmdbyte;
avail = 2;
break;
}
if (avail) tud_vendor_n_write(itfnum, itf->readbuf, avail);
break;
default: // drop incoming data so that the pipes don't get clogged. can't do much else
avail = tud_vendor_n_available(itfnum);
if (avail) tud_vendor_n_read(itfnum, itf->writebuf, avail);
break;
}
}
void ftdi_task_ifa(void) { ftdi_task(VND_N_FTDI_IFA); }
void ftdi_task_ifb(void) { ftdi_task(VND_N_FTDI_IFB); }
#define FT2232D_CLOCK (48*1000*1000)
uint32_t ftdi_if_decode_baudrate(uint32_t enc_brate) { // basically reversing libftdi ftdi_to_clkbits
static const uint8_t ftdi_brate_frac_lut[8] = { 0, 4, 2, 1, 3, 5, 6, 7 };
// special cases
if (enc_brate == 0) return FT2232D_CLOCK >> 4;
else if (enc_brate == 1) return FT2232D_CLOCK / 24;
else if (enc_brate == 2) return FT2232D_CLOCK >> 5;
uint32_t div = (enc_brate & 0x7fff) << 3; // integer part
div = div | ftdi_brate_frac_lut[(enc_brate >> 14) & 7];
uint32_t baud = FT2232D_CLOCK / div;
if (baud & 1) baud = (baud >> 1) + 1; // raunding
else baud >>= 1;
return baud;
}
static uint8_t control_buf[2];
bool ftdi_control_xfer_cb(uint8_t rhport, uint8_t stage,
tusb_control_request_t const* req) {
// return true: don't stall
// return false: stall
// not a vendor request -> not meant for this code
if (req->bmRequestType_bit.type != TUSB_REQ_TYPE_VENDOR) return true;
// data stage not needed: data stages only for device->host xfers, never host->device
if (stage != CONTROL_STAGE_SETUP) return true;
// tud_control_status(rhport, req); : acknowledge
// tud_control_xfer(rhport, req, bufaddr, size); : ack + there's a data phase with stuff to do
// write: send bufaddr value. read: data stage will have bufaddr filled out
// do EEPROM stuff first, as these don't use wIndex as channel select
uint16_t tmp16;
switch (req->bRequest) {
case sio_readeeprom:
tmp16 = ftdi_eeprom[req->wIndex & 0x7f];
control_buf[0] = tmp16 & 0xff;
control_buf[1] = tmp16 >> 8;
return tud_control_xfer(rhport, req, control_buf, 2);
case sio_writeeeprom:
ftdi_eeprom[req->wIndex & 0x7f] = req->wValue;
return tud_control_status(rhport, req);
case sio_eraseeeprom:
memset(ftdi_eeprom, 0xff, sizeof ftdi_eeprom);
return tud_control_status(rhport, req);
}
int itfnum = req->wIndex & 0xff;
struct ftdi_interface* itf = &ftdi_ifa; // default
if (itfnum > 2) return false; // bad interface number
else if (itfnum == 1) itf = &ftdi_ifa;
else if (itfnum == 2) itf = &ftdi_ifb;
switch (req->bRequest) {
case sio_cmd:
if (req->wValue == sio_reset) {
itf->modem_mask = 0;
itf->modem_data = 0;
itf->flow = ftflow_none;
itf->lineprop = sio_bits_8 | sio_stop_1; // 8n1
itf->charen = 0;
itf->bb_dir = 0;
itf->bb_mode = sio_mode_reset;
itf->mcu_addr_latch = 0;
itf->rxavail = 0; itf->rxpos = 0;
ftdi_if_sio_reset(itf);
} else if (req->wValue == sio_tciflush) {
itf->rxavail = 0; itf->rxpos = 0;
ftdi_if_sio_tciflush(itf);
} else if (req->wValue == sio_tcoflush) {
// nothing extra to clear here I think
ftdi_if_sio_tcoflush(itf);
} else return false; // unk
return tud_control_status(rhport, req);
case sio_setmodemctrl:
ftdi_if_set_modemctrl(itf,
itf->modem_mask = (req->wValue >> 8),
itf->modem_data = (req->wValue & 0xff));
return tud_control_status(rhport, req);
case sio_setflowctrl: {
enum ftdi_flowctrl flow = (req->wIndex >> 8);
if (!req->wValue)
flow = (flow & ~ftflow_xonxoff) | (itf->flow & ftflow_xonxoff);
ftdi_if_set_flowctrl(itf, itf->flow = flow);
} return tud_control_status(rhport, req);
case sio_setbaudrate: {
uint32_t enc_brate = (uint32_t)req->wValue | ((uint32_t)(req->wIndex & 0xff00) << 8);
uint32_t brate = ftdi_if_decode_baudrate(enc_brate);
ftdi_if_set_baudrate(itf, itf->baudrate = brate);
} return tud_control_status(rhport, req);
case sio_setlineprop:
ftdi_if_set_lineprop(itf, itf->lineprop = req->wValue);
return tud_control_status(rhport, req);
case sio_pollmodemstat:
tmp16 = ftdi_if_poll_modemstat(itf);
control_buf[0] = tmp16 & 0xff;
control_buf[1] = tmp16 >> 8;
return tud_control_xfer(rhport, req, control_buf, 2);
case sio_seteventchar:
if (req->wValue >> 8) itf->charen |= eventchar_enable;
else if (itf->charen & eventchar_enable) itf->charen ^= eventchar_enable;
ftdi_if_set_eventchar(itf, req->wValue >> 8,
itf->eventchar = (req->wValue & 0xff));
return tud_control_status(rhport, req);
case sio_seterrorchar:
if (req->wValue >> 8) itf->charen |= errorchar_enable;
else if (itf->charen & errorchar_enable) itf->charen ^= errorchar_enable;
ftdi_if_set_errorchar(itf, req->wValue >> 8,
itf->errorchar = (req->wValue & 0xff));
return tud_control_status(rhport, req);
case sio_setlatency:
ftdi_if_set_latency(itf, itf->latency = (req->wValue & 0xff));
return tud_control_status(rhport, req);
case sio_getlatency:
control_buf[0] = ftdi_if_get_latency(itf);
return tud_control_xfer(rhport, req, control_buf, 1);
case sio_setbitbang: {
uint8_t olddir = itf->bb_dir;
enum ftdi_sio_bitmode oldmode = itf->bb_mode;
ftdi_if_set_bitbang(itf,
itf->bb_dir = (req->wValue >> 8),
itf->bb_mode = (req->wValue & 0xff),
olddir, oldmode);
} return tud_control_status(rhport, req);
case sio_readpins:
control_buf[0] = ftdi_if_read_pins(itf);
return tud_control_xfer(rhport, req, control_buf, 1);
default: return false; // stall if not recognised
}
}
#endif /* DBOARD_HAS_FTDI */

375
src/m_ftdi/ftdi.h Normal file
View File

@ -0,0 +1,375 @@
// vim: set et:
#ifndef FTDI_H_
#define FTDI_H_
#include "tusb_config.h"
#include <tusb.h>
// USB command handling, and mode interfacing stuff
void ftdi_init(void);
void ftdi_deinit(void);
// have separate tasks for the separate bulk endpoints so that a wait-for-data
// in one ep won't cause the otehr to stall, too
void ftdi_task_ifa(void);
void ftdi_task_ifb(void);
bool ftdi_control_xfer_cb(uint8_t rhport, uint8_t ep_addr,
tusb_control_request_t const* req);
void ftdi_eeprom_dirty_set(bool v);
bool ftdi_eeprom_dirty_get(void);
extern uint16_t ftdi_eeprom[128];
#define FTDI_EEP_IFA_MODE (ftdi_eeprom[0] & 0xff)
#define FTDI_EEP_IFB_MODE (ftdi_eeprom[0] >> 8)
#define FTDI_EEP_IDVENDOR (ftdi_eeprom[1])
#define FTDI_EEP_IDPRODUCT (ftdi_eeprom[2])
#define FTDI_EEP_BCDDEVICE (ftdi_eeprom[3])
#define FTDI_EEP_PWFLAGS (ftdi_eeprom[4] & 0xff)
#define FTDI_EEP_MAXAMP (ftdi_eeprom[4] >> 8)
#define FTDI_EEP_USBFLAGS (ftdi_eeprom[5])
#define FTDI_EEP_BCDUSB (ftdi_eeprom[6])
#define FTDI_EEP_MANUF_OFF (ftdi_eeprom[7] & 0xff)
#define FTDI_EEP_MANUF_LEN (ftdi_eeprom[7] >> 8)
#define FTDI_EEP_PROD_OFF (ftdi_eeprom[8] & 0xff)
#define FTDI_EEP_PROD_LEN (ftdi_eeprom[8] >> 8)
#define FTDI_EEP_SERIAL_OFF (ftdi_eeprom[9] & 0xff)
#define FTDI_EEP_SERIAL_LEN (ftdi_eeprom[9] >> 8)
#define FTDI_EEP_CHIPTYPE (ftdi_eeprom[10])
static inline uint16_t ftdi_eeprom_checksum_init(void) {
return 0xaaaa;
}
static inline uint16_t ftdi_eeprom_checksum_digest(uint16_t val, const uint16_t* data, size_t len) {
for (size_t i = 0; i < len; ++i) {
val = (val ^ data[i]);
val = (val << 1) | (val >> 15);
}
return val;
}
static inline uint16_t ftdi_eeprom_checksum_calc(const uint16_t* data, size_t len) {
return ftdi_eeprom_checksum_digest(ftdi_eeprom_checksum_init(), data, len);
}
// USB protocol stuff
enum ftdi_request {
sio_cmd = 0,
sio_setmodemctrl = 1,
sio_setflowctrl = 2,
sio_setbaudrate = 3,
sio_setlineprop = 4,
sio_pollmodemstat = 5,
sio_seteventchar = 6,
sio_seterrorchar = 7,
sio_setlatency = 9,
sio_getlatency = 10,
sio_setbitbang = 11,
sio_readpins = 12,
sio_readeeprom = 0x90,
sio_writeeeprom = 0x91,
sio_eraseeeprom = 0x92
};
enum ftdi_sio_cmd {
sio_reset = 0,
sio_tciflush = 2,
sio_tcoflush = 1
};
enum ftdi_sio_lineprop {
sio_break_on = 1<<14,
sio_break_off = 0<<14,
sio_break__mask = 1<<14,
sio_stop_1 = 0<<11,
sio_stop_15 = 1<<11, // deprecated?
sio_stop_2 = 2<<11,
sio_stop__mask = 3<<11,
sio_parity_none = 0<<8,
sio_parity_odd = 1<<8,
sio_parity_even = 2<<8,
sio_parity_mark = 3<<8,
sio_parity_space = 4<<8,
sio_parity__mask = 7<<8,
sio_bits_7 = 7<<0,
sio_bits_8 = 8<<0,
sio_bits__mask = 0xff<<0,
};
enum ftdi_sio_modemstat {
sio_modem_cts = 1<< 4, // Clear to Send active
sio_modem_dts = 1<< 5, // Data Set Ready active
sio_modem_ri = 1<< 6, // Ring Indicator active
sio_modem_rlsd = 1<< 7, // Receive Line Signal Detect active
sio_modem_dr = 1<< 8, // Data Ready
sio_modem_oe = 1<< 9, // Overrun Error
sio_modem_pe = 1<<10, // Parity Error
sio_modem_fe = 1<<11, // Framing Error
sio_modem_bi = 1<<12, // Break Interrupt
sio_modem_thre = 1<<13, // Transmitter Holding REgister
sio_modem_temt = 1<<14, // Transmitter Empty
sio_modem_fifoerr = 1<<15 // Error in receive FIFO
};
enum ftdi_sio_bitmode {
sio_mode_reset = 0, // i.e. from EEPROM
sio_mode_bitbang = 1,
sio_mode_mpsse = 2,
sio_mode_syncbb = 4,
sio_mode_mcu = 8,
// 0x10: opto
// 0x20: cbus bitbang (R-type only)
// 0x40: sync fifo (2232h) // like regular fifo mode, but with clock output
// 0x80: ft1284 (232h, not 2232d)
};
enum ftdi_eep_defmode {
fteep_mode_uart = 0,
fteep_mode_fifo = 1,
fteep_mode_opto = 2, // not implementing this here
fteep_mode_cpu = 4,
fteep_mode_ft1284 = 8, // not impl. on 2232d, 232h-only
};
// mpsse, mcuhost commands
// if bit 7 of an MPSSE command byte
enum ftdi_mpsse_cflg {
ftmpsse_negedge_wr = 1<<0, // if 0, output bits on positive clock edige
ftmpsse_bitmode = 1<<1, // if 0, byte mode
ftmpsse_negedge_rd = 1<<2, // if 0, input bits on positive clock edge
ftmpsse_lsbfirst = 1<<3, // if 0, msb first
ftmpsse_tdiwrite = 1<<4, // 1 == do perform output
ftmpsse_tdoread = 1<<5, // 1 == do perform input
ftmpsse_tmswrite = 1<<6, // 1 == do perform output?
ftmpsse_specialcmd = 1<<7 // see below enum if set
};
// bitmode: 1 length byte, max=7 (#bits = length+1) for separate bits
// bytemode: 2 length bytes == number of bytes that follow
// both tdiwrite and tdoread high: only one length value/equal number of bits in/out!
// if both tdiwrite and tdoread are high, negedge_wr and negedge_rd must differ
// tms read/writes: readback is from tdo, bit7 in databyte is tdi output, held constant
// tdiwrite always 0, bitmode 1 in impls, can be ignored I guess
// also always lsbfirst, but not too hard to support msbfirst too
// idle levels (of eg. tms/cs) -> set_dirval?
enum ftdi_mpssemcu_cmd {
ftmpsse_set_dirval_lo = 0x80, // sets initial clock level!
ftmpsse_set_dirval_hi = 0x82,
ftmpsse_read_lo = 0x81,
ftmpsse_read_hi = 0x83,
ftmpsse_loopback_on = 0x84,
ftmpsse_loopback_off = 0x85,
ftmpsse_set_clkdiv = 0x86, // period = 12MHz / ((1 + value16) * 2)
ftmpsse_flush = 0x87, // flush dev->host usb buffer
ftmpsse_wait_io_hi = 0x88, // wait for gpiol1/io1 to be high
ftmpsse_wait_io_lo = 0x89, // wait for gpiol1/io1 to be low
// technically ft2232h-only but we can support these, too
ftmpsse_div5_disable = 0x8a, // ft2232h internally has a 5x faster clock, but slows it down by default
ftmpsse_div5_enable = 0x8b, // for backwards compat. these two commands enable/disable that slowdown
ftmpsse_data_3ph_en = 0x8c, // enable 3-phase data
ftmpsse_data_3ph_dis = 0x8d, // disable 3-phase data
ftmpsse_clockonly_bits = 0x8e, // enable clock for n bits, no data xfer
ftmpsse_clockonly_bytes = 0x8f, // enable clock for n bytes, no data xfer
ftmpsse_clock_wait_io_hi = 0x94, // wait_io_hi + clockonly
ftmpsse_clock_wait_io_lo = 0x95, // wait_io_lo + clockonly
ftmpsse_adapclk_enable = 0x96, // enable ARM JTAG adaptive clocking (rtck gpiol3 input)
ftmpsse_adapclk_disable = 0x97, // disable ARM JTAG adaptive clocking (rtck gpiol3 input)
ftmpsse_clock_bits_wait_io_hi = 0x9c, // clock_wait_io_hi + clockonly_bits
ftmpsse_clock_bits_wait_io_lo = 0x9d, // clock_wait_io_lo + clockonly_bits
ftmpsse_hi_is_tristate = 0x9e, // turns 1 output to tristate for selected outputs
ftmcu_flush = 0x87, // flush dev->host usb buffer
ftmcu_wait_io_hi = 0x88, // wait for gpiol1/io1 to be high
ftmcu_wait_io_lo = 0x89, // wait for gpiol1/io1 to be low
ftmcu_read8 = 0x90,
ftmcu_read16 = 0x91,
ftmcu_write8 = 0x92,
ftmcu_write16 = 0x93
};
// internal use only types
enum ftdi_mode { // combines EEPROM setting + bitmode
ftmode_uart = 0,
ftmode_mpsse = 1,
ftmode_asyncbb = 2,
ftmode_syncbb = 4,
ftmode_mcuhost = 8,
ftmode_fifo = 0x10,
ftmode_opto = 0x20, // not implementing this here
ftmode_cpufifo = 0x40,
ftmode_ft1284 = 0x80, // not impl. on 2232d
};
enum ftdi_flowctrl {
ftflow_none = 0,
ftflow_ctsrts = 1,
ftflow_dtrdts = 2,
ftflow_xonxoff = 4,
};
struct ftdi_interface {
// TODO soft fields maybe, because it's a mess with lots of padding right now
int index;
uint8_t modem_mask;
uint8_t modem_data;
enum ftdi_flowctrl flow;
uint32_t baudrate;
enum ftdi_sio_lineprop lineprop;
enum ftdi_sio_modemstat modemstat;
uint8_t eventchar, errorchar;
enum { eventchar_enable = 1<<0, errorchar_enable = 1<<1 } charen;
uint8_t latency; // latency timer. TODO: implement this
uint8_t bb_dir; // high/1 bit = output, 0=input
enum ftdi_sio_bitmode bb_mode;
uint16_t mcu_addr_latch;
// these are for USB bulk cmds etc.
// "write" means write to hardware output pins
// "read" means read from hardware input pins
uint8_t writebuf[CFG_TUD_VENDOR_RX_BUFSIZE];
uint8_t readbuf [CFG_TUD_VENDOR_TX_BUFSIZE];
uint8_t bufbuf [CFG_TUD_VENDOR_RX_BUFSIZE]; // for buffered IO
uint32_t rxavail, rxpos;
};
extern struct ftdi_interface ftdi_ifa, ftdi_ifb;
// interface control stuff
static inline enum ftdi_mode ftdi_get_mode_of(enum ftdi_sio_bitmode bb_mode, uint8_t eepmode) {
if (bb_mode == 0x10) return ftmode_opto;
if (bb_mode == sio_mode_reset) {
return (eepmode << 4) & 0xf0;
} else return bb_mode & 0xf;
}
static inline enum ftdi_mode ftdi_if_get_mode(struct ftdi_interface* itf) {
return ftdi_get_mode_of(itf->bb_mode, itf->index ? FTDI_EEP_IFB_MODE : FTDI_EEP_IFA_MODE);
}
uint32_t ftdi_if_decode_baudrate(uint32_t enc_brate);
// control request stuff. implemented by bsp driver
void ftdi_if_init(struct ftdi_interface* itf);
void ftdi_if_deinit(struct ftdi_interface* itf);
void ftdi_if_sio_reset(struct ftdi_interface* itf);
void ftdi_if_sio_tciflush(struct ftdi_interface* itf);
void ftdi_if_sio_tcoflush(struct ftdi_interface* itf);
void ftdi_if_set_modemctrl(struct ftdi_interface* itf, uint8_t mask, uint8_t data);
void ftdi_if_set_flowctrl(struct ftdi_interface* itf, enum ftdi_flowctrl flow);
void ftdi_if_set_baudrate(struct ftdi_interface* itf, uint32_t baudrate);
void ftdi_if_set_lineprop(struct ftdi_interface* itf, enum ftdi_sio_lineprop lineprop);
enum ftdi_sio_modemstat ftdi_if_poll_modemstat(struct ftdi_interface* itf);
void ftdi_if_set_eventchar(struct ftdi_interface* itf, bool enable, uint8_t evchar);
void ftdi_if_set_errorchar(struct ftdi_interface* itf, bool enable, uint8_t erchar);
void ftdi_if_set_latency(struct ftdi_interface* itf, uint8_t latency);
uint8_t ftdi_if_get_latency(struct ftdi_interface* itf);
void ftdi_if_set_bitbang(struct ftdi_interface* itf, uint8_t dirmask, enum ftdi_sio_bitmode,
uint8_t olddir, enum ftdi_sio_bitmode oldmode);
uint8_t ftdi_if_read_pins(struct ftdi_interface* itf);
// bulk commands (also implemented by bsp driver)
// "write" means write to hardware output pins
// "read" means read from hardware input pins
void ftdi_if_uart_write(struct ftdi_interface* itf, const uint8_t* data, size_t datasize);
size_t ftdi_if_uart_read (struct ftdi_interface* itf, uint8_t* data, size_t maxsize);
void ftdi_if_fifo_write(struct ftdi_interface* itf, const uint8_t* data, size_t datasize);
size_t ftdi_if_fifo_read (struct ftdi_interface* itf, uint8_t* data, size_t maxsize);
void ftdi_if_cpufifo_write(struct ftdi_interface* itf, const uint8_t* data, size_t datasize);
size_t ftdi_if_cpufifo_read (struct ftdi_interface* itf, uint8_t* data, size_t maxsize);
void ftdi_if_asyncbb_write(struct ftdi_interface* itf, const uint8_t* data, size_t datasize);
size_t ftdi_if_asyncbb_read (struct ftdi_interface* itf, uint8_t* data, size_t maxsize);
void ftdi_if_syncbb_write (struct ftdi_interface* itf, const uint8_t* data, size_t datasize);
size_t ftdi_if_syncbb_read (struct ftdi_interface* itf, uint8_t* data, size_t maxsize);
void ftdi_if_mpsse_flush(struct ftdi_interface* itf);
void ftdi_if_mpsse_wait_io(struct ftdi_interface* itf, bool level);
void ftdi_if_mpsse_set_dirval_lo(struct ftdi_interface* itf, uint8_t dir, uint8_t val);
void ftdi_if_mpsse_set_dirval_hi(struct ftdi_interface* itf, uint8_t dir, uint8_t val);
uint8_t ftdi_if_mpsse_read_lo(struct ftdi_interface* itf);
uint8_t ftdi_if_mpsse_read_hi(struct ftdi_interface* itf);
void ftdi_if_mpsse_loopback(struct ftdi_interface* itf, bool enable);
void ftdi_if_mpsse_set_clkdiv(struct ftdi_interface* itf, uint16_t div);
uint8_t ftdi_if_mpsse_xfer_bits(struct ftdi_interface* itf, int flags, size_t nbits, uint8_t value);
void ftdi_if_mpsse_xfer_bytes(struct ftdi_interface* itf, int flags, size_t nbytes, uint8_t* dst, const uint8_t* src);
uint8_t ftdi_if_mpsse_tms_xfer(struct ftdi_interface* itf, int flags, size_t nbits, uint8_t value);
void ftdi_if_mpsse_div5(struct ftdi_interface* itf, bool enable);
void ftdi_if_mpsse_data_3ph(struct ftdi_interface* itf, bool enable);
void ftdi_if_mpsse_adaptive(struct ftdi_interface* itf, bool enable);
void ftdi_if_mpsse_clockonly(struct ftdi_interface* itf, uint32_t cycles);
void ftdi_if_mpsse_clock_wait_io(struct ftdi_interface* itf, bool level);
void ftdi_if_mpsse_clockonly_wait_io(struct ftdi_interface* itf, bool level, uint32_t cycles);
void ftdi_if_mpsse_hi_is_tristate(struct ftdi_interface* itf, uint16_t pinmask);
void ftdi_if_mcuhost_flush(struct ftdi_interface* itf);
void ftdi_if_mcuhost_wait_io(struct ftdi_interface* itf, bool level);
uint8_t ftdi_if_mcuhost_read8 (struct ftdi_interface* itf, uint8_t addr);
uint8_t ftdi_if_mcuhost_read16(struct ftdi_interface* itf, uint16_t addr);
void ftdi_if_mcuhost_write8 (struct ftdi_interface* itf, uint8_t addr, uint8_t value);
void ftdi_if_mcuhost_write16(struct ftdi_interface* itf, uint16_t addr, uint8_t value);
void ftdi_if_uart_init(struct ftdi_interface* itf);
void ftdi_if_mpsse_init(struct ftdi_interface* itf);
void ftdi_if_asyncbb_init(struct ftdi_interface* itf);
void ftdi_if_syncbb_init(struct ftdi_interface* itf);
void ftdi_if_mcuhost_init(struct ftdi_interface* itf);
void ftdi_if_fifo_init(struct ftdi_interface* itf);
void ftdi_if_cpufifo_init(struct ftdi_interface* itf);
void ftdi_if_uart_deinit(struct ftdi_interface* itf);
void ftdi_if_mpsse_deinit(struct ftdi_interface* itf);
void ftdi_if_asyncbb_deinit(struct ftdi_interface* itf);
void ftdi_if_syncbb_deinit(struct ftdi_interface* itf);
void ftdi_if_mcuhost_deinit(struct ftdi_interface* itf);
void ftdi_if_fifo_deinit(struct ftdi_interface* itf);
void ftdi_if_cpufifo_deinit(struct ftdi_interface* itf);
void ftdi_if_uart_set_baudrate(struct ftdi_interface* itf, uint32_t baudrate);
void ftdi_if_mpsse_set_baudrate(struct ftdi_interface* itf, uint32_t baudrate);
void ftdi_if_asyncbb_set_baudrate(struct ftdi_interface* itf, uint32_t baudrate);
void ftdi_if_syncbb_set_baudrate(struct ftdi_interface* itf, uint32_t baudrate);
void ftdi_if_mcuhost_set_baudrate(struct ftdi_interface* itf, uint32_t baudrate);
void ftdi_if_fifo_set_baudrate(struct ftdi_interface* itf, uint32_t baudrate);
void ftdi_if_cpufifo_set_baudrate(struct ftdi_interface* itf, uint32_t baudrate);
#endif

View File

@ -1,350 +0,0 @@
// vim: set et:
/* UGLY HACK */
#define BSP_FEATURE_M_DEFAULT_H_
#include <tusb.h>
#include "mode.h"
#include "thread.h"
#include "usbstdio.h"
#include "vnd_cfg.h"
#include "m_isp/bsp-feature.h"
/* CMSIS-DAP */
#include "DAP_config.h" /* ARM code *assumes* this is included prior to DAP.h */
#include "DAP.h"
/* CDC UART */
#include "m_default/cdc.h" /* yeah just reuse this one */
/* MehFET */
#include "m_isp/mehfet.h"
enum m_isp_cmds {
misp_cmd_idk = mode_cmd__specific,
};
enum m_isp_feature {
misp_feat_uart = 1<<0,
misp_feat_cmsisdap = 1<<1,
misp_feat_mehfet = 1<<2,
};
#ifdef DBOARD_HAS_UART
extern cothread_t m_def_uartthread;
extern uint8_t m_def_uartstack[THREAD_STACK_SIZE];
__attribute__((__weak__)) cothread_t m_def_uartthread;
__attribute__((__weak__)) uint8_t m_def_uartstack[THREAD_STACK_SIZE];
__attribute__((__weak__)) void m_def_uart_thread_fn(void) {
cdc_uart_init();
thread_yield();
while (1) {
cdc_uart_task();
thread_yield();
}
}
#define uartthread m_def_uartthread
#define uartstack m_def_uartstack
#define uart_thread_fn m_def_uart_thread_fn
#endif
#ifdef DBOARD_HAS_MEHFET
static cothread_t mehfetthread;
static uint8_t mehfetstack[THREAD_STACK_SIZE];
static void mehfet_thread_fn(void) {
mehfet_init();
thread_yield();
while (1) {
mehfet_task();
thread_yield();
}
}
#endif
static void enter_cb(void) {
#ifdef USE_USBCDC_FOR_STDIO
stdio_usb_set_itf_num(CDC_N_STDIO);
#endif
vnd_cfg_set_itf_num(VND_N_CFG);
// TODO: CMSISDAP?
// HACK: we init UART stuff first: UART inits gpio 10,11 pinmux fn to UART
// flow control signals, which is ok for mode1, but conflicts with
// stuff in mode2
#ifdef DBOARD_HAS_UART
uartthread = co_derive(uartstack, sizeof uartstack, uart_thread_fn);
thread_enter(uartthread); // will call cdc_uart_init() on correct thread
#endif
#ifdef DBOARD_HAS_MEHFET
mehfetthread = co_derive(mehfetstack, sizeof mehfetstack, mehfet_thread_fn);
thread_enter(mehfetthread);
#endif
}
static void leave_cb(void) {
// TODO: CMSISDAP?
#ifdef DBOARD_HAS_MEHFET
mehfet_deinit();
#endif
#ifdef DBOARD_HAS_UART
cdc_uart_deinit();
#endif
}
void dap_do_bulk_stuff(int itf);
static void task_cb(void) {
#ifdef DBOARD_HAS_UART
tud_task();
thread_enter(uartthread);
#endif
#ifdef DBOARD_HAS_MEHFET
tud_task();
thread_enter(mehfetthread);
#endif
dap_do_bulk_stuff(VND_N_CMSISDAP);
}
static void handle_cmd_cb(uint8_t cmd) {
uint8_t resp = 0;
switch (cmd) {
case mode_cmd_get_features:
#ifdef DBOARD_HAS_UART
resp |= misp_feat_uart;
#endif
#ifdef DBOARD_HAS_CMSISDAP
resp |= misp_feat_cmsisdap;
#endif
#ifdef DBOARD_HAS_MEHFET
resp |= misp_feat_mehfet;
#endif
vnd_cfg_write_resp(cfg_resp_ok, 1, &resp);
break;
default:
vnd_cfg_write_strf(cfg_resp_illcmd, "unknown mode1 command %02x", cmd);
break;
}
}
enum {
STRID_LANGID = 0,
STRID_MANUFACTURER,
STRID_PRODUCT,
STRID_SERIAL,
STRID_CONFIG,
STRID_IF_VND_CFG,
STRID_IF_HID_CMSISDAP,
STRID_IF_VND_CMSISDAP,
STRID_IF_CDC_UART,
STRID_IF_VND_MEHFET,
STRID_IF_CDC_STDIO,
};
enum {
#ifdef DBOARD_HAS_CMSISDAP
ITF_NUM_VND_CMSISDAP,
#endif
#if CFG_TUD_VENDOR > 0
ITF_NUM_VND_CFG,
#endif
#ifdef DBOARD_HAS_CMSISDAP
ITF_NUM_HID_CMSISDAP,
#endif
#ifdef DBOARD_HAS_UART
ITF_NUM_CDC_UART_COM,
ITF_NUM_CDC_UART_DATA,
#endif
#ifdef DBOARD_HAS_MEHFET
ITF_NUM_VND_MEHFET,
#endif
#ifdef USE_USBCDC_FOR_STDIO
ITF_NUM_CDC_STDIO_COM,
ITF_NUM_CDC_STDIO_DATA,
#endif
ITF_NUM__TOTAL
};
enum {
CONFIG_TOTAL_LEN
= TUD_CONFIG_DESC_LEN
#if CFG_TUD_VENDOR > 0
+ TUD_VENDOR_DESC_LEN
#endif
#ifdef DBOARD_HAS_CMSISDAP
+ TUD_VENDOR_DESC_LEN
+ TUD_HID_INOUT_DESC_LEN
#endif
#ifdef DBOARD_HAS_UART
+ TUD_CDC_DESC_LEN
#endif
#ifdef DBOARD_HAS_MEHFET
+ TUD_VENDOR_DESC_LEN
#endif
#ifdef USE_USBCDC_FOR_STDIO
+ TUD_CDC_DESC_LEN
#endif
};
#define EPNUM_VND_DAP_OUT 0x01
#define EPNUM_VND_DAP_IN 0x81
#define EPNUM_VND_CFG_OUT 0x02/*-1*/
#define EPNUM_VND_CFG_IN 0x82/*-1*/
#define EPNUM_HID_CMSISDAP 0x03/*-1*/
#define EPNUM_CDC_UART_OUT 0x04/*-1*/
#define EPNUM_CDC_UART_IN 0x84/*-1*/
#define EPNUM_CDC_UART_NOTIF 0x85/*-1*/
#define EPNUM_VND_MEHFET_OUT 0x06/*-1*/
#define EPNUM_VND_MEHFET_IN 0x86/*-1*/
#define EPNUM_CDC_STDIO_OUT 0x07/*-1*/
#define EPNUM_CDC_STDIO_IN 0x87/*-1*/
#define EPNUM_CDC_STDIO_NOTIF 0x88/*-1*/
// clang-format off
#if CFG_TUD_HID > 0
static const uint8_t desc_hid_report[] = { // ugh
TUD_HID_REPORT_DESC_GENERIC_INOUT(CFG_TUD_HID_EP_BUFSIZE)
};
#endif
static const uint8_t desc_configuration[] = {
TUD_CONFIG_DESCRIPTOR(1, ITF_NUM__TOTAL, STRID_CONFIG, CONFIG_TOTAL_LEN,
TUSB_DESC_CONFIG_ATT_REMOTE_WAKEUP, 100),
#ifdef DBOARD_HAS_CMSISDAP
TUD_VENDOR_DESCRIPTOR_EX(ITF_NUM_VND_CMSISDAP, STRID_IF_VND_CMSISDAP, EPNUM_VND_DAP_OUT,
EPNUM_VND_DAP_IN, CFG_TUD_VENDOR_RX_BUFSIZE, 0, 0),
#endif
#if CFG_TUD_VENDOR > 0
TUD_VENDOR_DESCRIPTOR_EX(ITF_NUM_VND_CFG, STRID_IF_VND_CFG, EPNUM_VND_CFG_OUT,
EPNUM_VND_CFG_IN, CFG_TUD_VENDOR_RX_BUFSIZE, VND_CFG_SUBCLASS, VND_CFG_PROTOCOL),
#endif
#ifdef DBOARD_HAS_CMSISDAP
TUD_HID_INOUT_DESCRIPTOR(ITF_NUM_HID_CMSISDAP, STRID_IF_HID_CMSISDAP,
0 /*HID_PROTOCOL_NONE*/, sizeof(desc_hid_report), EPNUM_HID_CMSISDAP,
0x80 | (EPNUM_HID_CMSISDAP + 0), CFG_TUD_HID_EP_BUFSIZE, 1),
#endif
#ifdef DBOARD_HAS_UART
TUD_CDC_DESCRIPTOR(ITF_NUM_CDC_UART_COM, STRID_IF_CDC_UART, EPNUM_CDC_UART_NOTIF,
CFG_TUD_CDC_RX_BUFSIZE, EPNUM_CDC_UART_OUT, EPNUM_CDC_UART_IN, CFG_TUD_CDC_RX_BUFSIZE),
#endif
#ifdef DBOARD_HAS_MEHFET
TUD_VENDOR_DESCRIPTOR_EX(ITF_NUM_VND_MEHFET, STRID_IF_VND_MEHFET, EPNUM_VND_MEHFET_OUT,
EPNUM_VND_MEHFET_IN, CFG_TUD_VENDOR_RX_BUFSIZE, '4', '3'),
#endif
#ifdef USE_USBCDC_FOR_STDIO
TUD_CDC_DESCRIPTOR(ITF_NUM_CDC_STDIO_COM, STRID_IF_CDC_STDIO, EPNUM_CDC_STDIO_NOTIF,
CFG_TUD_CDC_RX_BUFSIZE, EPNUM_CDC_STDIO_OUT, EPNUM_CDC_STDIO_IN, CFG_TUD_CDC_RX_BUFSIZE),
#endif
};
static const char* string_desc_arr[] = {
NULL,
// no hw info here, or the name will be too long >__>
// CMSIS-DAP spec:
// "The Product String must contain 'CMSIS-DAP' somewhere in the string.
// This is used by the debuggers to identify a CMSIS-DAP compliant Debug
// Unit that is connected to a host computer."
[STRID_PRODUCT] = INFO_PRODUCT_BARE " CMSIS-DAP",
[STRID_CONFIG] = "Configuration descriptor",
// max string length check: |||||||||||||||||||||||||||||||
[STRID_IF_VND_CFG ] = "Device cfg/ctl interface",
[STRID_IF_HID_CMSISDAP] = "CMSIS-DAP HID interface",
[STRID_IF_VND_CMSISDAP] = "CMSIS-DAP bulk interface",
[STRID_IF_CDC_UART] = "UART CDC interface",
[STRID_IF_VND_MEHFET] = "MehFET MSP430 debug interface",
#ifdef USE_USBCDC_FOR_STDIO
[STRID_IF_CDC_STDIO] = "stdio CDC interface (debug)",
#endif
};
// clang-format on
#ifdef DBOARD_HAS_CMSISDAP
static const uint8_t* my_hid_descriptor_report_cb(uint8_t instance) {
(void)instance;
return desc_hid_report;
}
/*static uint16_t my_hid_get_report_cb(uint8_t instance, uint8_t report_id,
hid_report_type_t report_type, uint8_t* buffer, uint16_t reqlen) {
// TODO not implemented
(void)instance;
(void)report_id;
(void)report_type;
(void)buffer;
(void)reqlen;
return 0;
}*/
static void my_hid_set_report_cb(uint8_t instance, uint8_t report_id,
hid_report_type_t report_type, uint8_t const* rx_buffer, uint16_t bufsize) {
static uint8_t tx_buffer[CFG_TUD_HID_EP_BUFSIZE];
uint32_t response_size = TU_MIN(CFG_TUD_HID_EP_BUFSIZE, bufsize);
(void)instance;
(void)report_id;
(void)report_type;
memset(tx_buffer, 0, CFG_TUD_HID_EP_BUFSIZE);
DAP_ProcessCommand(rx_buffer, tx_buffer);
tud_hid_report(0, tx_buffer, response_size);
}
#endif
#if CFG_TUD_CDC > 0
static void my_cdc_line_coding_cb(uint8_t itf, cdc_line_coding_t const* line_coding) {
switch (itf) {
#ifdef DBOARD_HAS_UART
case CDC_N_UART:
cdc_uart_set_coding(line_coding->bit_rate, line_coding->stop_bits,
line_coding->parity, line_coding->data_bits);
break;
#endif
#ifdef USE_USBCDC_FOR_STDIO
case CDC_N_STDIO:
stdio_usb_line_coding_cb(line_coding);
break;
#endif
}
}
#endif
extern struct mode m_02_isp;
// clang-format off
struct mode m_02_isp = {
.name = "In-system-programming/debugging mode",
.version = 0x0010,
.n_string_desc = sizeof(string_desc_arr)/sizeof(string_desc_arr[0]),
.usb_desc = desc_configuration,
.string_desc = string_desc_arr,
.enter = enter_cb,
.leave = leave_cb,
.task = task_cb,
.handle_cmd = handle_cmd_cb,
#if defined(DBOARD_HAS_CMSISDAP) && CFG_TUD_HID > 0
#if 0
.tud_hid_get_report_cb = my_hid_get_report_cb,
#endif
.tud_hid_set_report_cb = my_hid_set_report_cb,
.tud_hid_descriptor_report_cb = my_hid_descriptor_report_cb,
#endif
#if CFG_TUD_CDC > 0
.tud_cdc_line_coding_cb = my_cdc_line_coding_cb,
#endif
};
// clang-format on

View File

@ -1,441 +0,0 @@
// vim: set et:
#include <string.h>
#include <tusb.h>
#include "info.h"
#include "thread.h"
#include "m_isp/bsp-feature.h"
#include "m_isp/mehfet.h"
struct cmdlen {
uint32_t len;
uint8_t cmd;
};
#define BUFSIZE 256
static uint8_t rx_buf[BUFSIZE];
static uint8_t tx_buf[BUFSIZE];
static uint32_t rxavail, rxpos, txpos;
// TODO: this is duplicated several times over the codebase, maybe reduce this
static uint8_t read_byte(void) {
while (rxavail <= 0) {
if (!tud_vendor_n_mounted(VND_N_MEHFET) || !tud_vendor_n_available(VND_N_MEHFET)) {
thread_yield();
continue;
}
rxpos = 0;
rxavail = tud_vendor_n_read(VND_N_MEHFET, rx_buf, sizeof rx_buf);
if (rxavail == 0) thread_yield();
}
uint8_t rv = rx_buf[rxpos];
++rxpos;
--rxavail;
return rv;
}
/*static void drop_incoming(void) {
rxavail = 0;
rxpos = 0;
// empty tinyusb internal buffer
if (tud_vendor_n_mounted(VND_N_MEHFET)) {
while (tud_vendor_n_available(VND_N_MEHFET)) {
tud_vendor_n_read(VND_N_MEHFET, rx_buf, sizeof rx_buf);
}
}
}*/
static void write_flush(void) {
// TODO: is this needed?
while (tud_vendor_n_write_available(VND_N_MEHFET) < txpos) {
thread_yield();
}
tud_vendor_n_write(VND_N_MEHFET, tx_buf, txpos);
txpos = 0;
}
static void write_byte(uint8_t v) {
if (txpos == sizeof tx_buf) {
write_flush();
}
tx_buf[txpos] = v;
++txpos;
}
////////////////
static uint32_t plpos;
static struct cmdlen read_cmd_len(void) {
uint8_t cmd = read_byte(),
lastbyte = cmd;
uint32_t l = 0;
//printf("cmd=%02x\n", cmd);
for (size_t i = 0; (i < 4) && (lastbyte & 0x80); ++i) {
lastbyte = read_byte();
//printf("lenbyte=%02x\n");
uint8_t mask = (i == 3) ? 0xff : 0x7f;
l |= (lastbyte & mask) << (i * 7);
}
//printf("len=0x%x\n");
plpos = 0;
return (struct cmdlen){ .len = l, .cmd = cmd & 0x7f };
}
static inline uint8_t read_pl(void) {
++plpos;
return read_byte();
}
static void flush_pl(uint32_t len) {
while (plpos < len) read_byte();
}
static void write_resp(enum mehfet_status stat, size_t resplen, const uint8_t* resp) {
//if (stat != mehfet_ok) drop_incoming();
write_byte((stat & 0x7f) | (resplen ? 0x80 : 0));
for (size_t i = 0, len2 = resplen; (i < 4) && len2; ++i) {
uint8_t nextv;
if (i == 3) {
nextv = (uint8_t)len2;
} else {
nextv = len2 & 0x7f;
if ((len2 >> 7) != 0) nextv |= 0x80;
}
len2 >>= 7;
write_byte(nextv);
}
for (size_t i = 0; i < resplen; ++i)
write_byte(resp[i]);
write_flush();
}
static void write_resp_str(enum mehfet_status stat, const char* str) {
write_resp(stat, strlen(str)+1 /* include null terminator */, (const uint8_t*)str);
}
///////////////
static uint8_t connstat;
void mehfet_init(void) {
rxavail = 0;
rxpos = 0;
txpos = 0;
plpos = 0;
connstat = mehfet_conn_none;
mehfet_hw_init();
}
void mehfet_deinit(void) {
if (connstat != mehfet_conn_none) {
mehfet_hw_disconnect();
connstat = mehfet_conn_none;
}
mehfet_hw_deinit();
}
void mehfet_task(void) {
struct cmdlen cmdhdr = read_cmd_len();
switch (cmdhdr.cmd) {
case mehfet_info:
//printf("in info cmd\n");
if (cmdhdr.len != 0) write_resp_str(mehfet_badargs, "Info takes no parameters");
else {
// TODO: add flag once Loop has been implemented
uint32_t caps = mehfet_hw_get_caps() /*| mehfet_cap_has_loop*/;
uint16_t ver = MEHFET_PROTO_VER;
uint8_t pktbuf_l2 = __builtin_ctz(sizeof tx_buf);
const char* name = INFO_PRODUCT(INFO_BOARDNAME);
size_t bufsize = strlen(name) + 1 + 8;
if (bufsize > 256) // uuuuuh, (stack size is 512b)
write_resp_str(mehfet_error, "not enough space for info buffer");
uint8_t buf[bufsize];
buf[0] = (caps >> 0) & 0xff;
buf[1] = (caps >> 8) & 0xff;
buf[2] = (caps >> 16) & 0xff;
buf[3] = (caps >> 24) & 0xff;
buf[4] = (ver >> 0) & 0xff;
buf[5] = (ver >> 8) & 0xff;
buf[6] = pktbuf_l2;
buf[7] = 0; // reserved
memcpy(&buf[8], name, bufsize - 8);
//printf("infocmd done\n");
write_resp(mehfet_ok, bufsize, buf);
}
break;
case mehfet_status:
if (cmdhdr.len != 0) write_resp_str(mehfet_badargs, "Status takes no parameters");
else write_resp(mehfet_ok, 1, &connstat);
break;
case mehfet_connect:
if (cmdhdr.len != 1) write_resp_str(mehfet_badargs, "Connect takes one parameter byte");
else if (connstat != mehfet_conn_none) {
write_resp_str(mehfet_badstate, "Already connected");
} else {
enum mehfet_conn conn = read_pl();
// check if we can actually do this
enum mehfet_conn conntyp = conn & mehfet_conn_typemask;
switch (conntyp) {
case mehfet_conn_jtag_noentry:
if (!(mehfet_hw_get_caps() & mehfet_cap_jtag_noentry)) {
write_resp(mehfet_nocaps, 0, NULL);
goto EXIT;
}
break;
case mehfet_conn_jtag_entryseq:
if (!(mehfet_hw_get_caps() & mehfet_cap_jtag_entryseq)) {
write_resp(mehfet_nocaps, 0, NULL);
goto EXIT;
}
break;
case mehfet_conn_sbw_entryseq:
if (!(mehfet_hw_get_caps() & mehfet_cap_sbw_entryseq)) {
write_resp(mehfet_nocaps, 0, NULL);
goto EXIT;
}
break;
case mehfet_conn_auto:
if (mehfet_hw_get_caps() & mehfet_cap_sbw_entryseq)
conntyp = mehfet_conn_sbw_entryseq;
else if (mehfet_hw_get_caps() & mehfet_cap_jtag_entryseq)
conntyp = mehfet_conn_jtag_entryseq;
else if (mehfet_hw_get_caps() & mehfet_cap_jtag_noentry)
conntyp = mehfet_conn_jtag_noentry;
else {
write_resp_str(mehfet_nocaps, "Connect: no mode implemented in hardware...");
goto EXIT;
}
break;
default:
write_resp_str(mehfet_badargs, "Connect: invalid connection mode");
goto EXIT;
}
const char* resp = mehfet_hw_connect(conntyp | (conn & mehfet_conn_nrstmask));
if (!resp) {
connstat = conntyp;
write_resp(mehfet_ok, 0, NULL);
} else
write_resp_str(mehfet_error, resp);
}
break;
case mehfet_disconnect:
if (cmdhdr.len != 0) write_resp_str(mehfet_badargs, "Disconnect takes no parameters");
else {
if (connstat != mehfet_conn_none) mehfet_hw_disconnect();
connstat = mehfet_conn_none;
write_resp(mehfet_ok, 0, NULL);
}
break;
case mehfet_delay:
if (cmdhdr.len != 4) write_resp_str(mehfet_badargs, "Delay takes 4 parameter bytes");
else {
uint32_t v = 0;
v |= (uint32_t)read_pl() << 0 ;
v |= (uint32_t)read_pl() << 8 ;
v |= (uint32_t)read_pl() << 16;
v |= (uint32_t)read_pl() << 24;
bool exact = v & (1u<<31), us = v & (1u<<30);
v &= (1u << 30) - 1;
if (exact) {
if (us) mehfet_hw_delay_us(v);
else mehfet_hw_delay_ms(v);
} else {
mehfet_hw_timer_start(us, v);
while (!mehfet_hw_timer_reached()) thread_yield();
}
write_resp(mehfet_ok, 0, NULL);
}
break;
case mehfet_set_clkspeed:
if (cmdhdr.len != 1) write_resp_str(mehfet_badargs, "SetClkSpeed takes one parameter byte");
else if (connstat == mehfet_conn_none) write_resp(mehfet_badstate, 0, NULL);
else {
mehfet_hw_set_clkspeed(read_pl() != 0);
write_resp(mehfet_ok, 0, NULL);
}
break;
case mehfet_get_old_lines:
if (cmdhdr.len != 0) write_resp_str(mehfet_badargs, "GetOldLines takes no parameters");
else if (connstat == mehfet_conn_none) write_resp(mehfet_badstate, 0, NULL);
else {
uint8_t v = mehfet_hw_get_old_lines();
write_resp(mehfet_ok, 1, &v);
}
break;
case mehfet_tdio_seq:
if (cmdhdr.len < 6) write_resp_str(mehfet_badargs,
"TdioSequence: need at least a TMS level, number of cycles and some TDI data (at least 6 bytes)");
// stack size is 512b
else if (cmdhdr.len > 128 + 5) write_resp_str(mehfet_badargs,
"TdioSequence: too much data to process, can do max. 1024 bits (128B) at once");
else if (connstat == mehfet_conn_none) write_resp(mehfet_badstate, 0, NULL);
else {
uint32_t ncyc = 0;
ncyc |= (uint32_t)read_pl() << 0 ;
ncyc |= (uint32_t)read_pl() << 8 ;
ncyc |= (uint32_t)read_pl() << 16;
ncyc |= (uint32_t)read_pl() << 24;
bool tmslvl = read_pl() != 0;
size_t nbytes = (ncyc + 7) >> 3;
if (nbytes != cmdhdr.len - 5) {
write_resp_str(mehfet_badargs, "TdioSequence: bad ncyc<->payload length");
} else {
uint8_t tdi_stuff[nbytes], tdo_stuff[nbytes];
for (size_t i = 0; i < nbytes; ++i) tdi_stuff[i] = read_pl();
mehfet_hw_tdio_seq(ncyc, tmslvl, tdi_stuff, tdo_stuff);
write_resp(mehfet_ok, nbytes, tdo_stuff);
}
}
break;
case mehfet_tms_seq:
if (cmdhdr.len < 6) write_resp_str(mehfet_badargs,
"TmsSequence: need a TDI level, the number of cycles and some TMS data (at least 6 bytes)");
// stack size is 512b
else if (cmdhdr.len > 256 + 5) write_resp_str(mehfet_badargs,
"TmsSequence: too much data to process, can do max. 2048 bits (256B) at once");
else if (connstat == mehfet_conn_none) write_resp(mehfet_badstate, 0, NULL);
else {
uint32_t ncyc = 0;
ncyc |= (uint32_t)read_pl() << 0 ;
ncyc |= (uint32_t)read_pl() << 8 ;
ncyc |= (uint32_t)read_pl() << 16;
ncyc |= (uint32_t)read_pl() << 24;
bool tdilvl = read_pl() != 0;
size_t nbytes = (ncyc + 7) >> 3;
if (nbytes != cmdhdr.len - 5) {
write_resp_str(mehfet_badargs, "TmsSequence: bad ncyc<->payload length");
} else {
uint8_t tms_stuff[nbytes];
for (size_t i = 0; i < nbytes; ++i) tms_stuff[i] = read_pl();
mehfet_hw_tms_seq(ncyc, tdilvl, tms_stuff);
write_resp(mehfet_ok, 0, NULL);
}
}
break;
case mehfet_tclk_edge:
if (cmdhdr.len != 1) write_resp_str(mehfet_badargs, "TclkEdge takes one parameter byte");
else if (connstat == mehfet_conn_none) write_resp(mehfet_badstate, 0, NULL);
else {
uint8_t newtclk = read_pl();
mehfet_hw_tclk_edge(newtclk);
write_resp(mehfet_ok, 0, NULL);
}
break;
case mehfet_tclk_burst:
if (cmdhdr.len != 4) write_resp_str(mehfet_badargs, "TclkBurst takes 4 parameter bytes");
else if (connstat == mehfet_conn_none) write_resp(mehfet_badstate, 0, NULL);
else {
uint32_t ncyc = 0;
ncyc |= (uint32_t)read_pl() << 0 ;
ncyc |= (uint32_t)read_pl() << 8 ;
ncyc |= (uint32_t)read_pl() << 16;
ncyc |= (uint32_t)read_pl() << 24;
mehfet_hw_tclk_burst(ncyc);
write_resp(mehfet_ok, 0, NULL);
}
break;
case mehfet_reset_tap:
if (!(mehfet_hw_get_caps() & mehfet_cap_has_reset_tap)) write_resp(mehfet_nocaps, 0, NULL);
else if (cmdhdr.len != 1) write_resp_str(mehfet_badargs, "ResetTAP takes one parameter byte");
else if (connstat == mehfet_conn_none) write_resp(mehfet_badstate, 0, NULL);
else {
uint8_t v = mehfet_hw_reset_tap(read_pl());
write_resp(mehfet_ok, 1, &v);
}
break;
case mehfet_irshift:
if (!(mehfet_hw_get_caps() & mehfet_cap_has_irshift)) write_resp(mehfet_nocaps, 0, NULL);
else if (cmdhdr.len != 1) write_resp_str(mehfet_badargs, "IRshift takes one parameter byte");
else if (connstat == mehfet_conn_none) write_resp(mehfet_badstate, 0, NULL);
else {
uint8_t newir = read_pl();
uint8_t oldir = mehfet_hw_shift_ir(newir);
write_resp(mehfet_ok, 1, &oldir);
}
break;
case mehfet_drshift:
if (!(mehfet_hw_get_caps() & mehfet_cap_has_drshift)) write_resp(mehfet_nocaps, 0, NULL);
else if (cmdhdr.len < 5) write_resp_str(mehfet_badargs,
"DRshift takes at least a bit count and some data (at least 5 bytes)");
else if (cmdhdr.len > 128 + 4) write_resp_str(mehfet_badargs,
"DRshift: too much data to process, can do max. 1024 bits (128B)");
else if (connstat == mehfet_conn_none) write_resp(mehfet_badstate, 0, NULL);
else {
uint32_t nbits = 0;
nbits |= (uint32_t)read_pl() << 0 ;
nbits |= (uint32_t)read_pl() << 8 ;
nbits |= (uint32_t)read_pl() << 16;
nbits |= (uint32_t)read_pl() << 24;
size_t nbytes = (nbits + 7) >> 3;
if (nbytes != cmdhdr.len - 4) {
write_resp_str(mehfet_badargs, "DRshift: bad nbits<->payload length");
} else {
uint8_t newdr[nbytes], olddr[nbytes];
for (size_t i = 0; i < nbytes; ++i) newdr[i] = read_pl();
mehfet_hw_shift_dr(nbits, newdr, olddr);
write_resp(mehfet_ok, nbytes, olddr);
}
}
break;
case mehfet_loop:
// TODO
write_resp_str(mehfet_nocaps, "not implemented yet, sorry");
//else if (connstat == mehfet_conn_none) write_resp(mehfet_badstate, 0, NULL);
break;
default:
write_resp(mehfet_invalidcmd, 0, NULL);
}
EXIT:
flush_pl(cmdhdr.len);
}

View File

@ -1,114 +0,0 @@
// vim: set et:
#ifndef M_ISP_MEHFET
#define M_ISP_MEHFET
#include <stdint.h>
#include <stdbool.h>
#define MEHFET_PROTO_VER 0x0001
void mehfet_init(void);
void mehfet_deinit(void);
void mehfet_task(void);
enum mehfet_cmd {
mehfet_info = 0x01,
mehfet_status = 0x02,
mehfet_connect = 0x03,
mehfet_disconnect = 0x04,
mehfet_delay = 0x05,
mehfet_set_clkspeed = 0x06,
mehfet_get_old_lines = 0x07,
mehfet_tdio_seq = 0x08,
mehfet_tms_seq = 0x09,
mehfet_tclk_edge = 0x0a,
mehfet_tclk_burst = 0x0b,
mehfet_reset_tap = 0x0c,
mehfet_irshift = 0x0d,
mehfet_drshift = 0x0e,
mehfet_loop = 0x0f,
};
enum mehfet_status {
mehfet_ok = 0x00,
mehfet_badargs = 0x7b,
mehfet_nocaps = 0x7c,
mehfet_badstate = 0x7d,
mehfet_invalidcmd = 0x7e,
mehfet_error = 0x7f
};
enum mehfet_caps {
mehfet_cap_jtag_noentry = 1<<0,
mehfet_cap_jtag_entryseq = 1<<1,
mehfet_cap_sbw_entryseq = 1<<2,
mehfet_cap_has_reset_tap = 1<< 8,
mehfet_cap_has_irshift = 1<< 9,
mehfet_cap_has_drshift = 1<<10,
mehfet_cap_has_loop = 1<<11,
};
enum mehfet_conn {
mehfet_conn_none = 0,
mehfet_conn_auto = 0,
mehfet_conn_jtag_noentry = 1,
mehfet_conn_jtag_entryseq = 2,
mehfet_conn_sbw_entryseq = 3,
mehfet_conn_typemask = 0x7f,
mehfet_conn_nrstmask = 0x80
};
enum mehfet_resettap_flags {
mehfet_rsttap_do_reset = 1<<0, // reset TAP to run-test/idle state
mehfet_rsttap_fuse_do = 1<<1, // perform fuse check procedure (TMS pulses) on target
mehfet_rsttap_fuse_read = 1<<2, // check whether the JTAG fuse has been blown
mehfet_rsttap_highspeed = 1<<3, // can move to high-speed transport afterwards
// (fuse check procedure is max 50 kHz)
};
enum mehfet_resettap_status {
mehfet_rsttap_fuse_blown = 0x80
};
// hw routines
void mehfet_hw_init(void);
void mehfet_hw_deinit(void);
__attribute__((__const__))
enum mehfet_caps mehfet_hw_get_caps(void);
const char* /*error string, NULL if no error*/ mehfet_hw_connect(enum mehfet_conn conn);
void mehfet_hw_disconnect(void);
void mehfet_hw_delay_ms(uint32_t t);
void mehfet_hw_delay_us(uint32_t t);
void mehfet_hw_timer_start(bool us, uint32_t to_reach);
bool mehfet_hw_timer_reached(void);
void mehfet_hw_set_clkspeed(bool fast);
uint8_t mehfet_hw_get_old_lines(void);
void mehfet_hw_tdio_seq(uint32_t ncyc, bool tmslvl, const uint8_t* tdi, uint8_t* tdo);
void mehfet_hw_tms_seq(uint32_t ncyc, bool tdilvl, const uint8_t* tms);
void mehfet_hw_tclk_edge(bool newtclk);
void mehfet_hw_tclk_burst(uint32_t ncyc);
enum mehfet_resettap_status mehfet_hw_reset_tap(enum mehfet_resettap_flags flags);
uint8_t mehfet_hw_shift_ir(uint8_t newir);
// drin is not const here, as the implementation may want to shuffle stuff around
void mehfet_hw_shift_dr(uint32_t nbits, uint8_t* drin, uint8_t* drout);
static inline uint16_t mehfet_hw_shift_dr16(uint16_t newdr) {
uint8_t drin[2], drout[2];
drin[0] = (uint8_t)newdr;
drin[1] = (uint8_t)(newdr >> 8);
mehfet_hw_shift_dr(16, drin, drout);
return drout[0] | ((uint16_t)drout[1] << 8);
}
#endif

View File

@ -3,6 +3,7 @@
#include <tusb.h>
#include "mode.h"
#include "storage.h"
#include "thread.h"
#include "usbstdio.h"
#include "vnd_cfg.h"
@ -20,6 +21,8 @@ enum m_sump_feature {
msump_feat_sump = 1<<0,
};
static bool data_dirty = false;
#ifdef DBOARD_HAS_SUMP
static cothread_t sumpthread;
static uint8_t sumpstack[THREAD_STACK_SIZE];
@ -44,6 +47,16 @@ static void enter_cb(void) {
sumpthread = co_derive(sumpstack, sizeof sumpstack, sump_thread_fn);
thread_enter(sumpthread);
#endif
if (!data_dirty) { // only read when not read yet
struct mode_info mi = storage_mode_get_info(4);
if (mi.size != 0 && mi.version == 0x0010 /* TODO: version migration? */) {
uint8_t dst[1];
storage_mode_read(4, dst, 0, 1);
sump_hw_set_overclock(dst[0]);
}
}
}
static void leave_cb(void) {
#ifdef DBOARD_HAS_SUMP
@ -133,7 +146,6 @@ enum {
#define EPNUM_CDC_STDIO_NOTIF 0x85
// clang-format off
// TODO: replace magic 64s by actual buffer size macros
static const uint8_t desc_configuration[] = {
TUD_CONFIG_DESCRIPTOR(1, ITF_NUM__TOTAL, STRID_CONFIG, CONFIG_TOTAL_LEN,
TUSB_DESC_CONFIG_ATT_REMOTE_WAKEUP, 100),
@ -178,6 +190,17 @@ static void my_cdc_line_coding_cb(uint8_t itf, cdc_line_coding_t const* line_cod
}
#endif
static uint16_t my_get_size(void) { return 1; }
static void my_get_data(void* dst, size_t offset, size_t maxsize) {
(void)offset; (void)maxsize;
uint8_t* d = dst;
d[0] = sump_hw_get_overclock();
data_dirty = false;
}
static bool my_is_dirty(void) { return data_dirty; }
extern struct mode m_04_sump;
// clang-format off
struct mode m_04_sump = {
@ -188,6 +211,13 @@ struct mode m_04_sump = {
.usb_desc = desc_configuration,
.string_desc = string_desc_arr,
.storage = {
.stclass = mode_storage_32b,
.get_size = my_get_size,
.get_data = my_get_data,
.is_dirty = my_is_dirty
},
.enter = enter_cb,
.leave = leave_cb,
.task = task_cb,

View File

@ -27,7 +27,6 @@
*/
#include <assert.h>
#include <tusb.h>
#include "alloc.h"

View File

@ -7,6 +7,7 @@
#include "tusb.h"
#include "mode.h"
#include "storage.h"
#include "thread.h"
#include "usbstdio.h"
#include "vnd_cfg.h"
@ -14,16 +15,6 @@
static cothread_t vndcfg_thread;
static uint8_t vndcfg_stack[THREAD_STACK_SIZE];
// shrug, somewhere
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];
}
static void vndcfg_thread_fn(void) {
vnd_cfg_init();
thread_yield();
@ -34,6 +25,8 @@ static void vndcfg_thread_fn(void) {
}
int main() {
stdio_uart_init();
thread_init();
board_init(); // tinyusb hardware support function
@ -41,15 +34,24 @@ int main() {
vndcfg_thread = co_derive(vndcfg_stack, sizeof vndcfg_stack, vndcfg_thread_fn);
thread_enter(vndcfg_thread);
modes_init();
#if defined(PERSISTENT_STORAGE) && defined(DBOARD_HAS_STORAGE)
int startupmode = storage_init();
#else
int startupmode = -1;
#endif
// FIXME: put elsewhere?
#ifdef USE_USBCDC_FOR_STDIO
stdio_usb_set_itf_num(0);
#endif
modes_init(startupmode);
if (mode_current) mode_current->enter();
tusb_init();
// FIXME: put elsewhere?
#ifdef USE_USBCDC_FOR_STDIO
stdio_usb_set_itf_num(0);
stdio_usb_init();
#endif

View File

@ -9,6 +9,8 @@
#include "tusb_config.h"
#include <tusb.h>
#include "storage.h"
// clang-format off
struct mode {
@ -19,6 +21,8 @@ struct mode {
const uint8_t* usb_desc;
const char** string_desc;
struct mode_storage storage;
void (*enter)(void); // claim required hardware. no tusb calls here please
void (*leave)(void); // release current in-use hardware. no tusb calls here please
void (*task )(void);
@ -35,7 +39,7 @@ struct mode {
void (*tud_cdc_line_coding_cb)(uint8_t itf, cdc_line_coding_t const* line_coding);
#endif
//#if CFG_TUD_VENDOR > 0
bool (*tud_vendor_control_xfer_cb)(uint8_t rhport, uint8_t ep_addr,
bool (*tud_vendor_control_xfer_cb)(uint8_t rhport, uint8_t stage,
tusb_control_request_t const* req);
//#endif
@ -46,8 +50,9 @@ struct mode {
};
// call this BEFORE tusb_init!
void modes_init(void);
void modes_init(int init_mode);
// IMMEDIATELY switches mode. use "mode_next_id' to do a graceful switch
void modes_switch(uint8_t newmode);
extern int mode_current_id;

View File

@ -5,16 +5,18 @@
#include "alloc.h"
#include "board.h" /* bsp_reset_bootloader() */
#include "mode.h"
#include "storage.h"
extern struct mode m_01_default, m_02_isp, m_03_jscan, m_04_sump;
extern struct mode m_01_default, m_03_jscan, m_04_sump, m_05_ftdi;
// clang-format off
const struct mode* const mode_list[16] = {
NULL, // dummy 0 entry
&m_01_default, // entry 1 CANNOT be NULL!
&m_02_isp, //NULL, // mode 2 (hw chip programming stuff) not implemented yet
NULL, // mode 2 (hw chip programming stuff) not implemented yet
&m_03_jscan,
&m_04_sump,
&m_05_ftdi,
NULL, // terminating entry
};
// clang-format on
@ -37,9 +39,13 @@ enum tusbgot_index {
#define ORDEF(a,b) ((a != NULL) ? a : b)
void modes_init(void) {
// switch to the default mode, but without doing a USB reboot thing
mode_current_id = &mode_default - mode_list;
void modes_init(int newid) {
if (newid < 0 || newid >= 16 || mode_list[newid] == NULL) {
// switch to the default mode, but without doing a USB reboot thing
mode_current_id = &mode_default - mode_list;
} else {
mode_current_id = newid;
}
mode_next_id = -1;
//if (!mode_default) return;
@ -94,6 +100,8 @@ void modes_switch(uint8_t newmode) {
// maybe wait a second or so for the host to notice this
sleep_ms(500/2);
printf("disconnect\n");
if (newmode == 0) bsp_reset_bootloader();
// now apply the new tusb settings
@ -159,11 +167,25 @@ void modes_switch(uint8_t newmode) {
// clang-format on
}
printf("reconnect\n");
// and reconnect
tud_connect();
sleep_ms(500/2);
//while (!tud_mounted()) sleep_ms(5);
printf("enter\n");
if (mode_current) mode_current->enter();
}
#if defined(PERSISTENT_STORAGE) && defined(DBOARD_HAS_STORAGE)
void tud_umount_cb(void) {
storage_flush_data();
}
void tud_suspend_cb(bool remote_wakeup_en) {
(void)remote_wakeup_en;
storage_flush_data();
}
#endif

194
src/storage.c Normal file
View File

@ -0,0 +1,194 @@
// vim: set et:
#include "bsp-info.h"
#include "bsp-storage.h"
#include "mode.h"
#include "storage.h"
#if !defined(PERSISTENT_STORAGE) || !defined(DBOARD_HAS_STORAGE)
int storage_init(void) { return -1; }
bool storage_flush_data(void) { return false; }
struct mode_info storage_mode_get_info(int _) {
(void)_; return (struct mode_info){ .size = 0, .version = 0 };
}
void storage_mode_read(int _, void* __, size_t ___, size_t ____) {
(void)_; (void)__; (void)___; (void)____;
}
#else
#include "storage_internal.h"
uint32_t str_hash_djb2_digest(uint32_t hash, const void* data, size_t len) {
const uint8_t* d = data;
for (size_t i = 0; i < len; ++i) hash += hash * 33 + d[i];
return hash;
}
bool header_valid = false;
struct storage_header header_tmp;
uint8_t data_tmp[256];
uint16_t mode_bad = 0;
static int in_init = 0;
static void storage_init_defaults(void) {
memcpy(header_tmp.magic, STORAGE_MAGIC, STORAGE_MAGIC_LEN);
header_tmp.fwversion = STORAGE_VER;
header_tmp.curmode = mode_current_id;
header_tmp.nmodes = 0;
memset(header_tmp.reserved, 0xff, sizeof(header_tmp.reserved));
memset(header_tmp.mode_data_table, 0xff, sizeof(header_tmp.mode_data_table));
header_tmp.table_djb2 = str_hash_djb2(header_tmp.mode_data_table,
sizeof(struct mode_data)*MAX_MDT_ELEMENTS);
header_valid = true;
}
int storage_init(void) {
++in_init;
mode_bad = 0;
storage_read(&header_tmp, STORAGE_SIZE - sizeof(struct storage_header),
sizeof(struct storage_header));
bool bad = false;
if (memcmp(header_tmp.magic, STORAGE_MAGIC, STORAGE_MAGIC_LEN)) {
storage_init_defaults();
if (in_init == 1) storage_flush_data();
--in_init;
return -1;
}
if (header_tmp.fwversion != STORAGE_VER) {
// TODO: migrate... if there were any older versions
header_valid = false;
--in_init;
return -1;
}
if (header_tmp.nmodes >= 16) bad = true;
else if (str_hash_djb2(header_tmp.mode_data_table,
sizeof(struct mode_data)*MAX_MDT_ELEMENTS) != header_tmp.table_djb2)
bad = true;
else if (header_tmp.curmode >= 16 || header_tmp.curmode == 0
|| mode_list[header_tmp.curmode] == NULL)
bad = true;
if (bad) {
storage_init_defaults();
if (in_init == 1) storage_flush_data();
--in_init;
return -1;
}
header_valid = true;
--in_init;
return header_tmp.curmode;
}
struct mode_info storage_mode_get_info(int mode) {
#define DEF_RETVAL ({ \
if (mode < 16 && mode > 0 && header_valid && header_tmp.nmodes != 0) mode_bad |= 1<<mode; \
(struct mode_info){ .size = 0, .version = 0 }; \
}) \
if (mode >= 16 || !header_valid || mode <= 0 || header_tmp.nmodes == 0)
return DEF_RETVAL;
for (size_t i = 0; i < header_tmp.nmodes; ++i) {
struct mode_data md = header_tmp.mode_data_table[i];
int mdmode = (uint8_t)(md.offsetandmode >> 28);
uint16_t mdsize = md.datasize;
uint32_t mdoffset = md.offsetandmode & ((1<<28)-1);
if (mdmode != mode) continue;
if (mdsize == 0xffff || md.version == 0xffff || md.offsetandmode == 0xffffffffu)
continue; // empty (wut?)
// found it!
if (mdsize == 0) return DEF_RETVAL; // no data stored
if (mdoffset == 0 || mdoffset >= STORAGE_SIZE)
return DEF_RETVAL; // bad offset
// program code collision cases
if (mdoffset < storage_get_program_offset() && mdoffset+mdsize >=
storage_get_program_offset()) return DEF_RETVAL;
if (mdoffset < storage_get_program_offset()+storage_get_program_size()
&& mdoffset+mdsize >= storage_get_program_offset()+storage_get_program_size())
return DEF_RETVAL;
if (mdoffset >= storage_get_program_offset()
&& mdoffset+mdsize <= storage_get_program_offset()+storage_get_program_size())
return DEF_RETVAL;
// now check whether the data hash is corrupted
uint32_t hash = str_hash_djb2_init();
for (size_t i = 0; i < mdsize; i += sizeof(data_tmp)) {
size_t toread = sizeof(data_tmp);
if (mdsize - i < toread) toread = mdsize - i;
storage_read(data_tmp, mdoffset + i, toread);
hash = str_hash_djb2_digest(hash, data_tmp, toread);
}
if (hash != md.data_djb2) return DEF_RETVAL;
return (struct mode_info) {
.size = mdsize,
.version = md.version
};
}
return DEF_RETVAL;
#undef DEF_RETVAL
}
void storage_mode_read(int mode, void* dst, size_t offset, size_t maxlen) {
<<<<<<< HEAD
if (mode >= 16 || !header_valid || mode <= 0 || header_tmp.nmodes == 0) return;
=======
if (mode >= 16 || !header_valid || mode <= 0) return;
>>>>>>> 62e3181 (PERSISTENT_STORAGE usage flag)
for (size_t i = 0; i < header_tmp.nmodes; ++i) {
struct mode_data md = header_tmp.mode_data_table[i];
int mdmode = (uint8_t)(md.offsetandmode >> 28);
uint16_t mdsize = md.datasize;
uint32_t mdoffset = md.offsetandmode & ((1<<28)-1);
if (mdmode != mode) continue;
if (mdsize == 0xffff || md.version == 0xffff || md.offsetandmode == 0xffffffffu)
continue; // empty (wut?)
// found it!
if (mdsize == 0) { mode_bad |= 1<<mode; return; /* no data stored */ }
if (mdoffset == 0 || mdoffset >= STORAGE_SIZE) {
mode_bad |= 1<<mode; return; /* bad offset */
}
// program code collision cases
if (mdoffset < storage_get_program_offset() && mdoffset+mdsize >=
storage_get_program_offset()) { mode_bad |= 1<<mode; return; }
if (mdoffset < storage_get_program_offset()+storage_get_program_size()
&& mdoffset+mdsize >= storage_get_program_offset()+storage_get_program_size()) {
mode_bad |= 1<<mode; return;
}
if (mdoffset >= storage_get_program_offset()
&& mdoffset+mdsize <= storage_get_program_offset()+storage_get_program_size()) {
mode_bad |= 1<<mode; return;
}
if (offset >= mdsize) return;
// skip hash check in this case
storage_read(dst, mdoffset + offset, mdsize < maxlen ? mdsize : maxlen);
return;
}
}
#endif /* BOARD_HAS_STORAGE */

50
src/storage.h Normal file
View File

@ -0,0 +1,50 @@
// vim: set et:
#ifndef STORAGE_H_
#define STORAGE_H_
#include <stddef.h>
#include <stdint.h>
#define STORAGE_VER 0x0010
enum mode_storage_class {
mode_storage_none, // this mode has no storage
mode_storage_32b , // this mode typically won't use more than 32 bytes of data
mode_storage_128b, // this mode typically won't use more than 128 bytes of data
mode_storage_512b, // this mode typically won't use more than 512 bytes of data
mode_storage_big // this mode uses a lot of data
};
// mode callbacks used by the storage subsystem
struct mode_storage {
enum mode_storage_class stclass;
uint16_t (*get_size)(void);
// if stclass < 512b, offset & maxsize can be ignored
void (*get_data)(void* dst, size_t offset, size_t maxsize);
bool (*is_dirty)(void); // if data was changed since last mode_read/get_data call
};
struct mode_info {
uint32_t size;
uint16_t version;
};
// functions mode-specific code can use to retrieve the save data
struct mode_info storage_mode_get_info(int mode); // returns size=0 for none found
void storage_mode_read(int mode, void* dst, size_t offset, size_t maxlen);
// global functions
// reads all data, creates table if needed
int storage_init(void);
// flush edits if anything has been edited
bool storage_flush_data(void);
bool storage_priv_mode_has(int mode);
void* storage_priv_get_header_ptr(void);
#endif

74
src/storage_internal.h Normal file
View File

@ -0,0 +1,74 @@
// vim: set et:
#ifndef STORAGE_INTERNAL_H_
#define STORAGE_INTERNAL_H_
inline static uint32_t str_hash_djb2_init(void) {
return 5381;
}
uint32_t str_hash_djb2_digest(uint32_t hash, const void* data, size_t len);
inline static uint32_t str_hash_djb2(const void* data, size_t len) {
return str_hash_djb2_digest(str_hash_djb2_init(), data, len);
}
/*
* storage header (in last 256b of flash) (endianness is whatever is native to the device):
*
* 3 7 b f
* --+----------------------------------------------------+
* 0 | f0 9f 8f b3 ef b8 8f e2 80 8d e2 9a a7 ef b8 8f | magic number
* 10 | fwver mm nm <reserved (0xff) > <tbl djb2> | fwver: current version (bcd)
* 20 | <mode data table ...> | mm: current mode, nm: number of modes saved (~size of "mode data table")
* 30 | | tbl djb2: djb2-hash of "mode data table" (entire table, not nmodes)
* 40 | <reserved (0xff) ...> | empty "mode data table" entries are 0xff-filled
*
* mode data table: array of:
* struct mode_data {
* uint16_t version;
* uint16_t datasize;
* uint28_t flashoffset; // from beginning of flash
* uint4_t modeid;
* uint32_t data_djb2;
* };
*
* mode data blobs are typically allocated smallest to largest (according to
* mode_storage_class), from the last page of flash (cf. bsp-storage.h) down
*/
#define STORAGE_MAGIC "\xf0\x9f\x8f\xb3\xef\xb8\x8f\xe2\x80\x8d\xe2\x9a\xa7\xef\xb8\x8f"
#define STORAGE_MAGIC_LEN 16
__attribute__((__packed__)) struct mode_data {
uint16_t version;
uint16_t datasize;
uint32_t offsetandmode; // mode ID stored in MSNybble
uint32_t data_djb2;
};
#define MAX_MDT_ELEMENTS ((256 - 64) / sizeof(struct mode_data)) /* should be 16 */
__attribute__((__packed__)) struct storage_header {
// +0
uint8_t magic[STORAGE_MAGIC_LEN];
// +16
uint16_t fwversion;
uint8_t curmode;
uint8_t nmodes; // *stored* modes, not modes it knows of
uint8_t reserved[8];
uint32_t table_djb2;
// +32
struct mode_data mode_data_table[MAX_MDT_ELEMENTS]; // 192 bytes in size
// +224
uint8_t reserved2[32];
};
// TODO: static assert sizeof(struct storage_header) == 256
// TODO: static assert MAX_MDT_ELEMENTS >= 16
extern bool header_valid;
extern struct storage_header header_tmp;
extern uint8_t data_tmp[256];
extern uint16_t mode_bad;
#endif

172
src/storage_save.c Normal file
View File

@ -0,0 +1,172 @@
// vim: set et:
#include "bsp-info.h"
#include "bsp-storage.h"
#include "mode.h"
#include "storage.h"
#if defined(PERSISTENT_STORAGE) && defined(DBOARD_HAS_STORAGE)
#include "storage_internal.h"
bool storage_priv_mode_has(int i) {
if (mode_list[i]->storage.stclass == mode_storage_none) return false;
if (mode_list[i]->storage.get_size == NULL) return false;
if (mode_list[i]->storage.get_data == NULL) return false;
if (mode_list[i]->storage.is_dirty == NULL) return false;
return true;
}
void* storage_priv_get_header_ptr(void) {
return &header_tmp;
}
static struct mode_storage msto[16];
static size_t storage_allocate_new(void) {
static const size_t stclass_sz[] = { 0, 32, 128, 512, 0xffffffff };
memcpy(header_tmp.magic, STORAGE_MAGIC, STORAGE_MAGIC_LEN);
memset(header_tmp.reserved, 0xff, sizeof(header_tmp.reserved));
memset(header_tmp.reserved2, 0xff, sizeof(header_tmp.reserved2));
memset(header_tmp.mode_data_table, 0xff, sizeof(header_tmp.mode_data_table));
header_tmp.fwversion = STORAGE_VER;
header_tmp.curmode = mode_current_id;
header_tmp.nmodes = 0;
size_t current_page = STORAGE_SIZE - STORAGE_ERASEWRITE_ALIGN,
current_page_end = STORAGE_SIZE - sizeof(struct storage_header);
size_t current_wrhead = current_page;
size_t npages = 1;
for (enum mode_storage_class stcls = mode_storage_32b; stcls <= mode_storage_big; ++stcls) {
for (int mode = 1; mode < 16; ++mode) {
if (mode_list[mode] == NULL || !storage_priv_mode_has(mode)) continue;
if (mode_list[mode]->storage.stclass != stcls) continue;
// too big for the class? don't write the data, then
uint16_t dsize = mode_list[mode]->storage.get_size();
if (dsize > stclass_sz[stcls]) continue;
if (current_wrhead + dsize > current_page_end) { // FIXME: data that is >1 page size (do we want to support this?)
current_page_end = current_page;
current_page -= STORAGE_ERASEWRITE_ALIGN;
current_wrhead = current_page;
++npages;
if (current_page < storage_get_program_offset() + storage_get_program_size())
return 0; // welp, out of space
}
struct mode_data* md = &header_tmp.mode_data_table[header_tmp.nmodes];
md->version = mode_list[mode]->version;
md->datasize = dsize;
md->offsetandmode = current_wrhead | ((uint32_t)mode << 28);
msto[header_tmp.nmodes] = mode_list[mode]->storage; // copy to RAM because mode_list is in rodata!
current_wrhead += stclass_sz[stcls];
uint32_t hash = str_hash_djb2_init();
for (size_t i = 0; i < dsize; i += sizeof(data_tmp)) {
size_t tohash = sizeof(data_tmp);
if (dsize - i < tohash) tohash = dsize - i;
mode_list[mode]->storage.get_data(data_tmp, i, tohash);
hash = str_hash_djb2_digest(hash, data_tmp, tohash);
}
md->data_djb2 = hash;
++header_tmp.nmodes;
}
}
header_tmp.table_djb2 = str_hash_djb2(header_tmp.mode_data_table,
sizeof(header_tmp.mode_data_table));
return npages;
}
static void st_safe_memcpy(void* dst, const void* src, size_t size) {
const uint64_t* s = src;
uint64_t* d = dst;
for (size_t i = 0; i < (size>>3); ++i) {
d[i] = s[i];
asm volatile("":::"memory");
}
if (size & 7) {
for (size_t i = (size>>3)<<3; i < size; ++i) {
((uint8_t*)dst)[i] = ((const uint8_t*)src)[i];
asm volatile("":::"memory");
}
}
}
static void storage_serialize_xip(size_t page,
size_t pagestart, size_t pageend, void* dest) {
if (page == 0) {
st_safe_memcpy((uint8_t*)dest + pageend - pagestart,
&header_tmp, sizeof(struct storage_header));
}
for (size_t i = 0; i < header_tmp.nmodes; ++i) {
struct mode_data* md = &header_tmp.mode_data_table[i];
uint32_t mdoffset = md->offsetandmode & ((1<<28)-1);
uint32_t mdsize = md->datasize;
if (mdoffset < pagestart || mdoffset + mdsize >= pageend)
continue; // must be fully within the page
msto[i].get_data((uint8_t*)dest + mdoffset - pagestart, 0, mdsize);
}
}
static void storage_write_data(void) {
size_t npages = storage_allocate_new();
if (npages == 0) {
storage_init();
return; // TODO: error, somehow
}
static uint8_t base[STORAGE_ERASEWRITE_ALIGN]; // TODO FIXME: HUGE RAM HOG!
size_t current_page = STORAGE_SIZE - STORAGE_ERASEWRITE_ALIGN,
current_page_end = STORAGE_SIZE - sizeof(struct storage_header);
for (size_t page = 0; page < npages; ++page) {
storage_serialize_xip(page, current_page, current_page_end, base);
if (!storage_erasewrite(current_page, base, STORAGE_ERASEWRITE_ALIGN)) {
storage_init();
return; // TODO: error, somehow
}
current_page_end = current_page;
current_page -= STORAGE_ERASEWRITE_ALIGN;
}
// TODO:
// * save on a timer event?
// * try to save when unplugging???
}
bool storage_flush_data(void) {
if (mode_bad != 0 || mode_current_id != header_tmp.curmode) {
storage_write_data();
return true;
} else for (int i = 1; i < 16; ++i) {
if (mode_list[i] == NULL || !storage_priv_mode_has(i)) continue;
if (mode_list[i]->storage.is_dirty()) {
storage_write_data();
return true;
}
}
return false;
}
#endif

View File

@ -51,14 +51,14 @@ extern "C" {
// Default to Highspeed for MCU with internal HighSpeed PHY (can be port specific), otherwise
// FullSpeed
#ifndef BOARD_DEVICE_RHPORT_SPEED
/*#if (CFG_TUSB_MCU == OPT_MCU_LPC18XX || CFG_TUSB_MCU == OPT_MCU_LPC43XX || \
#if (CFG_TUSB_MCU == OPT_MCU_LPC18XX || CFG_TUSB_MCU == OPT_MCU_LPC43XX || \
CFG_TUSB_MCU == OPT_MCU_MIMXRT10XX || CFG_TUSB_MCU == OPT_MCU_NUC505 || \
CFG_TUSB_MCU == OPT_MCU_CXD56) \
#define BOARD_DEVICE_RHPORT_SPEED OPT_MODE_HIGH_SPEED
#else*/
#else
#define BOARD_DEVICE_RHPORT_SPEED OPT_MODE_FULL_SPEED
//#endif
#endif
#endif
// Device mode with rhport and speed defined by board.mk
@ -106,7 +106,7 @@ extern "C" {
//------------- CLASS -------------//
#define CFG_TUD_MSC 0
#define CFG_TUD_MIDI 0
#define CFG_TUD_ECM_RNDIS 0
#define CFG_TUD_NET 0
// see also: bsp/<family>/bsp-info.h
#define CFG_TUD_HID_EP_BUFSIZE 64

View File

@ -13,5 +13,7 @@ void stdio_usb_set_itf_num(int itf);
void stdio_usb_line_coding_cb(cdc_line_coding_t const* line_coding);
#endif
void stdio_uart_init(void);
#endif

View File

@ -15,7 +15,5 @@ uint8_t get_unique_id_u8 (uint8_t * desc_str);
uint8_t get_unique_id_u16(uint16_t* desc_str);
// clang-format on
uint8_t bitswap(uint8_t in);
#endif

View File

@ -12,12 +12,14 @@
#if CFG_TUD_VENDOR > 0
static uint8_t rx_buf[CFG_TUD_VENDOR_TX_BUFSIZE];
static uint8_t tx_buf[CFG_TUD_VENDOR_RX_BUFSIZE];
static uint8_t tx_buf[CFG_TUD_VENDOR_TX_BUFSIZE];
static uint32_t rxavail, rxpos, txpos;
static int VND_N_CFG = 0;
extern uint8_t data_tmp[256];
void vnd_cfg_init(void) {
rxavail = 0;
rxpos = 0;
@ -30,7 +32,6 @@ void vnd_cfg_set_itf_num(int itf) {
VND_N_CFG = itf;
}
// TODO: this is duplicated several times over the codebase, maybe reduce this
uint8_t vnd_cfg_read_byte(void) {
while (rxavail <= 0) {
if (!tud_vendor_n_mounted(VND_N_CFG) || !tud_vendor_n_available(VND_N_CFG)) {
@ -126,6 +127,8 @@ void vnd_cfg_task(void) {
uint8_t cmd = vnd_cfg_read_byte();
uint8_t verbuf[2];
//printf("vcfg %02x\n", cmd);
if (cmd & 0xf0) {
uint8_t mode = (uint8_t)(cmd & 0xf0) >> 4;
uint8_t mcmd = cmd & 0x0f;
@ -192,11 +195,54 @@ void vnd_cfg_task(void) {
case cfg_cmd_get_infostr:
vnd_cfg_write_str(cfg_resp_ok, INFO_PRODUCT(INFO_BOARDNAME));
break;
#if defined(PERSISTENT_STORAGE) && defined(DBOARD_HAS_STORAGE)
case cfg_cmd_storage_get_header:
vnd_cfg_write_resp(cfg_resp_ok, 256, storage_priv_get_header_ptr());
break;
case cfg_cmd_storage_get_modedata:
verbuf[0] = vnd_cfg_read_byte();
if (verbuf[0] == 0 || verbuf[0] >= 16 || mode_list[verbuf[0]] == NULL) {
vnd_cfg_write_resp(cfg_resp_nosuchmode, 0, NULL);
} else if (!storage_priv_mode_has(verbuf[0])) {
vnd_cfg_write_resp(cfg_resp_badarg, 0, NULL);
} else {
uint32_t len = storage_mode_get_info(verbuf[0]).size;
vnd_cfg_write_byte(cfg_resp_ok);
if (len < (1<<7)) {
vnd_cfg_write_byte(len);
} else if (len < (1<<14)) {
vnd_cfg_write_byte((len & 0x7f) | 0x80);
vnd_cfg_write_byte((len >> 7) & 0x7f);
} else {
vnd_cfg_write_byte((len & 0x7f) | 0x80);
vnd_cfg_write_byte(((len >> 7) & 0x7f) | 0x80);
vnd_cfg_write_byte(((len >> 14) & 0x7f));
}
for (size_t i = 0; i < len; i += sizeof data_tmp) {
size_t tosend = sizeof data_tmp;
if (tosend > len - i) tosend = len - i;
storage_mode_read(verbuf[0], data_tmp, i, tosend);
for (size_t ii = 0; ii < tosend; ++ii)
vnd_cfg_write_byte(data_tmp[ii]);
}
vnd_cfg_write_flush();
}
break;
case cfg_cmd_storage_flush_data:
verbuf[0] = storage_flush_data() ? 1 : 0;
vnd_cfg_write_resp(cfg_resp_ok, 1, verbuf);
break;
#endif
default:
vnd_cfg_write_resp(cfg_resp_illcmd, 0, NULL);
break;
}
}
//printf("vnd cfg cmd=%02x done\n", cmd);
}
#else /* CFG_TUD_VENDOR == 0 */
void vnd_cfg_init(void) { }

View File

@ -30,6 +30,12 @@ enum cfg_cmd {
cfg_cmd_get_cur_mode = 0x02,
cfg_cmd_set_cur_mode = 0x03,
cfg_cmd_get_infostr = 0x04,
#if defined(PERSISTENT_STORAGE) && defined(DBOARD_HAS_STORAGE)
cfg_cmd_storage_get_header = 0x0c,
cfg_cmd_storage_get_modedata = 0x0d,
cfg_cmd_storage_flush_data = 0x0e,
#endif
};
// common commands for every mode

@ -1 +1 @@
Subproject commit 4bfab30c02279a0530e1a56f4a7c539f2d35a293
Subproject commit d49938d0f5052bce70e55c652b657c0a6a7e84fe