diff --git a/fw/programmer/.gitignore b/fw/programmer/.gitignore new file mode 100644 index 0000000..6335249 --- /dev/null +++ b/fw/programmer/.gitignore @@ -0,0 +1,5 @@ +.pio +.zed +.vscode +.cache +compile_commands.json diff --git a/fw/programmer/platformio.ini b/fw/programmer/platformio.ini new file mode 100644 index 0000000..1308496 --- /dev/null +++ b/fw/programmer/platformio.ini @@ -0,0 +1,14 @@ +; PlatformIO Project Configuration File +; +; Build options: build flags, source filter +; Upload options: custom upload port, speed and extra flags +; Library options: dependencies, extra library storages +; Advanced options: extra scripting +; +; Please visit documentation for the other options and examples +; https://docs.platformio.org/page/projectconf.html + +[env:default] +platform = https://github.com/maxgerhardt/platform-raspberrypi +framework = arduino +board = rpipico diff --git a/fw/programmer/src/bitbang_rvswdio.h b/fw/programmer/src/bitbang_rvswdio.h new file mode 100644 index 0000000..09011d5 --- /dev/null +++ b/fw/programmer/src/bitbang_rvswdio.h @@ -0,0 +1,1096 @@ +// CH32V003 SWIO / CH32Vxxx / CH32Xxxx bit banging minimal reference +// implementation for bit banged IO or primitive IO. +// +// You will need to implement some of the basic functions. Example +// implementations for the functions on the ESP32-S2 are included. +// +// Copyright 2023-2024 <>< Charles Lohr, May be licensed under the MIT/x11 or +// NewBSD licenses. You choose. (Can be included in commercial and copyleft work) +// +// This file is original work. +// +// Mostly tested, though, not may not be perfect. Expect to tweak some things. +// The 003 code is pretty hardened, the other code may not be. +// +// Originally the only resources used to produce this file were publicly available docs +// and playing with the chips. The only leveraged tool was this: +// https://github.com/perigoso/sigrok-rvswd + +#ifndef _WCH_RVSWD_RVSWIO_H +#define _WCH_RVSWD_RVSWIO_H + +#include +#include + +// This is a hacky thing, but if you are laaaaazzzyyyy and don't want to add a 10k +// resistor, youcan do this. It glitches the line high very, very briefly. +// Enable for when you don't have a 10k pull-upand are relying on the internal pull-up. +// WARNING: If you set this, you should set the drive current to 5mA. This is +// only useful / needed if you are using the built-in functions. +//#define R_GLITCH_HIGH +//#define RUNNING_ON_ESP32S2 +//#define BB_PRINTF_DEBUG uprintf + +// You should interface to this file via these functions + +enum RiscVChip { + CHIP_UNKNOWN = 0x00, + CHIP_CH32V10x = 0x01, + CHIP_CH57x = 0x02, + CHIP_CH56x = 0x03, + CHIP_CH32V20x = 0x05, + CHIP_CH32V30x = 0x06, + CHIP_CH58x = 0x07, + CHIP_CH32V003 = 0x09, + CHIP_CH59x = 0x0b, + CHIP_CH643 = 0x0c, + CHIP_CH32X03x = 0x0d, + CHIP_CH32L10x = 0x0e, + CHIP_CH564 = 0x0f, + + CHIP_CH32V00x = 0x4e, + + CHIP_CH570 = 0x8b, + CHIP_CH585 = 0x4b, + CHIP_CH645 = 0x46, + CHIP_CH641 = 0x49, + CHIP_CH32V317 = 0x86, + CHIP_CH32M030 = 0x8e, +}; + +enum MemoryArea { + DEFAULT_AREA = 0, + PROGRAM_AREA = 1, + BOOTLOADER_AREA = 2, + OPTIONS_AREA = 3, + EEPROM_AREA = 4, + RAM_AREA = 5, +}; + +struct SWIOState +{ + int opmode; // 0 for undefined, 1 for SWIO, 2 for SWD + enum RiscVChip target_chip_type; + int sectorsize; + + // Zero the rest of the structure. + uint32_t statetag; + uint32_t lastwriteflags; + uint32_t currentstateval; + uint32_t flash_unlocked; + uint32_t autoincrement; + uint32_t clock_set; + uint32_t no_autoexec; +}; + +#define STTAG( x ) (*((uint32_t*)(x))) + + +// Provided Basic functions +static inline void ConfigureIOForRVSWD(void); +static inline void ConfigureIOForRVSWIO(void); +static void MCFWriteReg32( struct SWIOState * state, uint8_t command, uint32_t value ); +static int MCFReadReg32( struct SWIOState * state, uint8_t command, uint32_t * value ); + +// More advanced functions built on lower level PHY. +static int InitializeSWDSWIO( struct SWIOState * state ); +static int ReadWord( struct SWIOState * state, uint32_t word, uint32_t * ret ); +static int WriteWord( struct SWIOState * state, uint32_t word, uint32_t val ); +static int WaitForFlash( struct SWIOState * state ); +static int WaitForDoneOp( struct SWIOState * state ); +static int WriteBlock( struct SWIOState * iss, uint32_t address_to_write, uint8_t * data, uint8_t len, uint8_t erase ); +static int UnlockFlash( struct SWIOState * iss ); +static int EraseFlash( struct SWIOState * iss, uint32_t address, uint32_t length, int type ); +static void ResetInternalProgrammingState( struct SWIOState * iss ); +static int PollTerminal( struct SWIOState * iss, uint8_t * buffer, int maxlen, uint32_t leavevalA, uint32_t leavevalB ); + + +#define BDMDATA0 0x04 +#define BDMDATA1 0x05 +#define DMCONTROL 0x10 +#define DMSTATUS 0x11 +#define DMHARTINFO 0x12 +#define DMABSTRACTCS 0x16 +#define DMCOMMAND 0x17 +#define DMABSTRACTAUTO 0x18 +#define DMPROGBUF0 0x20 +#define DMPROGBUF1 0x21 +#define DMPROGBUF2 0x22 +#define DMPROGBUF3 0x23 +#define DMPROGBUF4 0x24 +#define DMPROGBUF5 0x25 +#define DMPROGBUF6 0x26 +#define DMPROGBUF7 0x27 + +#define DMCPBR 0x7C +#define DMCFGR 0x7D +#define DMSHDWCFGR 0x7E + +#ifndef __CH32V00x_H +#define FLASH_STATR_WRPRTERR ((uint8_t)0x10) +#define CR_PAGE_PG ((uint32_t)0x00010000) +#define CR_BUF_LOAD ((uint32_t)0x00040000) +#ifndef FLASH_CTLR_MER +#define FLASH_CTLR_MER ((uint16_t)0x0004) /* Mass Erase */ +#endif +#define CR_STRT_Set ((uint32_t)0x00000040) +#define CR_PAGE_ER ((uint32_t)0x00020000) +#define CR_BUF_RST ((uint32_t)0x00080000) +#endif + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// High level functions +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +static int InitializeSWDSWIO( struct SWIOState * state ) +{ + for( int timeout = 20; timeout; timeout-- ) + { + //printf( "CFG FOR RV\n" ); + ConfigureIOForRVSWIO(); + //printf( "DONE CFG FOR RV\n" ); + + // Careful - don't halt the part, we might just want to attach for a debug printf or something. + state->target_chip_type = CHIP_UNKNOWN; + state->sectorsize = 64; + + state->opmode = 1; // Try SWIO first + // First try to see if there is an 003. + MCFWriteReg32( state, DMSHDWCFGR, 0x5aa50000 | (1<<10) ); // Shadow Config Reg + MCFWriteReg32( state, DMCFGR, 0x5aa50000 | (1<<10) ); // CFGR (1<<10 == Allow output from slave) + MCFWriteReg32( state, DMSHDWCFGR, 0x5aa50000 | (1<<10) ); // Try twice just in case + MCFWriteReg32( state, DMCFGR, 0x5aa50000 | (1<<10) ); + + MCFWriteReg32( state, DMCONTROL, 0x00000001 ); + MCFWriteReg32( state, DMCONTROL, 0x00000001 ); + + // See if we can see a chip here... + uint32_t value = 0; + int readdm = MCFReadReg32( state, DMCFGR, &value ); + if( readdm == 0 && ( value & 0xffff0000 ) == ( 0x5aa50000 ) ) + { + BB_PRINTF_DEBUG( "Found RVSWIO interface.\n" ); + return 0; + } + + //printf( "CFG FOR SWD\n" ); + ConfigureIOForRVSWD(); + //printf( "DONE CFG FOR SWD\n" ); + + //Otherwise Maybe it's SWD? + state->opmode = 2; + + MCFWriteReg32( state, DMCONTROL, 0x00000001 ); + MCFWriteReg32( state, DMCONTROL, 0x00000001 ); + + uint32_t dmstatus, dmcontrol; + if( MCFReadReg32( state, DMSTATUS, &dmstatus ) != 0 || + MCFReadReg32( state, DMCONTROL, &dmcontrol ) != 0 ) + { + //BB_PRINTF_DEBUG( "Could not read from RVSWD connection\n" ); + state->opmode = 0; + continue; + } + + // See if we can see a chip here... + if( ( ( ( dmstatus >> 8 ) & 0xf ) != 0x0c && + ( ( dmstatus >> 8 ) & 0xf ) != 0x03 ) || + dmcontrol != 1 ) + { + //BB_PRINTF_DEBUG( "DMSTATUS invalid (Probably no RVSWD chip)\n" ); + state->opmode = 0; + continue; + } + + MCFWriteReg32( state, DMABSTRACTCS, 0x08000302 ); // Clear out abstractcs register. + BB_PRINTF_DEBUG( "Found RVSWD interface\n" ); + return 0; + } + //printf( "TIMEOUT\n" ); + return -55; +} + +static int DetermineChipTypeAndSectorInfo( struct SWIOState * iss, uint8_t * reply) +{ + struct SWIOState * dev = iss; + if( iss->target_chip_type == CHIP_UNKNOWN || reply != NULL ) + { + if( iss->opmode == 0 ) + { + int r = InitializeSWDSWIO( iss ); + if( r ) return 0x11; + } + uint32_t rr; + if( MCFReadReg32( dev, DMHARTINFO, &rr ) ) + { + BB_PRINTF_DEBUG( "Error: Could not get hart info.\n" ); + return 0x12; + } + + MCFWriteReg32( dev, DMCONTROL, 0x80000001 ); // Make the debug module work properly. + MCFWriteReg32( dev, DMCONTROL, 0x80000001 ); // Initiate halt request. + + // Tricky, this function needs to clean everything up because it may be used entering debugger. + uint32_t old_data0; + MCFReadReg32( dev, BDMDATA0, &old_data0 ); + MCFWriteReg32( dev, DMCOMMAND, 0x00221008 ); // Copy data from x8. + uint32_t old_x8; + MCFReadReg32( dev, BDMDATA0, &old_x8 ); + + uint32_t marchid = 0; + + MCFWriteReg32( dev, DMABSTRACTCS, 0x08000700 ); // Clear out any dmabstractcs errors. + + MCFWriteReg32( dev, DMABSTRACTAUTO, 0x00000000 ); + MCFWriteReg32( dev, DMCOMMAND, 0x00220000 | 0xf12 ); + MCFWriteReg32( dev, DMCOMMAND, 0x00220000 | 0xf12 ); // Need to double-read, not sure why. + MCFReadReg32( dev, BDMDATA0, &marchid ); + MCFWriteReg32( dev, DMPROGBUF0, 0x90024000 ); // c.ebreak <<== c.lw x8, 0(x8) + + uint32_t chip_id = 0; + uint32_t vendor_bytes = 0; + uint32_t sevenf_id = 0; + int read_protection = 0; + uint8_t ch5xx = 0; + MCFReadReg32( dev, 0x7f, &sevenf_id ); + + if( sevenf_id == 0 ) + { + // Need to load new progbuf because we're reading 1 byte now + MCFWriteReg32( dev, DMPROGBUF0, 0x00040403 ); // lb x8, 0(x8) + MCFWriteReg32( dev, DMPROGBUF1, 0x00100073 ); // c.ebreak + MCFWriteReg32( dev, BDMDATA0, 0x40001041 ); // Special chip ID location. + MCFWriteReg32( dev, DMCOMMAND, 0x00271008 ); // Copy data to x8, and execute. + WaitForDoneOp( dev ); + MCFWriteReg32( dev, DMCOMMAND, 0x00221008 ); // Copy data from x8. + MCFReadReg32( dev, BDMDATA0, &chip_id ); + chip_id = chip_id & 0xff; + + // Looks like a CH32V103 or a CH56x + if( chip_id == 0 ) + { + // First check for CH56x + MCFWriteReg32( dev, BDMDATA0, 0x40001001 ); + MCFWriteReg32( dev, DMCOMMAND, 0x00271008 ); // Copy data to x8, and execute. + WaitForDoneOp( dev ); + MCFWriteReg32( dev, DMCOMMAND, 0x00221008 ); // Copy data from x8. + MCFReadReg32( dev, BDMDATA0, &chip_id ); + MCFWriteReg32( dev, BDMDATA0, 0x40001002 ); // Special chip ID location. + MCFWriteReg32( dev, DMCOMMAND, 0x00271008 ); // Copy data to x8, and execute. + WaitForDoneOp( dev ); + MCFWriteReg32( dev, DMCOMMAND, 0x00221008 ); // Copy data from x8. + MCFReadReg32( dev, BDMDATA0, &vendor_bytes ); + + if( (vendor_bytes & 0xff) == 2 && ((chip_id & 0xff) == 65 || (chip_id & 0xff) == 69) ) + { + iss->target_chip_type = CHIP_CH56x; + chip_id = 0x500 | (chip_id & 0xff); + goto chip_identified; + } + + // Now actually check for CH32V103 + MCFWriteReg32( dev, DMPROGBUF0, 0x90024000 ); // c.ebreak <<== c.lw x8, 0(x8) + MCFWriteReg32( dev, BDMDATA0, 0x1ffff880 ); // Special chip ID location. + MCFWriteReg32( dev, DMCOMMAND, 0x00271008 ); // Copy data to x8, and execute. + WaitForDoneOp( dev ); + MCFWriteReg32( dev, DMCOMMAND, 0x00221008 ); // Copy data from x8. + MCFReadReg32( dev, BDMDATA0, &chip_id ); + MCFWriteReg32( dev, BDMDATA0, 0x1ffff884 ); // Special chip ID location. + MCFWriteReg32( dev, DMCOMMAND, 0x00271008 ); // Copy data to x8, and execute. + WaitForDoneOp( dev ); + MCFWriteReg32( dev, DMCOMMAND, 0x00221008 ); // Copy data from x8. + MCFReadReg32( dev, BDMDATA0, &vendor_bytes ); + + if( ((((vendor_bytes >> 16) & 0xff00) != 0x2500) && (((vendor_bytes >> 16) & 0xdf00) != 0x1500)) || chip_id != 0xdc78fe34 ) + { + uint32_t flash_obr = 0; + MCFWriteReg32( dev, BDMDATA0, 0x4002201c ); // Special chip ID location. + MCFWriteReg32( dev, DMCOMMAND, 0x00271008 ); // Copy data to x8, and execute. + WaitForDoneOp( dev ); + MCFWriteReg32( dev, DMCOMMAND, 0x00221008 ); // Copy data from x8. + MCFReadReg32( dev, BDMDATA0, &flash_obr ); + + if( (flash_obr & 3) == 2 ) + { + iss->target_chip_type = CHIP_CH32V10x; + iss->no_autoexec = 1; + iss->sectorsize = 128; + read_protection = 1; + chip_id = (3 << 12) | 0x103; + } + } + else + { + iss->target_chip_type = CHIP_CH32V10x; + iss->no_autoexec = 1; + iss->sectorsize = 128; + read_protection = -1; + chip_id = (3 << 12) | 0x103; + } + } + else + { + // Check for CH5xx + read_protection = -1; + if( (chip_id & 0xf0) == 0x90 ) + { + uint32_t sevenc = 0; + MCFReadReg32( dev, DMCPBR, &sevenc ); + if((sevenc & 0x30000) == 0) + { + iss->target_chip_type = CHIP_CH59x; + iss->sectorsize = 256; + ch5xx = 1; + chip_id = 0x500 | chip_id; + } + else + { + iss->target_chip_type = CHIP_CH585; + iss->no_autoexec = 1; + iss->sectorsize = 256; + ch5xx = 1; + chip_id = 0x500 | 0x85; + } + } + else + { + if( chip_id == 0x70 || chip_id == 0x72 ) + { + iss->target_chip_type = CHIP_CH570; + iss->sectorsize = 4096; + ch5xx = 1; + chip_id = 0x500 | chip_id; + } + else if( chip_id == 0x71 || chip_id == 0x73 ) + { + iss->target_chip_type = CHIP_CH57x; + iss->no_autoexec = 1; + iss->sectorsize = 256; + ch5xx = 1; + chip_id = 0x500 | chip_id; + } + else if( chip_id == 0x82 || chip_id == 0x83 ) + { + iss->target_chip_type = CHIP_CH58x; + iss->no_autoexec = 1; + iss->sectorsize = 256; + ch5xx = 1; + chip_id = 0x500 | chip_id; + } + } + } + } + else + { + uint32_t masked_id = sevenf_id & 0xfff00000; + uint32_t masked_id2 = sevenf_id & 0xfff00f00; + if( masked_id == 0x3b00000 ) + { + iss->target_chip_type = CHIP_CH32M030; + iss->sectorsize = 256; + chip_id = (2 << 12) | 0x30; + } + else if( masked_id == 0x56400000 ) + { + iss->target_chip_type = CHIP_CH564; + iss->sectorsize = 256; + chip_id = 0x564; + } + else if( masked_id == 0x31700000 ) + { + iss->target_chip_type = CHIP_CH32V317; + iss->sectorsize = 256; + chip_id = (3 << 12) | 0x317; + } + else if( masked_id == 0x00200000 || masked_id == 0x00400000 || masked_id == 0x00600000 || masked_id == 0x00700000 ) + { + iss->target_chip_type = CHIP_CH32V00x; + iss->sectorsize = 256; + chip_id = (3 << 12) | (masked_id >> 20); + } + + if( masked_id2 == 0x64100500 ) + { + iss->target_chip_type = CHIP_CH641; + iss->sectorsize = 64; + chip_id = 0x641; + } + else if( masked_id2 == 0x64300600 ) + { + iss->target_chip_type = CHIP_CH643; + iss->sectorsize = 256; + chip_id = 0x643; + } + else if( masked_id == 0x64500000 ) + { + iss->target_chip_type = CHIP_CH645; + iss->sectorsize = 256; + chip_id = 0x645; + } + else if( masked_id2 == 0x3500600 ) + { + iss->target_chip_type = CHIP_CH32X03x; + iss->sectorsize = 256; + chip_id = (4 << 12) | 0x35; + } + else if( masked_id2 == 0x10300700 ) + { + iss->target_chip_type = CHIP_CH32L10x; + iss->sectorsize = 256; + chip_id = (1 << 12) | 0x103; + } + else if( masked_id2 == 0x300500 ) + { + iss->target_chip_type = CHIP_CH32V003; + iss->sectorsize = 64; + chip_id = (3 << 12) | 0x3; + } + + if( (sevenf_id & 0x20000500) == 0x20000500 || (sevenf_id & 0x30000500) == 0x30000500 ) + { + switch ((sevenf_id & 0xfff00000) >> 20) + { + case 0x203: + case 0x208: + iss->target_chip_type = CHIP_CH32V20x; + iss->sectorsize = 256; + break; + case 0x303: + case 0x305: + case 0x307: + iss->target_chip_type = CHIP_CH32V30x; + iss->sectorsize = 256; + break; + } + chip_id = (3 << 12) | (masked_id >> 20); + } + } + +chip_identified: + + if( read_protection == 0 ) + { + uint32_t one; + int two; + MCFWriteReg32( dev, BDMDATA0, 0x4002201c ); + MCFWriteReg32( dev, DMCOMMAND, 0x00271008 ); // Copy data to x8, and execute. + WaitForDoneOp( dev ); + MCFWriteReg32( dev, DMCOMMAND, 0x00221008 ); // Copy data from x8. + MCFReadReg32( dev, BDMDATA0, &one ); + MCFWriteReg32( dev, BDMDATA0, 0x40022020 ); + MCFWriteReg32( dev, DMCOMMAND, 0x00271008 ); // Copy data to x8, and execute. + WaitForDoneOp( dev ); + MCFWriteReg32( dev, DMCOMMAND, 0x00221008 ); // Copy data from x8. + MCFReadReg32( dev, BDMDATA0, (uint32_t*)&two ); + + if( (one & 2) || two != -1 ) read_protection = 1; + } + + if( reply != NULL ) + { + reply[3] = sevenf_id >> 24; + reply[2] = sevenf_id >> 16; + reply[1] = sevenf_id >> 8; + reply[0] = sevenf_id; + reply[5] = chip_id >> 8; + reply[4] = chip_id; + if( read_protection == 1 ) reply[5] |= 0x80; + } + + // Cleanup + MCFWriteReg32( dev, BDMDATA0, old_x8 ); + MCFWriteReg32( dev, DMCOMMAND, 0x00231008 ); // Copy data to x8 + MCFWriteReg32( dev, BDMDATA0, old_data0 ); + iss->statetag = STTAG( "XXXX" ); +#if CH5xx_SUPPORT + if( ch5xx ) ch5xx_set_clock( dev, 0 ); +#endif + } + return 0; +} + +static int WaitForFlash( struct SWIOState * iss ) +{ + struct SWIOState * dev = iss; + + if( DetermineChipTypeAndSectorInfo( iss, NULL ) ) return -9; + + uint32_t rw, timeout = 0; + do + { + rw = 0; + ReadWord( dev, 0x4002200C, &rw ); // FLASH_STATR => 0x4002200C + } while( (rw & 1) && timeout++ < 2000); // BSY flag. + + WriteWord( dev, 0x4002200C, 0 ); + + if( rw & FLASH_STATR_WRPRTERR ) + return -44; + + if( rw & 1 ) + return -5; + + return 0; +} + + +static int WaitForDoneOp( struct SWIOState * iss ) +{ + int r; + uint32_t rrv; + int ret = 0; + int timeout = 1000; + struct SWIOState * dev = iss; + do + { + r = MCFReadReg32( dev, DMABSTRACTCS, &rrv ); + if( r ) return r; + if( timeout-- == 0 ) return -8; + } + while( rrv & (1<<12) ); + if( (rrv >> 8 ) & 7 ) + { + MCFWriteReg32( dev, DMABSTRACTCS, 0x00000700 ); + ret = -33; + } + return ret; +} + +static int StaticUpdatePROGBUFRegs( struct SWIOState * dev ) +{ + if( DetermineChipTypeAndSectorInfo( dev, NULL ) ) return -9; + + MCFWriteReg32( dev, DMABSTRACTAUTO, 0 ); // Disable Autoexec. + uint32_t rr; + MCFReadReg32( dev, DMHARTINFO, &rr ); + uint32_t data0offset = 0xe0000000 | ( rr & 0x7ff ); + MCFWriteReg32( dev, BDMDATA0, data0offset ); // DATA0's location in memory. (hard code to 0xe00000f4 if only working on the 003) + MCFWriteReg32( dev, DMCOMMAND, 0x0023100a ); // Copy data to x10 + MCFWriteReg32( dev, BDMDATA0, data0offset + 4 ); // DATA1's location in memory. (hard code to 0xe00000f8 if only working on the 003) + MCFWriteReg32( dev, DMCOMMAND, 0x0023100b ); // Copy data to x11 + MCFWriteReg32( dev, BDMDATA0, 0x4002200c ); //FLASH->STATR (note add 4 to FLASH->CTLR) + MCFWriteReg32( dev, DMCOMMAND, 0x0023100c ); // Copy data to x12 + + // This is not even needed on the v20x/v30x chips. But it won't harm us to set the register for simplicity. + // v003 requires bufload every word. + // x035 requires bufload every word in spite of what the datasheet says. + // CR_PAGE_PG = FTPG = 0x00010000 | CR_BUF_LOAD = 0x00040000 + MCFWriteReg32( dev, BDMDATA0, 0x00010000|0x00040000 ); + + MCFWriteReg32( dev, DMCOMMAND, 0x0023100d ); // Copy data to x13 + return 0; +} + +static void ResetInternalProgrammingState( struct SWIOState * iss ) +{ + iss->statetag = 0; + iss->lastwriteflags = 0; + iss->currentstateval = 0; + iss->flash_unlocked = 0; + iss->autoincrement = 0; + iss->clock_set = 0; +} + +static int ReadByte( struct SWIOState * iss, uint32_t address_to_read, uint8_t * data ) +{ + struct SWIOState * dev = iss; + int ret = 0; + iss->statetag = STTAG( "XXXX" ); + + MCFWriteReg32( dev, DMABSTRACTAUTO, 0x00000000 ); // Disable Autoexec. + + // Different address, so we don't need to re-write all the program regs. + // lb x8,0(x9) // Read from the address. + MCFWriteReg32( dev, DMPROGBUF0, 0x00048403 ); // lb x8, 0(x9) + MCFWriteReg32( dev, DMPROGBUF1, 0x00100073 ); // c.ebreak + + MCFWriteReg32( dev, BDMDATA0, address_to_read ); + MCFWriteReg32( dev, DMCOMMAND, 0x00231009 ); // Copy data to x9 + MCFWriteReg32( dev, DMCOMMAND, 0x00241000 ); // Only execute. + MCFWriteReg32( dev, DMCOMMAND, 0x00221008 ); // Read x8 into DATA0. + + ret |= WaitForDoneOp( dev ); + iss->currentstateval = -1; + + uint32_t rr; + ret |= MCFReadReg32( dev, BDMDATA0, &rr ); + *data = rr; + return ret; +} + +static int ReadWord( struct SWIOState * iss, uint32_t address_to_read, uint32_t * data ) +{ + struct SWIOState * dev = iss; + int autoincrement = 1; + + if( address_to_read == 0x40022010 || address_to_read == 0x4002200C ) // Don't autoincrement when checking flash flag. + autoincrement = 0; + + if( iss->statetag != STTAG( "RDSQ" ) || address_to_read != iss->currentstateval || autoincrement != iss->autoincrement ) + { + if( iss->statetag != STTAG( "RDSQ" ) || autoincrement != iss->autoincrement ) + { + if( iss->statetag != STTAG( "WRSQ" ) ) + { + int r = StaticUpdatePROGBUFRegs( dev ); + if( r ) return r; + } + + MCFWriteReg32( dev, DMABSTRACTAUTO, 0 ); // Disable Autoexec. + + // c.lw x8,0(x11) // Pull the address from DATA1 + // c.lw x9,0(x8) // Read the data at that location. + MCFWriteReg32( dev, DMPROGBUF0, 0x40044180 ); + if( autoincrement ) + { + // c.addi x8, 4 + // c.sw x9, 0(x10) // Write back to DATA0 + + MCFWriteReg32( dev, DMPROGBUF1, 0xc1040411 ); + } + else + { + // c.nop + // c.sw x9, 0(x10) // Write back to DATA0 + + MCFWriteReg32( dev, DMPROGBUF1, 0xc1040001 ); + } + // c.sw x8, 0(x11) // Write addy to DATA1 + // c.ebreak + MCFWriteReg32( dev, DMPROGBUF2, 0x9002c180 ); + if( !iss->no_autoexec ) + MCFWriteReg32( dev, DMABSTRACTAUTO, 1 ); // Enable Autoexec (not autoincrement) + iss->autoincrement = autoincrement; + } + + MCFWriteReg32( dev, BDMDATA1, address_to_read ); + if( !iss->no_autoexec ) + MCFWriteReg32( dev, DMCOMMAND, 0x00240000 ); // Execute. + + iss->statetag = STTAG( "RDSQ" ); + iss->currentstateval = address_to_read; + } + + if( iss->autoincrement ) + iss->currentstateval += 4; + + if( iss->no_autoexec ) { + MCFWriteReg32( dev, DMCOMMAND, 0x00240000 ); + } + + // Only an issue if we are curising along very fast. + int r = WaitForDoneOp( dev ); + if( r ) return r; + + r = MCFReadReg32( dev, BDMDATA0, data ); + return r; +} + +static int WriteByte( struct SWIOState * iss, uint32_t address_to_write, uint8_t data ) +{ + struct SWIOState * dev = iss; + int ret = 0; + iss->statetag = STTAG( "XXXX" ); + + MCFWriteReg32( dev, DMABSTRACTAUTO, 0x00000000 ); // Disable Autoexec. + + // Different address, so we don't need to re-write all the program regs. + // sh x8,0(x9) // Write to the address. + MCFWriteReg32( dev, DMPROGBUF0, 0x00848023 ); // sb x8, 0(x9) + MCFWriteReg32( dev, DMPROGBUF1, 0x00100073 ); // c.ebreak + + MCFWriteReg32( dev, BDMDATA0, address_to_write ); + MCFWriteReg32( dev, DMCOMMAND, 0x00231009 ); // Copy data to x9 + MCFWriteReg32( dev, BDMDATA0, data ); + MCFWriteReg32( dev, DMCOMMAND, 0x00271008 ); // Copy data to x8, and execute program. + + ret |= WaitForDoneOp( dev ); + iss->currentstateval = -1; + return ret; +} + +static int WriteWord( struct SWIOState * iss, uint32_t address_to_write, uint32_t data ) +{ + struct SWIOState * dev = iss; + + int ret = 0; + + int is_flash = 0; + if( ( address_to_write & 0xff000000 ) == 0x08000000 || ( address_to_write & 0x1FFF0000 ) == 0x1FFF0000 ) + { + // Is flash. + is_flash = 1; + } + + if( iss->statetag != STTAG( "WRSQ" ) || is_flash != iss->lastwriteflags ) + { + int did_disable_req = 0; + + if( iss->statetag != STTAG( "WRSQ" ) ) + { + MCFWriteReg32( dev, DMABSTRACTAUTO, 0x00000000 ); // Disable Autoexec. + did_disable_req = 1; + + if( iss->statetag != STTAG( "RDSQ" ) ) + { + int r = StaticUpdatePROGBUFRegs( dev ); + if( r ) return r; + } + + // Different address, so we don't need to re-write all the program regs. + // c.lw x8,0(x10) // Get the value to write. + // c.lw x9,0(x11) // Get the address to write to. + MCFWriteReg32( dev, DMPROGBUF0, 0x41844100 ); + // c.sw x8,0(x9) // Write to the address. + // c.addi x9, 4 + MCFWriteReg32( dev, DMPROGBUF1, 0x0491c080 ); + } + + if( is_flash && iss->target_chip_type == CHIP_CH32V10x) + { + // Special 16 bytes buffer write sequence for CH32V103 + MCFWriteReg32( dev, DMPROGBUF2, 0x0001c184 ); // c.sw x9,0(x11); c.nop; + MCFWriteReg32( dev, DMPROGBUF3, 0x9002e391 ); // c.bnez x15, 4; c.ebreak; + MCFWriteReg32( dev, DMPROGBUF4, 0x4200c254 ); // c.sw x13,4(x12); c.lw x8,0(x12); + MCFWriteReg32( dev, DMPROGBUF5, 0xfc758805 ); // c.andi x8, 1; c.bnez x8, -4; + MCFWriteReg32( dev, DMPROGBUF6, 0x90024781 ); // c.li x15, 0; c.ebreak; + } + else if( is_flash ) + { + // A little weird - we need to wait until the buf load is done here to continue. + // x12 = 0x4002200C (FLASH_STATR) + // + // c254 c.sw x13,4(x12) // Acknowledge the page write. (BUT ONLY ON x035 / v003) + // otherwise c.nop + // 4200 c.lw x8,0(x12) // Start checking to see when buf load is done. + // 8809 c.andi x8, 2 // Only look at WR_BSY (seems to be rather undocumented) + // 8805 c.andi x8, 1 // Only look at BSY if we're not on a v30x / v20x + // fc75 c.bnez x8, -4 + // c.ebreak + MCFWriteReg32( dev, DMPROGBUF2, 0x0001c184 ); + MCFWriteReg32( dev, DMPROGBUF3, + (iss->target_chip_type == CHIP_CH32V003 || iss->target_chip_type == CHIP_CH32V00x + || iss->target_chip_type == CHIP_CH32X03x || iss->target_chip_type == CHIP_CH32L10x + || iss->target_chip_type == CHIP_CH641 || iss->target_chip_type == CHIP_CH643) ? + 0x4200c254 : 0x42000001 ); + + MCFWriteReg32( dev, DMPROGBUF4, + (iss->target_chip_type == CHIP_CH32V20x || iss->target_chip_type == CHIP_CH32V30x ) ? + 0xfc758809 : 0xfc758805 ); + + MCFWriteReg32( dev, DMPROGBUF5, 0x90029002 ); + } + else + { + MCFWriteReg32( dev, DMPROGBUF2, 0x9002c184 ); // c.sw x9,0(x11); c.ebreak; + } + + MCFWriteReg32( dev, BDMDATA1, address_to_write ); + MCFWriteReg32( dev, BDMDATA0, data ); + + if( iss->no_autoexec ) + { + MCFWriteReg32( dev, DMCOMMAND, 0x00240000 ); // Execute. + } + else if( did_disable_req ) + { + MCFWriteReg32( dev, DMCOMMAND, 0x00240000 ); // Execute. + MCFWriteReg32( dev, DMABSTRACTAUTO, 1 ); // Enable Autoexec. + } + + iss->lastwriteflags = is_flash; + + iss->statetag = STTAG( "WRSQ" ); + iss->currentstateval = address_to_write; + } + else + { + if( address_to_write != iss->currentstateval ) + { + MCFWriteReg32( dev, BDMDATA1, address_to_write ); + } + MCFWriteReg32( dev, BDMDATA0, data ); + if( iss->no_autoexec ) + { + MCFWriteReg32( dev, DMCOMMAND, 0x00240000 ); // Execute. + } + } + if( is_flash ) + ret |= WaitForDoneOp( dev ); + + iss->currentstateval += 4; + + return ret; +} + +static int UnlockFlash( struct SWIOState * iss ) +{ + struct SWIOState * dev = iss; + + if( DetermineChipTypeAndSectorInfo( iss, NULL ) ) return -9; + + uint32_t rw; + ReadWord( dev, 0x40022010, &rw ); // FLASH->CTLR = 0x40022010 + if( rw & 0x8080 ) + { + + WriteWord( dev, 0x40022004, 0x45670123 ); // FLASH->KEYR = 0x40022004 + WriteWord( dev, 0x40022004, 0xCDEF89AB ); + WriteWord( dev, 0x40022008, 0x45670123 ); // OBKEYR = 0x40022008 + WriteWord( dev, 0x40022008, 0xCDEF89AB ); + WriteWord( dev, 0x40022024, 0x45670123 ); // MODEKEYR = 0x40022024 + WriteWord( dev, 0x40022024, 0xCDEF89AB ); + + ReadWord( dev, 0x40022010, &rw ); // FLASH->CTLR = 0x40022010 + if( rw & 0x8080 ) + { + return -9; + } + } + iss->statetag = STTAG( "XXXX" ); + iss->flash_unlocked = 1; + return 0; +} + +static int EraseFlash( struct SWIOState * iss, uint32_t address, uint32_t length, int type ) +{ + struct SWIOState * dev = iss; + + uint32_t rw; + + if( !iss->flash_unlocked ) + { + if( ( rw = UnlockFlash( iss ) ) ) + return rw; + } + + if( type == 1 ) + { + // Whole-chip flash + iss->statetag = STTAG( "XXXX" ); + BB_PRINTF_DEBUG( "Whole-chip erase\n" ); + WriteWord( dev, 0x40022010, 0 ); // FLASH->CTLR = 0x40022010 + WriteWord( dev, 0x40022010, FLASH_CTLR_MER ); + WriteWord( dev, 0x40022010, CR_STRT_Set|FLASH_CTLR_MER ); + if( WaitForFlash( dev ) ) return -13; + WriteWord( dev, 0x40022010, 0 ); // FLASH->CTLR = 0x40022010 + } + else + { + // 16.4.7, Step 3: Check the BSY bit of the FLASH_STATR register to confirm that there are no other programming operations in progress. + // skip (we make sure at the end) + + int chunk_to_erase = address; + + while( chunk_to_erase < address + length ) + { + if( WaitForFlash( dev ) ) return -14; + + // Step 4: set PAGE_ER of FLASH_CTLR(0x40022010) + WriteWord( dev, 0x40022010, CR_PAGE_ER ); // Actually FTER // FLASH->CTLR = 0x40022010 + + // Step 5: Write the first address of the fast erase page to the FLASH_ADDR register. + WriteWord( dev, 0x40022014, chunk_to_erase ); // FLASH->ADDR = 0x40022014 + + // Step 6: Set the STAT bit of FLASH_CTLR register to '1' to initiate a fast page erase (64 bytes) action. + WriteWord( dev, 0x40022010, CR_STRT_Set|CR_PAGE_ER ); // FLASH->CTLR = 0x40022010 + if( WaitForFlash( dev ) ) return -15; + + WriteWord( dev, 0x40022010, 0 ); // FLASH->CTLR = 0x40022010 (Disable any pending ops) + chunk_to_erase+=64; + } + } + return 0; +} + +static int WriteBlock( struct SWIOState * iss, uint32_t address_to_write, uint8_t * blob, uint8_t len, uint8_t erase ) +{ + struct SWIOState * dev = iss; + + if( DetermineChipTypeAndSectorInfo( iss, NULL ) ) return -9; + + const int blob_size = len; + uint32_t wp = address_to_write; + uint32_t ew = wp + blob_size; + int group = -1; + int is_flash = 0; + int rw = 0; + + if( (address_to_write & 0xff000000) == 0x08000000 || (address_to_write & 0xff000000) == 0x00000000 || (address_to_write & 0x1FFF0000) == 0x1FFF0000 ) + { + // Need to unlock flash. + // Flash reg base = 0x40022000, + // FLASH_MODEKEYR => 0x40022024 + // FLASH_KEYR => 0x40022004 + + if( !iss->flash_unlocked ) + { + if( ( rw = UnlockFlash( dev ) ) ) + return rw; + } + + is_flash = 1; + + if( erase ) + { + // Only erase on first block in sector. + int block_in_sector = (wp & ( iss->sectorsize - 1 )) / blob_size; + int is_first_block = block_in_sector == 0; + + if( is_first_block ) + { + rw = EraseFlash( dev, address_to_write, blob_size, 0 ); + if( rw ) return rw; + // 16.4.6 Main memory fast programming, Step 5 + //if( WaitForFlash( dev ) ) return -11; + //WriteWord( dev, 0x40022010, FLASH_CTLR_BUF_RST ); + //if( WaitForFlash( dev ) ) return -11; + } + } + } + + /* General Note: + Most flash operations take about 3ms to complete :( + */ + + while( wp < ew ) + { + if( is_flash ) + { + group = (wp & 0xffffffc0); + + int block_in_sector = (group & ( iss->sectorsize - 1 )) / blob_size; + int is_first_block = block_in_sector == 0; + int is_last_block = block_in_sector == (iss->sectorsize / blob_size - 1 ); + + if( is_first_block ) + { + if( dev->target_chip_type == CHIP_CH32V20x || dev->target_chip_type == CHIP_CH32V30x ) + { + // No bufrst on v20x, v30x + WaitForFlash( dev ); + WriteWord( dev, 0x40022010, CR_PAGE_PG ); // THIS IS REQUIRED, (intptr_t)&FLASH->CTLR = 0x40022010 + //FTPG == CR_PAGE_PG == ((uint32_t)0x00010000) + } + else + { + // V003, x035, maybe more. + WriteWord( dev, 0x40022010, CR_PAGE_PG ); // THIS IS REQUIRED, (intptr_t)&FLASH->CTLR = 0x40022010 + WriteWord( dev, 0x40022010, CR_BUF_RST | CR_PAGE_PG ); // (intptr_t)&FLASH->CTLR = 0x40022010 + } + WaitForFlash( dev ); + } + + int j; + for( j = 0; j < blob_size/4; j++ ) + { + int index = (wp-address_to_write); + uint32_t data = 0xffffffff; + if( index + 3 < blob_size ) + { + memcpy( &data, &blob[index], 4 ); + } + else if( (int32_t)(blob_size - index) > 0 ) + { + memcpy( &data, &blob[index], blob_size - index ); + } + if( iss->target_chip_type == CHIP_CH32V10x && !((j+1)&3) ) + { + // Signal to WriteWord that we need to do a buffer load + MCFWriteReg32( dev, BDMDATA0, 1 ); + MCFWriteReg32( dev, DMCOMMAND, 0x0023100f ); + } + WriteWord( dev, wp, data ); + wp += 4; + } + + + if( ( iss->target_chip_type == CHIP_CH32V20x || iss->target_chip_type == CHIP_CH32V30x ) ) + { + if( is_last_block ) + { + WriteWord( dev, 0x40022010, CR_PAGE_PG | (1<<21) ); // Page Start + if( WaitForFlash( dev ) ) return -13; + } + } + else + { + // Datasheet says the x03x needs to have this called every group-of-16, but that's not true, it should be every 16-words. + WriteWord( dev, 0x40022014, group ); //0x40022014 -> FLASH->ADDR + WriteWord( dev, 0x40022010, CR_PAGE_PG|CR_STRT_Set ); // 0x40022010 -> FLASH->CTLR + if( WaitForFlash( dev ) ) return -16; + } + } + else + { + int index = (wp-address_to_write); + uint32_t data = 0xffffffff; + if( index + 3 < blob_size ) + { + memcpy( &data, &blob[index], 4 ); + } + else if( (int32_t)(blob_size - index) > 0 ) + { + memcpy( &data, &blob[index], blob_size - index ); + } + WriteWord( dev, wp, data ); + wp += 4; + } + } + + return 0; +} + +// Polls up to 7 bytes of printf, and can leave a 7-bit flag for the CH32V003. +static int PollTerminal( struct SWIOState * iss, uint8_t * buffer, int maxlen, uint32_t leavevalA, uint32_t leavevalB ) +{ + struct SWIOState * dev = iss; + + int r; + uint32_t rr; + if( iss->statetag != STTAG( "TERM" ) ) + { + MCFWriteReg32( dev, DMABSTRACTAUTO, 0x00000000 ); // Disable Autoexec. + iss->statetag = STTAG( "TERM" ); + } + r = MCFReadReg32( dev, BDMDATA0, &rr ); + if( r < 0 ) return r; + + if( maxlen < 8 ) return -9; + + // BDMDATA1: + // bit 7 = host-acknowledge. + if( rr & 0x80 ) + { + int ret = 0; + int num_printf_chars = (rr & 0xf)-4; + + if( num_printf_chars > 0 && num_printf_chars <= 7) + { + if( num_printf_chars > 3 ) + { + uint32_t r2; + r = MCFReadReg32( dev, BDMDATA1, &r2 ); + memcpy( buffer+3, &r2, num_printf_chars - 3 ); + } + int firstrem = num_printf_chars; + if( firstrem > 3 ) firstrem = 3; + memcpy( buffer, ((uint8_t*)&rr)+1, firstrem ); + buffer[num_printf_chars] = 0; + ret = num_printf_chars; + } + if( leavevalA ) + { + MCFWriteReg32( dev, BDMDATA1, leavevalB ); + } + MCFWriteReg32( dev, BDMDATA0, leavevalA ); // Write that we acknowledge the data. + return ret; + } + else + { + return 0; + } +} + +#endif // _CH32V003_SWIO_H diff --git a/fw/programmer/src/bitbang_rvswdio_pico.h b/fw/programmer/src/bitbang_rvswdio_pico.h new file mode 100644 index 0000000..e8ffdc8 --- /dev/null +++ b/fw/programmer/src/bitbang_rvswdio_pico.h @@ -0,0 +1,219 @@ +#include +#include + +#include "bitbang_rvswdio.h" +#include "hardware/timer.h" +#include "lwipopts.h" +#include "pico.h" + + +#define PIN_SWDIO 10 +#define PIN_SWCLK 11 + + +// open drain emulation, the pin is set with output '0' and is switched between input or output +// depending on the wanted value, in the high state the line is pulled high by the pull-up and +// in the low state the line in forced low +#define OD_PULL(pin, value) gpio_set_dir((pin), (value) ? GPIO_IN : GPIO_OUT) + +// wait time between line transitions +#define SWD_DELAY() busy_wait_us(1); + +// microseconds between each register read/write +#define STOP_WAIT 8 + +// Single wire debug (SWDIO and SWCLK) +static inline void ConfigureIOForRVSWD(void) +{ + // SWDIO, open drain (emulated) with pull-up + gpio_init(PIN_SWDIO); + gpio_set_pulls(PIN_SWDIO, true, false); + gpio_put(PIN_SWDIO, 0); + gpio_set_dir(PIN_SWDIO, GPIO_IN); + + gpio_init(PIN_SWCLK); + gpio_set_pulls(PIN_SWCLK, false, false); + gpio_put(PIN_SWCLK, 0); + gpio_set_dir(PIN_SWCLK, GPIO_OUT); +} + + +// Single wire input-output SDI (just SWDIO) +static inline void ConfigureIOForRVSWIO(void) +{ + BB_PRINTF_DEBUG( "TODO: add support for SWIO\n" ); +} + + +static inline void rvswd_start(void) +{ + // Start with both lines high + gpio_put(PIN_SWCLK, 1); + OD_PULL(PIN_SWDIO, 1); + //SWD_DELAY(); + + // Pull data low + OD_PULL(PIN_SWDIO, 0); + SWD_DELAY(); +} + + +static inline void rvswd_stop(void) +{ + gpio_put(PIN_SWCLK, 0); + SWD_DELAY(); + + OD_PULL(PIN_SWDIO, 0); + SWD_DELAY(); + + gpio_put(PIN_SWCLK, 1); + SWD_DELAY(); + + OD_PULL(PIN_SWDIO, 1); +} + + +void rvswd_write_bit(bool value) +{ + gpio_put(PIN_SWCLK, 0); + OD_PULL(PIN_SWDIO, value); + SWD_DELAY(); + gpio_put(PIN_SWCLK, 1); // Data is sampled on rising edge of clock + SWD_DELAY(); +} + + +bool rvswd_read_bit(void) +{ + OD_PULL(PIN_SWDIO, 0); + gpio_put(PIN_SWCLK, 0); + OD_PULL(PIN_SWDIO, 1); + SWD_DELAY(); + bool bit = gpio_get(PIN_SWDIO); + + gpio_put(PIN_SWCLK, 1); // Data is output on rising edge of clock + SWD_DELAY(); + return bit; +} + + +static void MCFWriteReg32( struct SWIOState * state, uint8_t command, uint32_t value ) +{ + // only supported mode is SWD + if (state->opmode != 2) { + BB_PRINTF_DEBUG( "TODO: add support for SWIO\n" ); + return; + } + + noInterrupts(); + rvswd_start(); + + // ADDR HOST + bool parity = false; // This time it's odd parity? + for (uint8_t position = 0; position < 7; position++) { + bool bit = (command >> (6 - position)) & 1; + rvswd_write_bit(bit); + if (bit) parity = !parity; + } + + // Operation: write + rvswd_write_bit(1); + parity = !parity; + + // Parity bit (even) + rvswd_write_bit(parity); + + rvswd_read_bit(); // ??? + rvswd_read_bit(); // Seems only need to be set for first transaction (We are ignoring that though) + rvswd_read_bit(); // ??? + rvswd_write_bit(0); // 0 for register, 1 for value. + rvswd_write_bit(0); // ??? Seems to have something to do with halting. + + // Data + parity = false; // This time it's even parity? + for (uint8_t position = 0; position < 32; position++) { + bool bit = (value >> (31 - position)) & 1; + rvswd_write_bit(bit); + if (bit) parity = !parity; + } + + // Parity bit + rvswd_write_bit(parity); + + rvswd_read_bit(); // ??? + rvswd_read_bit(); // ??? + rvswd_read_bit(); // ??? + rvswd_write_bit(1); // 0 for register, 1 for value + rvswd_write_bit(0); // ??? Seems to have something to do with halting? + + rvswd_stop(); + interrupts(); + sleep_us(STOP_WAIT); +} + + +static int MCFReadReg32( struct SWIOState * state, uint8_t command, uint32_t * value ) +{ + // only supported mode is SWD + if (state->opmode != 2) { + BB_PRINTF_DEBUG( "TODO: add support for SWIO\n" ); + return -1; + } + + bool parity; + + noInterrupts(); + rvswd_start(); + + // ADDR HOST + parity = false; + for (uint8_t position = 0; position < 7; position++) { + bool bit = (command >> (6 - position)) & 1; + rvswd_write_bit(bit); + if (bit) parity = !parity; + } + + // Operation: read + rvswd_write_bit(0); + + // Parity bit (even) + rvswd_write_bit(parity); + + rvswd_read_bit(); // ?? + rvswd_read_bit(); // ?? + rvswd_read_bit(); // ?? + + rvswd_write_bit(0); // 0 for register, 1 for value + rvswd_write_bit(0); // ??? Seems to have something to do with halting? + + *value = 0; + + // Data + parity = false; + uint32_t rval = 0; + for (uint8_t position = 0; position < 32; position++) { + bool bit = rvswd_read_bit(); + rval <<= 1; + if (bit) { + rval |= 1; + parity ^= 1; + } + } + *value = rval; + + // Parity bit + bool parity_read = rvswd_read_bit(); + + rvswd_read_bit(); // ?? + rvswd_read_bit(); // ?? + rvswd_read_bit(); // ?? + + rvswd_write_bit(1); // 1 for data + rvswd_write_bit(0); // ??? Seems to have something to do with halting? + + rvswd_stop(); + interrupts(); + + sleep_us(STOP_WAIT); + return (parity == parity_read) ? 0 : -1; +} diff --git a/fw/programmer/src/main.cpp b/fw/programmer/src/main.cpp new file mode 100644 index 0000000..df6993a --- /dev/null +++ b/fw/programmer/src/main.cpp @@ -0,0 +1,90 @@ + #include "pico/time.h" + #include + #include + + //#define BB_PRINTF_DEBUG(...) Serial.printf(__VA_ARGS__) + #define BB_PRINTF_DEBUG(...) + #include "bitbang_rvswdio.h" + #include "bitbang_rvswdio_pico.h" + + #define PROTOCOL_START '!' + #define PROTOCOL_ACK '+' + #define PROTOCOL_TEST '?' + #define PROTOCOL_POWER_ON 'p' + #define PROTOCOL_POWER_OFF 'P' + #define PROTOCOL_WRITE_REG 'w' + #define PROTOCOL_READ_REG 'r' + + bool last_dtr = false; + SWIOState state = {.opmode = 2}; + + void setup() { + Serial.begin(115200); + Serial.setTimeout(100); + ConfigureIOForRVSWD(); + + // We do NOT block waiting for Serial here, + // we handle the connection dynamically in the loop. + } + + + void setup1() + { + pinMode(LED_BUILTIN, OUTPUT); + } + + + void loop() { + // Monitor DTR line to simulate Arduino Reset behavior + bool current_dtr = Serial.dtr(); + if (current_dtr && !last_dtr) { + // minichlink just opened the port + ConfigureIOForRVSWD(); + while (Serial.available() > 0) { Serial.read(); } + delay(100); + Serial.write(PROTOCOL_START); // Announce readiness + } + last_dtr = current_dtr; + + if (Serial.available() > 0) { + char cmd = Serial.read(); + uint8_t reg; + uint32_t val; + + switch (cmd) { + case PROTOCOL_TEST: + Serial.write(PROTOCOL_ACK); + break; + case PROTOCOL_POWER_ON: + // Not needed for rvswd + sleep_us(10); + Serial.write(PROTOCOL_ACK); + break; + case PROTOCOL_POWER_OFF: + // Not needed for rvswd + sleep_us(10); + Serial.write(PROTOCOL_ACK); + break; + case PROTOCOL_WRITE_REG: + if (Serial.readBytes((char*)®, sizeof(uint8_t)) != 1) break; + if (Serial.readBytes((char*)&val, sizeof(uint32_t)) != 4) break; + MCFWriteReg32(&state, reg, val); + Serial.write(PROTOCOL_ACK); + break; + case PROTOCOL_READ_REG: + if (Serial.readBytes((char*)®, sizeof(uint8_t)) != 1) break; + MCFReadReg32(&state, reg, &val); + Serial.write((char*)&val, sizeof(uint32_t)); + break; + } + } + } + + + void loop1() + { + digitalWrite(LED_BUILTIN, 1); + delay(200); + digitalWrite(LED_BUILTIN, 0); + delay(200); + } diff --git a/fw/programmer/test/rvswd.c b/fw/programmer/test/rvswd.c new file mode 100644 index 0000000..a80c948 --- /dev/null +++ b/fw/programmer/test/rvswd.c @@ -0,0 +1,199 @@ +#include "rvswd.h" +#include +#include +#include +#include + + +// adapted from https://github.com/Nicolai-Electronics/esp32-component-rvswd + +rvswd_result_t rvswd_init(rvswd_handle_t* handle) { + //gpio_config_t swio_cfg = { + // .pin_bit_mask = BIT64(handle->swdio), + // .mode = GPIO_MODE_INPUT_OUTPUT_OD, + // .pull_up_en = true, + // .pull_down_en = false, + // .intr_type = GPIO_INTR_DISABLE, + //}; + gpio_init(handle->swdio); + gpio_set_pulls(handle->swdio, true, false); + gpio_put(handle->swdio, 0); + gpio_set_dir(handle->swdio, GPIO_IN); + + //gpio_config_t swck_cfg = { + // .pin_bit_mask = BIT64(handle->swclk), + // .mode = GPIO_MODE_OUTPUT, + // .pull_up_en = false, + // .pull_down_en = false, + // .intr_type = GPIO_INTR_DISABLE, + //}; + gpio_init(handle->swclk); + gpio_set_pulls(handle->swclk, false, false); + gpio_put(handle->swclk, 0); + gpio_set_dir(handle->swclk, GPIO_OUT); + + return RVSWD_OK; +} + +rvswd_result_t rvswd_start(rvswd_handle_t* handle) { + // Start with both lines high + // open drain emulation, input for high (pulled up) and out for low (forced) + gpio_set_dir(handle->swdio, GPIO_IN); // high + gpio_put(handle->swclk, 1); + sleep_us(2); + + // Pull data low + gpio_set_dir(handle->swdio, GPIO_OUT); // low + gpio_put(handle->swclk, 1); + sleep_us(1); + + // Pull clock low + gpio_set_dir(handle->swdio, GPIO_OUT); // low + gpio_put(handle->swclk, 0); + sleep_us(1); + + return RVSWD_OK; +} + +rvswd_result_t rvswd_stop(rvswd_handle_t* handle) { + // Pull data low + gpio_set_dir(handle->swdio, GPIO_OUT); + sleep_us(1); + gpio_put(handle->swclk, 1); + sleep_us(2); + + // Let data float high + gpio_set_dir(handle->swdio, GPIO_IN); + sleep_us(1); + return RVSWD_OK; +} + +rvswd_result_t rvswd_reset(rvswd_handle_t* handle) { + // set data floating high + gpio_set_dir(handle->swdio, GPIO_IN); + sleep_us(1); + // let clock go brrr + for (uint8_t i = 0; i < 100; i++) { + gpio_put(handle->swclk, 0); + sleep_us(1); + gpio_put(handle->swclk, 1); + sleep_us(1); + } + return rvswd_stop(handle); +} + +#define OD_PULL(pin, value) gpio_set_dir((pin), (value) ? GPIO_IN : GPIO_OUT) + +void rvswd_write_bit(rvswd_handle_t* handle, bool value) { + OD_PULL(handle->swdio, value); + gpio_put(handle->swclk, 0); + // FIXME: does this need a delay? + gpio_put(handle->swclk, 1); // Data is sampled on rising edge of clock +} + +bool rvswd_read_bit(rvswd_handle_t* handle) { + // let data float high + gpio_set_dir(handle->swdio, GPIO_IN); + // pulse clock + gpio_put(handle->swclk, 0); + gpio_put(handle->swclk, 1); // Data is output on rising edge of clock + // FIXME: does this need a delay? + return gpio_get(handle->swdio); +} + +rvswd_result_t rvswd_write(rvswd_handle_t* handle, uint8_t reg, uint32_t value) { + rvswd_start(handle); + + // ADDR HOST + bool parity = false; // This time it's odd parity? + for (uint8_t position = 0; position < 7; position++) { + bool bit = (reg >> (6 - position)) & 1; + rvswd_write_bit(handle, bit); + if (bit) parity = !parity; + } + + // Operation: write + rvswd_write_bit(handle, true); + parity = !parity; + + // Parity bit (even) + rvswd_write_bit(handle, parity); + + rvswd_write_bit(handle, 1); + rvswd_write_bit(handle, 0); + rvswd_write_bit(handle, 1); + rvswd_write_bit(handle, 0); + rvswd_write_bit(handle, 1); + + // Data + parity = false; // This time it's even parity? + for (uint8_t position = 0; position < 32; position++) { + bool bit = (value >> (31 - position)) & 1; + rvswd_write_bit(handle, bit); + if (bit) parity = !parity; + } + + // Parity bit + rvswd_write_bit(handle, parity); + + rvswd_write_bit(handle, 1); + rvswd_write_bit(handle, 0); + rvswd_write_bit(handle, 1); + rvswd_write_bit(handle, 1); + rvswd_write_bit(handle, 1); + + rvswd_stop(handle); + + return RVSWD_OK; +} + +rvswd_result_t rvswd_read(rvswd_handle_t* handle, uint8_t reg, uint32_t* value) { + bool parity; + + rvswd_start(handle); + + // ADDR HOST + parity = false; + for (uint8_t position = 0; position < 7; position++) { + bool bit = (reg >> (6 - position)) & 1; + rvswd_write_bit(handle, bit); + if (bit) parity = !parity; + } + + // Operation: read + rvswd_write_bit(handle, false); + + // Parity bit (even) + rvswd_write_bit(handle, parity); + + rvswd_write_bit(handle, 1); + rvswd_write_bit(handle, 0); + rvswd_write_bit(handle, 1); + rvswd_write_bit(handle, 0); + rvswd_write_bit(handle, 1); + + *value = 0; + + // Data + parity = false; + for (uint8_t position = 0; position < 32; position++) { + bool bit = rvswd_read_bit(handle); + if (bit) { + *value |= 1 << (31 - position); + } + if (bit) parity = !parity; + } + + // Parity bit + bool parity_read = rvswd_read_bit(handle); + + rvswd_write_bit(handle, 1); + rvswd_write_bit(handle, 0); + rvswd_write_bit(handle, 1); + rvswd_write_bit(handle, 1); + rvswd_write_bit(handle, 1); + + rvswd_stop(handle); + + return (parity == parity_read) ? RVSWD_OK : RVSWD_FAIL; +} diff --git a/fw/programmer/test/rvswd.h b/fw/programmer/test/rvswd.h new file mode 100644 index 0000000..dd43d31 --- /dev/null +++ b/fw/programmer/test/rvswd.h @@ -0,0 +1,20 @@ +#pragma once + +#include + +typedef struct rvswd_handle { + int swdio; + int swclk; +} rvswd_handle_t; + +typedef enum rvswd_result { + RVSWD_OK = 0, + RVSWD_FAIL = 1, + RVSWD_INVALID_ARGS = 2, + RVSWD_PARITY_ERROR = 3, +} rvswd_result_t; + +rvswd_result_t rvswd_init(rvswd_handle_t* handle); +rvswd_result_t rvswd_reset(rvswd_handle_t* handle); +rvswd_result_t rvswd_write(rvswd_handle_t* handle, uint8_t reg, uint32_t value); +rvswd_result_t rvswd_read(rvswd_handle_t* handle, uint8_t reg, uint32_t* value);