diff --git a/examples/blink/.vscode/launch.json b/examples/blink/.vscode/launch.json new file mode 100644 index 0000000000000000000000000000000000000000..daf0b515adfa0bbf74d743499abafb8cb48dab64 --- /dev/null +++ b/examples/blink/.vscode/launch.json @@ -0,0 +1,25 @@ +{ + "configurations": [ + { + "name": "GDB debug - custom", + "type": "cppdbg", + "request": "launch", + "program": "blink.elf", + "args": [], + "stopAtEntry": true, + "cwd": "${workspaceFolder}", + "environment": [], + "externalConsole": false, + "MIMode": "gdb", + "setupCommands": [ + { + "description": "Enable pretty-printing for gdb", + "text": "-enable-pretty-printing", + "ignoreFailures": true + } + ], + "miDebuggerPath": "/usr/bin/gdb-multiarch", + "miDebuggerServerAddress": "127.0.0.1:2000" + } + ] +} diff --git a/examples/blink/.vscode/settings.json b/examples/blink/.vscode/settings.json new file mode 100644 index 0000000000000000000000000000000000000000..79b30b8432213edaa2ffffd7b366b7cb61bf8240 --- /dev/null +++ b/examples/blink/.vscode/settings.json @@ -0,0 +1,12 @@ +{ + "cmake.configureOnOpen": false, + "makefile.launchConfigurations": [ + { + "cwd": "/home/cnlohr/git/ch32v003fun/examples/blink", + "sbinaryPath": "/home/cnlohr/git/ch32v003fun/examples/blink/blink.elf", + "binaryArgs": [] + } + ], + "editor.insertSpaces": false, + "editor.tabSize": 4 +} diff --git a/examples/blink/blink.bin b/examples/blink/blink.bin index b0cf5a4fa20b888e4185c78fd1a5230786bc2059..cd9973989add95d2e43f0ad61d045a2bd32d14e3 100755 Binary files a/examples/blink/blink.bin and b/examples/blink/blink.bin differ diff --git a/examples/blink/blink.c b/examples/blink/blink.c index e65639989d63a04cef910d2ae4fd5b238e15407e..4646259b1352778b1a5985f335614ab3e91e477d 100644 --- a/examples/blink/blink.c +++ b/examples/blink/blink.c @@ -6,7 +6,7 @@ #define APB_CLOCK SYSTEM_CORE_CLOCK -uint32_t count; +volatile uint32_t count; int main() { @@ -34,8 +34,9 @@ int main() Delay_Ms( 250 ); GPIOD->BSHR = (1<<16) | (1<<(16+4)); // Turn off GPIODs GPIOC->BSHR = (1<<16); - Delay_Ms( 250 ); + Delay_Ms( count ); count++; + if( count > 250 ) count = 0; } } diff --git a/minichlink/Makefile b/minichlink/Makefile index 3a367d436565111d73007bd1a2d399ad87f4493a..0bd37d30107a55504282a51cafd0406d905875f9 100644 --- a/minichlink/Makefile +++ b/minichlink/Makefile @@ -4,6 +4,12 @@ all : $(TOOLS) CFLAGS:=-O0 -g3 -Wall +C_S:=minichlink.c pgm-wch-linke.c pgm-esp32s2-ch32xx.c nhc-link042.c minichgdb.c + +# General Note: To use with GDB, gdb-multiarch +# gdb-multilib {file} +# target remote :2345 + ifeq ($(OS),Windows_NT) LDFLAGS:=-lpthread -lusb-1.0 -lsetupapi else @@ -20,13 +26,14 @@ else endif endif +# will need mingw-w64-x86-64-dev gcc-mingw-w64-x86-64 +minichlink.exe : $(C_S) + x86_64-w64-mingw32-gcc -o $@ $^ -Os -s -lsetupapi ./libusb-1.0.dll - - -minichlink : minichlink.c pgm-wch-linke.c pgm-esp32s2-ch32xx.c nhc-link042.c +minichlink : $(C_S) gcc -o $@ $^ $(LDFLAGS) $(CFLAGS) $(INCS) -minichlink.so : minichlink.c pgm-wch-linke.c pgm-esp32s2-ch32xx.c nhc-link042.c +minichlink.so : $(C_S) gcc -o $@ $^ $(LDFLAGS) $(CFLAGS) $(INCS) -shared -fPIC install_udev_rules : diff --git a/minichlink/microgdbstub.h b/minichlink/microgdbstub.h new file mode 100644 index 0000000000000000000000000000000000000000..669c0806ff1633b8dbcf8f51f3569ebe92e77d18 --- /dev/null +++ b/minichlink/microgdbstub.h @@ -0,0 +1,624 @@ +/* + * Micro GDBStub Driver, for implementing a gdbserver. + * Copyright (C) Charles Lohr 2023 + * You may freely license this file under the MIT-x11, or the 2- or 3- or New BSD Licenses. + * You may also use this as though it is public domain. + * + * This project is based off of picorvd. https://github.com/aappleby/PicoRVD/ + * + * Simply: + * 1: define the RV_ Functions + * 2: Call the MicroGDB* functions needed. + * 3: Define MICROGDBSTUB_IMPLEMENTATION at least in one place this is included in your program. + * 4: If you want to let this manage the server as a network device, simply #define MICROGDBSTUB_SOCKETS + * + * To connect to your GDBStub running, you can: + * gdb-multiarch -ex 'target remote :2000' ./blink.elf + * + */ + +#ifndef _MICROGDBSTUB_H +#define _MICROGDBSTUB_H + +// You must write these for your processor. +void RVNetPoll(void * dev ); +int RVSendGDBHaltReason( void * dev ); +void RVNetConnect( void * dev ); +int RVSendGDBHaltReason( void * dev ); +void RVNetPoll(void * dev ); +int RVReadCPURegister( void * dev, int regno, uint32_t * regret ); +void RVDebugExec( void * dev, int halt_reset_or_resume ); +int RVReadMem( void * dev, uint32_t memaddy, uint8_t * payload, int len ); +int RVHandleBreakpoint( void * dev, int set, uint32_t address ); +int RVWriteRAM(void * dev, uint32_t memaddy, uint32_t length, uint8_t * payload ); +void RVHandleDisconnect( void * dev ); +void RVHandleGDBBreakRequest( void * dev ); + +#ifdef MICROGDBSTUB_SOCKETS +int MicroGDBPollServer( void * dev ); +int MicroGDBStubStartup( void * dev ); +void MicroGDBExitServer( void * dev ); +#endif + +// If you are not a network socket, you can pass in this data. +void MicroGDBStubSendReply( const void * data, int len, int docs ); +void MicroGDBStubHandleClientData( void * dev, const uint8_t * rxdata, int len ); + + +#ifdef MICROGDBSTUB_IMPLEMENTATION + +/////////////////////////////////////////////////////////////////////////////// +// Protocol Stuff + +#include <string.h> +#include <stdio.h> +#include <stdlib.h> + +uint16_t htons(uint16_t hostshort); +uint32_t htonl(uint32_t hostlong); + +char gdbbuffer[65536]; +uint8_t gdbchecksum = 0; +int gdbbufferplace = 0; +int gdbbufferstate = 0; + + +static inline char ToHEXNibble( int i ) +{ + i &= 0xf; + return ( i < 10 )?('0' + i):('a' - 10 + i); +} + +static int fromhex( char c ) +{ + if( c >= '0' && c <= '9' ) c = c - '0'; + else if( c >= 'A' && c <= 'F' ) c = c - 'A' + 10; + else if( c >= 'a' && c <= 'f' ) c = c - 'a' + 10; + else return -1; + return c; +} + +// if (numhex < 0) +static int ReadHex( char ** instr, int numhex, uint32_t * outwrite ) +{ + if( !instr ) return -1; + char * str = *instr; + // If negative - error. + // If positive - number of bytes read. + + *outwrite = 0; + + int scanhex = numhex; + if( scanhex < 0 ) + scanhex = strlen( str ); + + int i; + for( i = 0; i < scanhex; i++ ) + { + int v = fromhex( *(str++) ); + if( v < 0 ) + { + if( numhex < 0 ) + { + str--; + *instr = str; + return i; + } + else + { + *instr = str; + return - i - 1; + } + } + (*outwrite) = ((*outwrite) << 4) | v; + } + *instr = str; + return i; +} + +static int StringMatch( const char * haystack, const char * mat ) +{ + int i; + for( i = 0; mat[i] && haystack[i]; i++ ) + if( mat[i] != haystack[i] || haystack[i] == 0 ) break; + return mat[i] == 0; +} + +void SendReplyFull( const char * replyMessage ) +{ + MicroGDBStubSendReply( replyMessage, -1, '$' ); +} + +/////////////////////////////////////////////////////////////////////////////// +// General Protocol + +void HandleGDBPacket( void * dev, char * data, int len ) +{ + int i; + + //printf( ":::%s:::\n", data ); + // Got a packet? + if( data[0] != '$' ) return; + + data++; + + char cmd = *(data++); + switch( cmd ) + { + case 'q': + if( StringMatch( data, "Attached" ) ) + SendReplyFull( "1" ); //Attached to an existing process. + else if( StringMatch( data, "Supported" ) ) + SendReplyFull( "PacketSize=f000;qXfer:memory-map:read+" ); + else if( StringMatch( data, "C") ) // Get Current Thread ID. (Can't be -1 or 0. Those are special) + SendReplyFull( "QC1" ); + else if( StringMatch( data, "fThreadInfo" ) ) // Query all active thread IDs (Can't be 0 or 1) + SendReplyFull( "m1" ); + else if( StringMatch( data, "sThreadInfo" ) ) // Query all active thread IDs, continued + SendReplyFull( "l" ); + else if( StringMatch( data, "Xfer:memory-map" ) ) + SendReplyFull( MICROGDBSTUB_MEMORY_MAP ); + else + SendReplyFull( "" ); + break; + case 'c': + case 'C': + RVDebugExec( dev, (cmd == 'C')?4:2 ); + SendReplyFull( "OK" ); + break; + case 'D': + // Handle disconnect. + RVHandleDisconnect( dev ); + break; + case 'Z': + case 'z': + { + uint32_t type = 0; + uint32_t addr = 0; + uint32_t time = 0; + if( ReadHex( &data, -1, &type ) < 0 ) goto err; + if( *(data++) != ',' ) goto err; + if( ReadHex( &data, -1, &addr ) < 0 ) goto err; + if( *(data++) != ',' ) goto err; + if( ReadHex( &data, -1, &time ) < 0 ) goto err; + if( RVHandleBreakpoint( dev, cmd == 'Z', addr ) == 0 ) + { + SendReplyFull( "OK" ); + } + else + goto err; + break; + } + case 'm': + { + // Read memory (Binary) + uint32_t address_to_read = 0; + uint32_t length_to_read = 0; + if( ReadHex( &data, -1, &address_to_read ) < 0 ) goto err; + if( *(data++) != ',' ) goto err; + if( ReadHex( &data, -1, &length_to_read ) < 0 ) goto err; + + uint8_t * pl = alloca( length_to_read * 2 ); + if( RVReadMem( dev, address_to_read, pl, length_to_read ) < 0 ) + goto err; + char * repl = alloca( length_to_read * 2 ); + int i; + for( i = 0; i < length_to_read; i++ ) + { + sprintf( repl + i * 2, "%02x", pl[i] ); + } + SendReplyFull( repl ); + break; + } + case 'M': + { + uint32_t address_to_write = 0; + uint32_t length_to_write = 0; + if( ReadHex( &data, -1, &address_to_write ) < 0 ) goto err; + if( *(data++) != ',' ) goto err; + if( ReadHex( &data, -1, &length_to_write ) < 0 ) goto err; + if( *(data++) != ':' ) goto err; + + uint8_t * meml = alloca( length_to_write ); + int i; + for( i = 0; i < length_to_write; i++ ) + { + uint32_t rv; + if( ReadHex( &data, 2, &rv ) < 0 ) goto err; + meml[i] = rv; + } + if( RVWriteRAM( dev, address_to_write, length_to_write, meml ) < 0 ) goto err; + SendReplyFull( "OK" ); + break; + } + case 'X': + { + // Write memory, binary. + uint32_t address_to_write = 0; + uint32_t length_to_write = 0; + if( ReadHex( &data, -1, &address_to_write ) < 0 ) goto err; + if( *(data++) != ',' ) goto err; + if( ReadHex( &data, -1, &length_to_write ) < 0 ) goto err; + if( *(data++) != ':' ) goto err; + if( RVWriteRAM( dev, address_to_write, length_to_write, (uint8_t*)data ) < 0 ) goto err; + SendReplyFull( "OK" ); + break; + } + case 'v': + if( StringMatch( data, "Cont?" ) ) + { + // Request a list of actions supported by the ‘vCont’ packet. + // We don't support vCont + SendReplyFull( "vCont;s;c;t;" ); //no ;s + //SendReplyFull( "" ); + } + else + { + SendReplyFull( "" ); //"vMustReplyEmpty" + } + break; + case 'g': + { + // Register Read (All regs) + char cts[17*8+1]; + for( i = 0; i < 17; i++ ) + { + uint32_t regret; + if( RVReadCPURegister( dev, i, ®ret ) ) goto err; + sprintf( cts + i * 8, "%08x", htonl( regret ) ); + } + SendReplyFull( cts ); + break; + } + case 'p': + { + uint32_t regno; + // Register Read (Specific Reg) + if( ReadHex( &data, 2, ®no ) < 0 ) + SendReplyFull( "E 10" ); + else + { + char cts[9]; + uint32_t regret; + if( RVReadCPURegister( dev, regno, ®ret ) ) goto err; + sprintf( cts, "%08x", htonl( regret ) ); + SendReplyFull( cts ); + } + break; + } + case '?': // Query reason for target halt. + RVSendGDBHaltReason( dev ); + break; + case 'H': + // This is for things like selecting threads. + // I am going to leave this stubbed out. + SendReplyFull( "" ); + break; + default: + printf( "UNKNOWN PACKET: %d (%s)\n", len, data-1 ); + for( i = 0; i < len; i++ ) + { + printf( "%02x ", data[i] ); + } + printf( "\n" ); + goto err; + break; + } + return; +err: + SendReplyFull( "E 00" ); +} + +void MicroGDBStubHandleClientData( void * dev, const uint8_t * rxdata, int len ) +{ + int pl = 0; + for( pl = 0; pl < len; pl++ ) + { + int c = rxdata[pl]; + if( c == '$' && gdbbufferstate == 0 ) + { + gdbbufferplace = 0; + gdbbufferstate = 1; + } + if( c == 3 && gdbbufferstate == 0 ) + { + RVHandleGDBBreakRequest( dev ); + continue; + } + + switch( gdbbufferstate ) + { + default: + break; + case 1: + if( gdbbufferplace < sizeof( gdbbuffer ) - 2 ) + { + if( c == '}' ) { gdbbufferstate = 9; break; } + gdbbuffer[gdbbufferplace++] = c; + } + if( c == '#' ) gdbbufferstate = 2; + break; + case 9: // escape + gdbbuffer[gdbbufferplace++] = c ^ 0x20; + break; + case 2: + case 3: + { + int i; + c = fromhex( c ); + if( gdbbufferstate == 2 ) + { + gdbchecksum = c << 4; + gdbbufferstate++; + break; + } + if( gdbbufferstate == 3 ) gdbchecksum |= c; + + gdbbuffer[gdbbufferplace] = 0; + + uint8_t checkcsum = 0; + for( i = 1; i < gdbbufferplace - 1; i++ ) + { + checkcsum += ((uint8_t*)gdbbuffer)[i]; + } + if( checkcsum == gdbchecksum ) + { + MicroGDBStubSendReply( "+", -1, 0 ); + HandleGDBPacket( dev, (char*)gdbbuffer, gdbbufferplace ); + } + else + { + MicroGDBStubSendReply( "-", -1, 0 ); + } + + gdbbufferplace = 0; + gdbbufferstate = 0; + break; + } + } + } +} + + + + +#ifdef MICROGDBSTUB_SOCKETS + + +#ifdef WIN32 +#include <winsock2.h> +#define socklen_t uint32_t +#define SHUT_RDWR SD_BOTH +#define MSG_NOSIGNAL 0 +#else +#define closesocket close +#include <sys/types.h> +#include <sys/socket.h> +#include <linux/in.h> +#endif + +#include <fcntl.h> +#include <unistd.h> +#include <sys/time.h> +#include <poll.h> + +#include <stdio.h> +#include <string.h> +#include <time.h> +#include <stdlib.h> + + +int listenMode; // 0 for uninit. 1 for server, 2 for client. +int serverSocket; + +/////////////////////////////////////////////////////////////////////////////// +// Network layer. + + +void MicroGDBStubHandleDisconnect( void * dev ) +{ + RVHandleDisconnect( dev ); +} + +static int GDBListen( void * dev ) +{ + struct sockaddr_in sin; + serverSocket = socket(AF_INET, SOCK_STREAM, 0); + + //Make sure the socket worked. + if( serverSocket == -1 ) + { + fprintf( stderr, "Error: Cannot create socket.\n" ); + return -1; + } + + //Disable SO_LINGER (Well, enable it but turn it way down) +#ifdef WIN32 + struct linger lx; + lx.l_onoff = 1; + lx.l_linger = 0; + setsockopt( serverSocket, SOL_SOCKET, SO_LINGER, (const char *)&lx, sizeof( lx ) ); + + //Enable SO_REUSEADDR + int reusevar = 1; + setsockopt(serverSocket, SOL_SOCKET, SO_REUSEADDR, (const char*)&reusevar, sizeof(reusevar)); +#else + struct linger lx; + lx.l_onoff = 1; + lx.l_linger = 0; + setsockopt( serverSocket, SOL_SOCKET, SO_LINGER, &lx, sizeof( lx ) ); + + //Enable SO_REUSEADDR + int reusevar = 1; + setsockopt(serverSocket, SOL_SOCKET, SO_REUSEADDR, &reusevar, sizeof(reusevar)); +#endif + //Setup socket for listening address. + memset( &sin, 0, sizeof( sin ) ); + sin.sin_family = AF_INET; + sin.sin_addr.s_addr = INADDR_ANY; + sin.sin_port = htons( MICROGDBSTUB_PORT ); + + //Actually bind to the socket + if( bind( serverSocket, (struct sockaddr *) &sin, sizeof( sin ) ) == -1 ) + { + fprintf( stderr, "Could not bind to socket: %d\n", MICROGDBSTUB_PORT ); + closesocket( serverSocket ); + serverSocket = 0; + return -1; + } + + //Finally listen. + if( listen( serverSocket, 5 ) == -1 ) + { + fprintf(stderr, "Could not lieten to socket."); + closesocket( serverSocket ); + serverSocket = 0; + return -1; + } + return 0; +} + +int MicroGDBPollServer( void * dev ) +{ + if( !serverSocket ) return -4; + + struct pollfd allpolls[2]; + + int pollct = 1; + allpolls[0].fd = serverSocket; + allpolls[0].events = POLLIN; + + //Do something to watch all currently-waiting sockets. + poll( allpolls, pollct, 0 ); + + //If there's faults, bail. + if( allpolls[0].revents & (POLLERR|POLLHUP) ) + { + closesocket( serverSocket ); + if( listenMode == 1 ) + { + // Some sort of weird fatal close? Is this even possible? + fprintf( stderr, "Error: serverSocke was forcibly closed\n" ); + exit( -4 ); + } + else if( listenMode == 2 ) + { + MicroGDBStubHandleDisconnect( dev ); + if( serverSocket ) close( serverSocket ); + serverSocket = 0; + listenMode = 1; + GDBListen( dev ); + } + } + if( allpolls[0].revents & POLLIN ) + { + if( listenMode == 1 ) + { + struct sockaddr_in tin; + socklen_t addrlen = sizeof(tin); + memset( &tin, 0, addrlen ); + int tsocket = accept( serverSocket, (struct sockaddr *)&tin, &addrlen ); + closesocket( serverSocket ); + serverSocket = tsocket; + listenMode = 2; + gdbbufferstate = 0; + RVNetConnect( dev ); + // Established. + } + else if( listenMode == 2 ) + { + // Got data from a peer. + uint8_t buffer[16384]; + ssize_t rx = recv( serverSocket, buffer, sizeof( buffer ), MSG_NOSIGNAL ); + if( rx == 0 ) + { + MicroGDBStubHandleDisconnect( dev ); + close( serverSocket ); + serverSocket = 0; + listenMode = 1; + GDBListen( dev ); + } + else + MicroGDBStubHandleClientData( dev, buffer, (int)rx ); + } + } + + if( listenMode == 2 ) + { + RVNetPoll( dev ); + } + + return 0; +} + +void MicroGDBExitServer( void * dev ) +{ + shutdown( serverSocket, SHUT_RDWR ); + if( listenMode == 2 ) + { + MicroGDBStubHandleDisconnect( dev ); + } +} + + +void MicroGDBStubSendReply( const void * data, int len, int docs ) +{ + if( len < 0 ) len = strlen( data ); + if( docs ) + { + uint8_t * localbuffer = alloca( len ) + 5; + localbuffer[0] = '$'; + uint8_t checksum = 0; + int i; + for( i = 0; i < len; i++ ) + { + uint8_t v = ((const uint8_t*)data)[i]; + checksum += v; + localbuffer[i+1] = v; + } + localbuffer[len+1] = '#'; + localbuffer[len+2] = ToHEXNibble( checksum >> 4 ); + localbuffer[len+3] = ToHEXNibble( checksum ); + localbuffer[len+4] = 0; + data = (void*)localbuffer; + len += 4; + } + + if( listenMode == 2 ) + { + //printf( ">>>>%s<<<<(%d)\n", data ); + send( serverSocket, data, len, MSG_NOSIGNAL ); + } +} + + +int MicroGDBStubStartup( void * dev ) +{ +#ifdef WIN32 +{ + WORD wVersionRequested; + WSADATA wsaData; + int err; + wVersionRequested = MAKEWORD(2, 2); + + err = WSAStartup(wVersionRequested, &wsaData); + if (err != 0) { + /* Tell the user that we could not find a usable */ + /* Winsock DLL. */ + fprintf( stderr, "WSAStartup failed with error: %d\n", err); + return 1; + } +} +#endif + + listenMode = 1; + + return GDBListen( dev ); +} + + +#endif +#endif + +#endif + diff --git a/minichlink/minichgdb.c b/minichlink/minichgdb.c new file mode 100644 index 0000000000000000000000000000000000000000..c3f783a6c00c200f48cb03b2128c6bc1646a2aa6 --- /dev/null +++ b/minichlink/minichgdb.c @@ -0,0 +1,344 @@ +// This file is loosely based on aappleby's GDBServer. + +// Connect in with: +// gdb-multiarch -ex 'target remote :2000' ./blink.elf + +#include "minichlink.h" + +#define MICROGDBSTUB_IMPLEMENTATION +#define MICROGDBSTUB_SOCKETS +#define MICROGDBSTUB_PORT 2000 + + +const char* MICROGDBSTUB_MEMORY_MAP = "l<?xml version=\"1.0\"?>" +"<!DOCTYPE memory-map PUBLIC \"+//IDN gnu.org//DTD GDB Memory Map V1.0//EN\" \"http://sourceware.org/gdb/gdb-memory-map.dtd\">" +"<memory-map>" +" <memory type=\"flash\" start=\"0x00000000\" length=\"0x4000\">" +" <property name=\"blocksize\">64</property>" +" </memory>" +" <memory type=\"ram\" start=\"0x20000000\" length=\"0x800\">" +" <property name=\"blocksize\">4</property>" +" </memory>" +"</memory-map>"; + +#include "microgdbstub.h" + +void SendReplyFull( const char * replyMessage ); + +/////////////////////////////////////////////////////////////////////////////// +// Actual Chip Operations + +// Several pieces from picorvd. https://github.com/aappleby/PicoRVD/ +int shadow_running_state = 1; +int last_halt_reason = 5; +uint32_t backup_regs[17]; + +#define MAX_SOFTWARE_BREAKPOINTS 128 +int num_software_breakpoints = 0; +uint8_t software_breakpoint_type[MAX_SOFTWARE_BREAKPOINTS]; // 0 = not in use, 1 = 32-bit, 2 = 16-bit. +uint32_t software_breakpoint_addy[MAX_SOFTWARE_BREAKPOINTS]; +uint32_t previous_word_at_breakpoint_address[MAX_SOFTWARE_BREAKPOINTS]; + +int IsGDBServerInShadowHaltState( void * dev ) { return !shadow_running_state; } + +void RVCommandPrologue( void * dev ) +{ + if( !MCF.ReadCPURegister ) + { + fprintf( stderr, "Error: Programmer does not support register reading\n" ); + exit( -5 ); + } + + MCF.WriteReg32( dev, DMABSTRACTAUTO, 0 ); // Disable autoexec. + if( MCF.ReadAllCPURegisters( dev, backup_regs ) ) + { + fprintf( stderr, "WARNING: failed to preserve registers\n" ); + } + MCF.VoidHighLevelState( dev ); +} + +void RVCommandEpilogue( void * dev ) +{ + MCF.WriteReg32( dev, DMABSTRACTAUTO, 0 ); // Disable autoexec. + MCF.WriteAllCPURegisters( dev, backup_regs ); + MCF.VoidHighLevelState( dev ); +} + +void RVNetConnect( void * dev ) +{ + // ??? Should we actually halt? + MCF.HaltMode( dev, 0 ); + MCF.SetEnableBreakpoints( dev, 1, 0 ); + RVCommandPrologue( dev ); + shadow_running_state = 0; +} + +int RVSendGDBHaltReason( void * dev ) +{ + char st[5]; + sprintf( st, "T%02x", last_halt_reason ); + SendReplyFull( st ); + return 0; +} + +void RVNetPoll(void * dev ) +{ + if( !MCF.ReadReg32 ) + { + fprintf( stderr, "Error: Can't poll GDB because no ReadReg32 supported on this programmer\n" ); + return; + } + + uint32_t status; + if( MCF.ReadReg32( dev, DMSTATUS, &status ) ) + { + fprintf( stderr, "Error: Could not get part status\n" ); + return; + } + int statusrunning = ((status & (1<<10))); + + static int laststatus; + if( status != laststatus ) + { + //printf( "DMSTATUS: %08x => %08x\n", laststatus, status ); + laststatus = status; + } + if( statusrunning != shadow_running_state ) + { + // If was running but now is halted. + if( statusrunning == 0 ) + { + RVCommandPrologue( dev ); + //uint32_t dscr; + //MCF.ReadCPURegister( dev, 0x7b0, &dscr ); + last_halt_reason = 5;//((dscr>>6)&3)+5; + RVSendGDBHaltReason( dev ); + } + shadow_running_state = statusrunning; + } +} + +int RVReadCPURegister( void * dev, int regno, uint32_t * regret ) +{ + if( shadow_running_state ) + { + MCF.HaltMode( dev, 0 ); + RVCommandPrologue( dev ); + shadow_running_state = 0; + } + + if( regno == 32 ) regno = 16; // Hack - Make 32 also 16 for old GDBs. + if( regno > 16 ) return 0; // Invalid register. + + *regret = backup_regs[regno]; + return 0; +} + +void RVDebugExec( void * dev, int halt_reset_or_resume ) +{ + if( !MCF.HaltMode ) + { + fprintf( stderr, "Error: Can't alter halt mode with this programmer.\n" ); + exit( -6 ); + } + + // Special case halt_reset_or_resume = 4: Skip instruction and resume. + if( halt_reset_or_resume == 4 || halt_reset_or_resume == 2 ) + { + // For this we want to advance PC. + uint32_t exceptionptr = backup_regs[16]; + uint32_t instruction = 0; + if( exceptionptr & 2 ) + { + uint32_t part1, part2; + MCF.ReadWord( dev, exceptionptr & ~3, &part1 ); + MCF.ReadWord( dev, (exceptionptr & ~3)+4, &part2 ); + instruction = (part1 >> 16) | (part2 << 16); + } + else + { + MCF.ReadWord( dev, exceptionptr, &instruction ); + } + if( instruction == 0x00100073 ) + backup_regs[16]+=4; + else if( ( instruction & 0xffff ) == 0x9002 ) + backup_regs[16]+=2; + halt_reset_or_resume = 2; + } + + if( shadow_running_state != ( halt_reset_or_resume >= 2 ) ) + { + if( halt_reset_or_resume < 2 ) + { + RVCommandPrologue( dev ); + } + else + { + RVCommandEpilogue( dev ); + } + MCF.HaltMode( dev, halt_reset_or_resume ); + } + + shadow_running_state = halt_reset_or_resume >= 2; +} + +int RVReadMem( void * dev, uint32_t memaddy, uint8_t * payload, int len ) +{ + if( !MCF.ReadBinaryBlob ) + { + fprintf( stderr, "Error: Can't alter halt mode with this programmer.\n" ); + exit( -6 ); + } + int ret = MCF.ReadBinaryBlob( dev, memaddy, len, payload ); + if( ret < 0 ) + { + fprintf( stderr, "Error reading binary blob at %08x\n", memaddy ); + } + return ret; +} + +static int InternalDisableBreakpoint( void * dev, int i ) +{ + int r; + if( software_breakpoint_type[i] == 1 ) + { + //32-bit instruction + r = MCF.WriteBinaryBlob( dev, software_breakpoint_addy[i], 4, (uint8_t*)&previous_word_at_breakpoint_address[i] ); + } + else + { + //16-bit instruction + r = MCF.WriteBinaryBlob( dev, software_breakpoint_addy[i], 2, (uint8_t*)&previous_word_at_breakpoint_address[i] ); + } + previous_word_at_breakpoint_address[i] = 0; + software_breakpoint_type[i] = 0; + software_breakpoint_addy[i] = 0; + return r; +} + +int RVHandleBreakpoint( void * dev, int set, uint32_t address ) +{ + int i; + int first_free = -1; + for( i = 0; i < MAX_SOFTWARE_BREAKPOINTS; i++ ) + { + if( software_breakpoint_type[i] && software_breakpoint_addy[i] == address ) + break; + if( first_free < 0 && software_breakpoint_type[i] == 0 ) + first_free = i; + } + + if( i != MAX_SOFTWARE_BREAKPOINTS ) + { + // There is already a break slot here. + if( !set ) + { + InternalDisableBreakpoint( dev, i ); + } + else + { + // Already set. + } + } + else + { + if( first_free == -1 ) + { + fprintf( stderr, "Error: Too many breakpoints\n" ); + return -1; + } + if( set ) + { + i = first_free; + uint32_t readval_at_addy; + int r = MCF.ReadBinaryBlob( dev, address, 4, (uint8_t*)&readval_at_addy ); + if( r ) return -5; + if( ( readval_at_addy & 3 ) == 3 ) // Check opcode LSB's. + { + // 32-bit instruction. + software_breakpoint_type[i] = 1; + software_breakpoint_addy[i] = address; + previous_word_at_breakpoint_address[i] = readval_at_addy; + uint32_t ebreak = 0x00100073; // ebreak + MCF.WriteBinaryBlob( dev, address, 4, (uint8_t*)&ebreak ); + } + else + { + // 16-bit instructions + software_breakpoint_type[i] = 2; + software_breakpoint_addy[i] = address; + previous_word_at_breakpoint_address[i] = readval_at_addy & 0xffff; + uint32_t ebreak = 0x9002; // c.ebreak + MCF.WriteBinaryBlob( dev, address, 2, (uint8_t*)&ebreak ); + } + } + else + { + // Already unset. + } + } + + return 0; +} + +int RVWriteRAM(void * dev, uint32_t memaddy, uint32_t length, uint8_t * payload ) +{ + if( !MCF.WriteBinaryBlob ) + { + fprintf( stderr, "Error: Can't alter halt mode with this programmer.\n" ); + exit( -6 ); + } + + int r = MCF.WriteBinaryBlob( dev, memaddy, length, payload ); + + return r; +} + +void RVHandleDisconnect( void * dev ) +{ + MCF.HaltMode( dev, 0 ); + MCF.SetEnableBreakpoints( dev, 0, 0 ); + + int i; + for( i = 0; i < MAX_SOFTWARE_BREAKPOINTS; i++ ) + { + if( software_breakpoint_type[i] ) + { + InternalDisableBreakpoint( dev, i ); + } + } + + if( shadow_running_state == 0 ) + { + RVCommandEpilogue( dev ); + } + MCF.HaltMode( dev, 2 ); + shadow_running_state = 1; +} + +void RVHandleGDBBreakRequest( void * dev ) +{ + if( shadow_running_state ) + { + MCF.HaltMode( dev, 0 ); + } +} + + +int PollGDBServer( void * dev ) +{ + return MicroGDBPollServer( dev ); +} + +void ExitGDBServer( void * dev ) +{ + MicroGDBExitServer( dev ); +} + + +int SetupGDBServer( void * dev ) +{ + return MicroGDBStubStartup( dev ); +} + + diff --git a/minichlink/minichlink.c b/minichlink/minichlink.c index a384b3721a2855317c1b487768e308d033beecfa..0b44dea162e1e9f4fa3dbbc03898e1588d8b8ebb 100644 --- a/minichlink/minichlink.c +++ b/minichlink/minichlink.c @@ -160,24 +160,45 @@ keep_going: else goto unimplemented; break; + case 'G': case 'T': { if( !MCF.PollTerminal ) goto unimplemented; + + if( argchar[1] == 'G' && SetupGDBServer( dev ) ) + { + printf( "Error: can't start GDB server\n" ); + return -1; + } + do { uint8_t buffer[256]; - int r = MCF.PollTerminal( dev, buffer, sizeof( buffer ), 0, 0 ); - if( r < 0 ) + if( !IsGDBServerInShadowHaltState( dev ) ) { - fprintf( stderr, "Terminal dead. code %d\n", r ); - return -32; + int r = MCF.PollTerminal( dev, buffer, sizeof( buffer ), 0, 0 ); + if( r < 0 ) + { + fprintf( stderr, "Terminal dead. code %d\n", r ); + return -32; + } + if( r > 0 ) + { + fwrite( buffer, r, 1, stdout ); + } } - if( r > 0 ) + + if( argchar[1] == 'G' ) { - fwrite( buffer, r, 1, stdout ); + PollGDBServer( dev ); } } while( 1 ); + + // Currently unreachable - consider reachable-ing + if( argchar[1] == 'G' ) + ExitGDBServer( dev ); + break; } case 's': { @@ -201,7 +222,7 @@ keep_going: goto unimplemented; break; } - case 'g': + case 'm': { iarg+=1; if( iarg >= argc ) @@ -474,14 +495,16 @@ help: fprintf( stderr, " -D Configure NRST as GPIO\n" ); fprintf( stderr, " -d Configure NRST as NRST\n" ); fprintf( stderr, " -s [debug register] [value]\n" ); - fprintf( stderr, " -g [debug register]\n" ); + fprintf( stderr, " -m [debug register]\n" ); + fprintf( stderr, " -T Terminal Only\n" ); + fprintf( stderr, " -G Terminal + GDB\n" ); // fprintf( stderr, " -P Enable Read Protection (UNTESTED)\n" ); // fprintf( stderr, " -p Disable Read Protection (UNTESTED)\n" ); fprintf( stderr, " -w [binary image to write] [address, decimal or 0x, try0x08000000]\n" ); fprintf( stderr, " -r [output binary image] [memory address, decimal or 0x, try 0x08000000] [size, decimal or 0x, try 16384]\n" ); fprintf( stderr, " Note: for memory addresses, you can use 'flash' 'launcher' 'bootloader' 'option' 'ram' and say \"ram+0x10\" for instance\n" ); - fprintf( stderr, " For filename, you can use - for raw or + for hex.\n" ); - fprintf( stderr, " -T is a terminal. This MUST be the last argument. You MUST have resumed or \n" ); + fprintf( stderr, " For filename, you can use - for raw (terminal) or + for hex (inline).\n" ); + fprintf( stderr, " -T is a terminal. This MUST be the last argument. Also, will start a gdbserver.\n" ); return -1; @@ -651,7 +674,7 @@ static int InternalUnlockBootloader( void * dev ) -static int DefaultWriteHalfWord( void * dev, uint32_t address_to_write, uint32_t data ) +static int DefaultWriteHalfWord( void * dev, uint32_t address_to_write, uint16_t data ) { int ret = 0; struct InternalState * iss = (struct InternalState*)(((struct ProgrammerStructBase*)dev)->internal); @@ -677,7 +700,7 @@ static int DefaultWriteHalfWord( void * dev, uint32_t address_to_write, uint32_t return ret; } -static int DefaultReadHalfWord( void * dev, uint32_t address_to_write, uint32_t * data ) +static int DefaultReadHalfWord( void * dev, uint32_t address_to_write, uint16_t * data ) { int ret = 0; struct InternalState * iss = (struct InternalState*)(((struct ProgrammerStructBase*)dev)->internal); @@ -688,7 +711,7 @@ static int DefaultReadHalfWord( void * dev, uint32_t address_to_write, uint32_t // Different address, so we don't need to re-write all the program regs. // lh x8,0(x9) // Write to the address. - MCF.WriteReg32( dev, DMPROGBUF0, 0x00049403 ); + MCF.WriteReg32( dev, DMPROGBUF0, 0x00049403 ); // lh x8, 0(x9) MCF.WriteReg32( dev, DMPROGBUF1, 0x00100073 ); // c.ebreak MCF.WriteReg32( dev, DMDATA0, address_to_write ); @@ -699,8 +722,64 @@ static int DefaultReadHalfWord( void * dev, uint32_t address_to_write, uint32_t ret |= MCF.WaitForDoneOp( dev ); iss->currentstateval = -1; + uint32_t rr; + ret |= MCF.ReadReg32( dev, DMDATA0, &rr ); + *data = rr; + return ret; +} + + + +static int DefaultWriteByte( void * dev, uint32_t address_to_write, uint8_t data ) +{ + int ret = 0; + struct InternalState * iss = (struct InternalState*)(((struct ProgrammerStructBase*)dev)->internal); + if( MCF.VoidHighLevelState ) MCF.VoidHighLevelState( dev ); + iss->statetag = STTAG( "XXXX" ); + + MCF.WriteReg32( dev, DMABSTRACTAUTO, 0x00000000 ); // Disable Autoexec. - return ret | MCF.ReadReg32( dev, DMDATA0, data ); + // Different address, so we don't need to re-write all the program regs. + // sh x8,0(x9) // Write to the address. + MCF.WriteReg32( dev, DMPROGBUF0, 0x00848023 ); // sb x8, 0(x9) + MCF.WriteReg32( dev, DMPROGBUF1, 0x00100073 ); // c.ebreak + + MCF.WriteReg32( dev, DMDATA0, address_to_write ); + MCF.WriteReg32( dev, DMCOMMAND, 0x00231009 ); // Copy data to x9 + MCF.WriteReg32( dev, DMDATA0, data ); + MCF.WriteReg32( dev, DMCOMMAND, 0x00271008 ); // Copy data to x8, and execute program. + + ret |= MCF.WaitForDoneOp( dev ); + iss->currentstateval = -1; + return ret; +} + +static int DefaultReadByte( void * dev, uint32_t address_to_write, uint8_t * data ) +{ + int ret = 0; + struct InternalState * iss = (struct InternalState*)(((struct ProgrammerStructBase*)dev)->internal); + if( MCF.VoidHighLevelState ) MCF.VoidHighLevelState( dev ); + iss->statetag = STTAG( "XXXX" ); + + MCF.WriteReg32( dev, DMABSTRACTAUTO, 0x00000000 ); // Disable Autoexec. + + // Different address, so we don't need to re-write all the program regs. + // lb x8,0(x9) // Write to the address. + MCF.WriteReg32( dev, DMPROGBUF0, 0x00048403 ); // lb x8, 0(x9) + MCF.WriteReg32( dev, DMPROGBUF1, 0x00100073 ); // c.ebreak + + MCF.WriteReg32( dev, DMDATA0, address_to_write ); + MCF.WriteReg32( dev, DMCOMMAND, 0x00231009 ); // Copy data to x9 + MCF.WriteReg32( dev, DMCOMMAND, 0x00241000 ); // Only execute. + MCF.WriteReg32( dev, DMCOMMAND, 0x00221008 ); // Read x8 into DATA0. + + ret |= MCF.WaitForDoneOp( dev ); + iss->currentstateval = -1; + + uint32_t rr; + ret |= MCF.ReadReg32( dev, DMDATA0, &rr ); + *data = rr; + return ret; } @@ -812,7 +891,11 @@ int DefaultWriteBinaryBlob( void * dev, uint32_t address_to_write, uint32_t blob if( (address_to_write & 0xff000000) == 0x08000000 || (address_to_write & 0xff000000) == 0x00000000 || (address_to_write & 0x1FFFF800) == 0x1FFFF000 ) is_flash = 1; - if( is_flash && MCF.BlockWrite64 && ( address_to_write & 0x3f ) == 0 ) + // We can't write into flash when mapped to 0x00000000 + if( is_flash ) + address_to_write |= 0x08000000; + + if( is_flash && MCF.BlockWrite64 && ( address_to_write & 0x3f ) == 0 && ( blob_size & 0x3f ) == 0 ) { int i; for( i = 0; i < blob_size; i+= 64 ) @@ -827,76 +910,137 @@ int DefaultWriteBinaryBlob( void * dev, uint32_t address_to_write, uint32_t blob return 0; } - if( is_flash ) + if( is_flash && !iss->flash_unlocked ) { - // Need to unlock flash. - // Flash reg base = 0x40022000, - // FLASH_MODEKEYR => 0x40022024 - // FLASH_KEYR => 0x40022004 - - if( !iss->flash_unlocked ) - { - if( ( rw = StaticUnlockFlash( dev, iss ) ) ) - return rw; - } - - is_flash = 1; - - printf( "Erasing TO %08x %08x\n", address_to_write, blob_size ); - MCF.Erase( dev, address_to_write, blob_size, 0 ); + if( ( rw = StaticUnlockFlash( dev, iss ) ) ) + return rw; } - printf( "Done\n" ); - MCF.FlushLLCommands( dev ); - MCF.DelayUS( dev, 100 ); // Why do we need this? - uint32_t wp = address_to_write; - uint32_t ew = wp + blob_size; - int group = -1; - while( wp < ew ) + uint8_t tempblock[64]; + int sblock = address_to_write >> 6; + int eblock = ( address_to_write + blob_size + 0x3f) >> 6; + int b; + int rsofar = 0; + + for( b = sblock; b < eblock; b++ ) { - if( is_flash ) - { - group = (wp & 0xffffffc0); - MCF.WriteWord( dev, 0x40022010, CR_PAGE_PG ); // THIS IS REQUIRED, (intptr_t)&FLASH->CTLR = 0x40022010 - MCF.WriteWord( dev, 0x40022010, CR_BUF_RST | CR_PAGE_PG ); // (intptr_t)&FLASH->CTLR = 0x40022010 + int offset_in_block = address_to_write - (b * 64); + if( offset_in_block < 0 ) offset_in_block = 0; + int end_o_plus_one_in_block = ( address_to_write + blob_size ) - (b*64); + if( end_o_plus_one_in_block > 64 ) end_o_plus_one_in_block = 64; + int base = b * 64; - int j; - for( j = 0; j < 16; j++ ) + if( offset_in_block == 0 && end_o_plus_one_in_block == 64 ) + { + if( MCF.BlockWrite64 ) + { + int r = MCF.BlockWrite64( dev, base, blob + rsofar ); + rsofar += 64; + if( r ) + { + fprintf( stderr, "Error writing block at memory %08x\n", base ); + return r; + } + } + else // Block Write not avaialble { - int index = (wp-address_to_write); - uint32_t data = 0xffffffff; - if( index + 3 < blob_size ) - data = ((uint32_t*)blob)[index/4]; - else if( (int32_t)(blob_size - index) > 0 ) + if( is_flash ) + { + MCF.Erase( dev, base, 64, 0 ); + MCF.WriteWord( dev, 0x40022010, CR_PAGE_PG ); // THIS IS REQUIRED, (intptr_t)&FLASH->CTLR = 0x40022010 + MCF.WriteWord( dev, 0x40022010, CR_BUF_RST | CR_PAGE_PG ); // (intptr_t)&FLASH->CTLR = 0x40022010 + } + + int j; + for( j = 0; j < 16; j++ ) + { + uint32_t writeword; + memcpy( &writeword, blob + rsofar, 4 ); + MCF.WriteWord( dev, j*4+base, writeword ); + rsofar += 4; + } + + if( is_flash ) { - printf( "%d %d\n", blob_size, index ); - memcpy( &data, &blob[index], blob_size - index ); + MCF.WriteWord( dev, 0x40022014, base ); //0x40022014 -> FLASH->ADDR + MCF.WriteWord( dev, 0x40022010, CR_PAGE_PG|CR_STRT_Set ); // 0x40022010 -> FLASH->CTLR + if( MCF.WaitForFlash ) MCF.WaitForFlash( dev ); } - MCF.WriteWord( dev, wp, data ); - wp += 4; } - MCF.WriteWord( dev, 0x40022014, group ); //0x40022014 -> FLASH->ADDR - MCF.WriteWord( dev, 0x40022010, CR_PAGE_PG|CR_STRT_Set ); // 0x40022010 -> FLASH->CTLR - if( MCF.WaitForFlash ) MCF.WaitForFlash( dev ); } else { - int index = (wp-address_to_write); - uint32_t data = 0xffffffff; - if( index + 3 < blob_size ) - data = ((uint32_t*)blob)[index/4]; - else if( (int32_t)(blob_size - index) > 0 ) - memcpy( &data, &blob[index], blob_size - index ); - MCF.WriteWord( dev, wp, data ); - wp += 4; + //Ok, we have to do something wacky. + if( is_flash ) + { + MCF.ReadBinaryBlob( dev, base, 64, tempblock ); + MCF.Erase( dev, base, 64, 0 ); + MCF.WriteWord( dev, 0x40022010, CR_PAGE_PG ); // THIS IS REQUIRED, (intptr_t)&FLASH->CTLR = 0x40022010 + MCF.WriteWord( dev, 0x40022010, CR_BUF_RST | CR_PAGE_PG ); // (intptr_t)&FLASH->CTLR = 0x40022010 + + // Permute tempblock + int tocopy = end_o_plus_one_in_block - offset_in_block; + memcpy( tempblock + offset_in_block, blob + rsofar, tocopy ); + rsofar += tocopy; + + int j; + for( j = 0; j < 16; j++ ) + { + MCF.WriteWord( dev, j*4+base, *(uint32_t*)(tempblock + j * 4) ); + rsofar += 4; + } + MCF.WriteWord( dev, 0x40022014, base ); //0x40022014 -> FLASH->ADDR + MCF.WriteWord( dev, 0x40022010, CR_PAGE_PG|CR_STRT_Set ); // 0x40022010 -> FLASH->CTLR + if( MCF.WaitForFlash && MCF.WaitForFlash( dev ) ) goto timedout; + } + else + { + // Accessing RAM. Be careful to only do the needed operations. + int j; + for( j = 0; j < 16; j++ ) + { + uint32_t taddy = j*4; + if( offset_in_block <= taddy && end_o_plus_one_in_block >= taddy + 4 ) + { + MCF.WriteWord( dev, taddy + base, *(uint32_t*)(blob + rsofar) ); + rsofar += 4; + } + else if( ( offset_in_block & 1 ) || ( end_o_plus_one_in_block & 1 ) ) + { + // Bytes only. + int j; + for( j = 0; j < 4; j++ ) + { + if( taddy >= offset_in_block && taddy < end_o_plus_one_in_block ) + { + MCF.WriteByte( dev, taddy + base, *(uint32_t*)(blob + rsofar) ); + rsofar ++; + } + taddy++; + } + } + else + { + // Half-words + int j; + for( j = 0; j < 4; j+=2 ) + { + if( taddy >= offset_in_block && taddy < end_o_plus_one_in_block ) + { + MCF.WriteHalfWord( dev, taddy + base, *(uint32_t*)(blob + rsofar) ); + rsofar +=2; + } + taddy+=2; + } + } + } + } } } - if( is_flash ) - { - if( MCF.WaitForFlash && MCF.WaitForFlash( dev ) ) goto timedout; - } + MCF.FlushLLCommands( dev ); + MCF.DelayUS( dev, 100 ); // Why do we need this? return 0; timedout: fprintf( stderr, "Timed out\n" ); @@ -906,17 +1050,34 @@ timedout: static int DefaultReadWord( void * dev, uint32_t address_to_read, uint32_t * data ) { struct InternalState * iss = (struct InternalState*)(((struct ProgrammerStructBase*)dev)->internal); - if( iss->statetag != STTAG( "RDSQ" ) || address_to_read != iss->currentstateval ) + + 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" ) ) { MCF.WriteReg32( 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. MCF.WriteReg32( dev, DMPROGBUF0, 0x40044180 ); - // c.addi x8, 4 - // c.sw x9, 0(x10) // Write back to DATA0 - MCF.WriteReg32( dev, DMPROGBUF1, 0xc1040411 ); + if( autoincrement ) + { + // c.addi x8, 4 + // c.sw x9, 0(x10) // Write back to DATA0 + + MCF.WriteReg32( dev, DMPROGBUF1, 0xc1040411 ); + } + else + { + // c.nop + // c.sw x9, 0(x10) // Write back to DATA0 + + MCF.WriteReg32( dev, DMPROGBUF1, 0xc1040001 ); + } // c.sw x8, 0(x11) // Write addy to DATA1 // c.ebreak MCF.WriteReg32( dev, DMPROGBUF2, 0x9002c180 ); @@ -925,7 +1086,8 @@ static int DefaultReadWord( void * dev, uint32_t address_to_read, uint32_t * dat { StaticUpdatePROGBUFRegs( dev ); } - MCF.WriteReg32( dev, DMABSTRACTAUTO, 1 ); // Enable Autoexec. + MCF.WriteReg32( dev, DMABSTRACTAUTO, 1 ); // Enable Autoexec (different kind of autoinc than outer autoinc) + iss->autoincrement = autoincrement; } MCF.WriteReg32( dev, DMDATA1, address_to_read ); @@ -937,9 +1099,11 @@ static int DefaultReadWord( void * dev, uint32_t address_to_read, uint32_t * dat MCF.WaitForDoneOp( dev ); } - iss->currentstateval += 4; + if( iss->autoincrement ) + iss->currentstateval += 4; - return MCF.ReadReg32( dev, DMDATA0, data ); + int r = MCF.ReadReg32( dev, DMDATA0, data ); + return r; } static int StaticUnlockFlash( void * dev, struct InternalState * iss ) @@ -1017,20 +1181,157 @@ int DefaultReadBinaryBlob( void * dev, uint32_t address_to_read_from, uint32_t r { uint32_t rpos = address_to_read_from; uint32_t rend = address_to_read_from + read_size; + while( rpos < rend ) { - uint32_t rw; - int r = DefaultReadWord( dev, rpos, &rw ); - if( r ) return r; + int r; int remain = rend - rpos; - if( remain > 3 ) remain = 4; - memcpy( blob, &rw, remain ); - blob += 4; - rpos += 4; + + if( ( rpos & 3 ) == 0 && remain >= 4 ) + { + uint32_t rw; + r = MCF.ReadWord( dev, rpos, &rw ); + if( r ) return r; + memcpy( blob, &rw, remain ); + blob += 4; + rpos += 4; + } + else + { + if( ( rpos & 1 ) ) + { + uint8_t rw; + r = MCF.ReadByte( dev, rpos, &rw ); + if( r ) return r; + memcpy( blob, &rw, 1 ); + blob += 1; + rpos += 1; + remain -= 1; + } + if( ( rpos & 2 ) && remain >= 2 ) + { + uint16_t rw; + r = MCF.ReadHalfWord( dev, rpos, &rw ); + if( r ) return r; + memcpy( blob, &rw, 2 ); + blob += 2; + rpos += 2; + remain -= 2; + } + if( remain >= 1 ) + { + uint8_t rw; + r = MCF.ReadByte( dev, rpos, &rw ); + if( r ) return r; + memcpy( blob, &rw, 1 ); + blob += 1; + rpos += 1; + remain -= 1; + } + } } return 0; } +int DefaultReadCPURegister( void * dev, uint32_t regno, uint32_t * regret ) +{ + if( !MCF.WriteReg32 || !MCF.ReadReg32 ) + { + fprintf( stderr, "Error: Can't read CPU register on this programmer because it is missing read/writereg32\n" ); + return -5; + } + + struct InternalState * iss = (struct InternalState*)(((struct ProgrammerStructBase*)dev)->internal); + MCF.WriteReg32( dev, DMABSTRACTAUTO, 0x00000000 ); // Disable Autoexec. + iss->statetag = STTAG( "REGR" ); + + MCF.WriteReg32( dev, DMCOMMAND, 0x00220000 | regno ); // Read xN into DATA0. + int r = MCF.ReadReg32( dev, DMDATA0, regret ); + + return r; +} + +int DefaultReadAllCPURegisters( void * dev, uint32_t * regret ) +{ + struct InternalState * iss = (struct InternalState*)(((struct ProgrammerStructBase*)dev)->internal); + MCF.WriteReg32( dev, DMABSTRACTAUTO, 0x00000001 ); // Disable Autoexec. + iss->statetag = STTAG( "RER2" ); + int i; + for( i = 0; i < 16; i++ ) + { + MCF.WriteReg32( dev, DMCOMMAND, 0x00220000 | 0x1000 | i ); // Read xN into DATA0. + if( MCF.ReadReg32( dev, DMDATA0, regret + i ) ) + { + MCF.WriteReg32( dev, DMABSTRACTAUTO, 0x00000000 ); // Disable Autoexec. + return -5; + } + } + MCF.WriteReg32( dev, DMCOMMAND, 0x00220000 | 0x7b1 ); // Read xN into DATA0. + int r = MCF.ReadReg32( dev, DMDATA0, regret + i ); + MCF.WriteReg32( dev, DMABSTRACTAUTO, 0x00000000 ); // Disable Autoexec. + return r; +} + +int DefaultWriteAllCPURegisters( void * dev, uint32_t * regret ) +{ + struct InternalState * iss = (struct InternalState*)(((struct ProgrammerStructBase*)dev)->internal); + MCF.WriteReg32( dev, DMABSTRACTAUTO, 0x00000001 ); // Disable Autoexec. + iss->statetag = STTAG( "WER2" ); + int i; + for( i = 0; i < 16; i++ ) + { + MCF.WriteReg32( dev, DMCOMMAND, 0x00230000 | 0x1000 | i ); // Read xN into DATA0. + if( MCF.WriteReg32( dev, DMDATA0, regret[i] ) ) + { + MCF.WriteReg32( dev, DMABSTRACTAUTO, 0x00000000 ); // Disable Autoexec. + return -5; + } + } + MCF.WriteReg32( dev, DMCOMMAND, 0x00230000 | 0x7b1 ); // Read xN into DATA0. + int r = MCF.WriteReg32( dev, DMDATA0, regret[i] ); + MCF.WriteReg32( dev, DMABSTRACTAUTO, 0x00000000 ); // Disable Autoexec. + return r; +} + + +int DefaultWriteCPURegister( void * dev, uint32_t regno, uint32_t value ) +{ + if( !MCF.WriteReg32 || !MCF.ReadReg32 ) + { + fprintf( stderr, "Error: Can't read CPU register on this programmer because it is missing read/writereg32\n" ); + return -5; + } + + struct InternalState * iss = (struct InternalState*)(((struct ProgrammerStructBase*)dev)->internal); + MCF.WriteReg32( dev, DMABSTRACTAUTO, 0x00000000 ); // Disable Autoexec. + iss->statetag = STTAG( "REGW" ); + MCF.WriteReg32( dev, DMDATA0, value ); + return MCF.WriteReg32( dev, DMCOMMAND, 0x00230000 | regno ); // Write xN from DATA0. +} + +int DefaultSetEnableBreakpoints( void * dev, int is_enabled, int single_step ) +{ + if( !MCF.ReadCPURegister || !MCF.WriteCPURegister ) + { + fprintf( stderr, "Error: Can't set breakpoints on this programmer because it is missing read/writereg32\n" ); + return -5; + } + uint32_t DCSR; + if( MCF.ReadCPURegister( dev, 0x7b0, &DCSR ) ) + fprintf( stderr, "Error: DCSR could not be read\n" ); + DCSR |= 0xb600; + if( single_step ) + DCSR |= 4; + else + DCSR &=~4; + + //printf( "Setting DCSR: %08x\n", DCSR ); + if( MCF.WriteCPURegister( dev, 0x7b0, DCSR ) ) + fprintf( stderr, "Error: DCSR could not be read\n" ); + + return 0; +} + static int DefaultHaltMode( void * dev, int mode ) { @@ -1342,6 +1643,13 @@ fail: return -11; } +int DefaultVoidHighLevelState( void * dev ) +{ + struct InternalState * iss = (struct InternalState*)(((struct ProgrammerStructBase*)dev)->internal); + iss->statetag = STTAG( "VOID" ); + return 0; +} + int SetupAutomaticHighLevelFunctions( void * dev ) { // Will populate high-level functions from low-level functions. @@ -1360,10 +1668,24 @@ int SetupAutomaticHighLevelFunctions( void * dev ) MCF.WriteWord = DefaultWriteWord; if( !MCF.WriteHalfWord ) MCF.WriteHalfWord = DefaultWriteHalfWord; + if( !MCF.WriteByte ) + MCF.WriteByte = DefaultWriteByte; + if( !MCF.ReadCPURegister ) + MCF.ReadCPURegister = DefaultReadCPURegister; + if( !MCF.WriteCPURegister ) + MCF.WriteCPURegister = DefaultWriteCPURegister; + if( !MCF.WriteAllCPURegisters ) + MCF.WriteAllCPURegisters = DefaultWriteAllCPURegisters; + if( !MCF.ReadAllCPURegisters ) + MCF.ReadAllCPURegisters = DefaultReadAllCPURegisters; + if( !MCF.SetEnableBreakpoints ) + MCF.SetEnableBreakpoints = DefaultSetEnableBreakpoints; if( !MCF.ReadWord ) MCF.ReadWord = DefaultReadWord; if( !MCF.ReadHalfWord ) MCF.ReadHalfWord = DefaultReadHalfWord; + if( !MCF.ReadByte ) + MCF.ReadByte = DefaultReadByte; if( !MCF.Erase ) MCF.Erase = DefaultErase; if( !MCF.HaltMode ) @@ -1380,10 +1702,13 @@ int SetupAutomaticHighLevelFunctions( void * dev ) MCF.Unbrick = DefaultUnbrick; if( !MCF.ConfigureNRSTAsGPIO ) MCF.ConfigureNRSTAsGPIO = DefaultConfigureNRSTAsGPIO; + if( !MCF.VoidHighLevelState ) + MCF.VoidHighLevelState = DefaultVoidHighLevelState; struct InternalState * iss = malloc( sizeof( struct InternalState ) ); iss->statetag = 0; iss->currentstateval = 0; + iss->autoincrement = 0; ((struct ProgrammerStructBase*)dev)->internal = iss; return 0; diff --git a/minichlink/minichlink.h b/minichlink/minichlink.h index 9a0e1e64a66479a0b405c6603c16e290c8865af4..008e8f8e5650331d6abdf66a8dd908eef8b46718 100644 --- a/minichlink/minichlink.h +++ b/minichlink/minichlink.h @@ -24,10 +24,10 @@ struct MiniChlinkFunctions int (*HaltMode)( void * dev, int mode ); //0 for halt, 1 for reset, 2 for resume int (*ConfigureNRSTAsGPIO)( void * dev, int one_if_yes_gpio ); - // WARNING: Reading/writing must be at 32-bit boundaries for 32-bit sizes. - // WARNING: Writing binary blobs may write groups of 64-bytes. + // No boundary or limit rules. Must support any combination of alignment and size. int (*WriteBinaryBlob)( void * dev, uint32_t address_to_write, uint32_t blob_size, uint8_t * blob ); int (*ReadBinaryBlob)( void * dev, uint32_t address_to_read_from, uint32_t read_size, uint8_t * blob ); + int (*Erase)( void * dev, uint32_t address, uint32_t length, int type ); //type = 0 for fast, 1 for whole-chip // MUST be 4-byte-aligned. @@ -35,6 +35,20 @@ struct MiniChlinkFunctions int (*WriteWord)( void * dev, uint32_t address_to_write, uint32_t data ); int (*ReadWord)( void * dev, uint32_t address_to_read, uint32_t * data ); + // Debugging operations. + // Note: You must already be in break mode to use these otherwise they + // will return nonsensical data. + // For x0...xN, use 0x1000 + regno. + // For PC, use 0x7b1 + int (*ReadCPURegister)( void * dev, uint32_t regno, uint32_t * regret ); + int (*WriteCPURegister)( void * dev, uint32_t regno, uint32_t regval ); + + // Actually returns 17 registers (All 16 CPU registers + the debug register) + int (*ReadAllCPURegisters)( void * dev, uint32_t * regret ); + int (*WriteAllCPURegisters)( void * dev, uint32_t * regret ); + + int (*SetEnableBreakpoints)( void * dev, int halt_on_break, int single_step ); + int (*WaitForFlash)( void * dev ); int (*WaitForDoneOp)( void * dev ); @@ -57,9 +71,12 @@ struct MiniChlinkFunctions int (*VendorCommand)( void * dev, const char * command ); - // Do Not override these. they are cursed. - int (*WriteHalfWord)( void * dev, uint32_t address_to_write, uint32_t data ); - int (*ReadHalfWord)( void * dev, uint32_t address_to_read, uint32_t * data ); + // Probably no need to override these. The base layer handles them. + int (*WriteHalfWord)( void * dev, uint32_t address_to_write, uint16_t data ); + int (*ReadHalfWord)( void * dev, uint32_t address_to_read, uint16_t * data ); + + int (*WriteByte)( void * dev, uint32_t address_to_write, uint8_t data ); + int (*ReadByte)( void * dev, uint32_t address_to_read, uint8_t * data ); }; /** If you are writing a driver, the minimal number of functions you can implement are: @@ -86,6 +103,7 @@ struct InternalState uint32_t flash_unlocked; int lastwriteflags; int processor_in_mode; + int autoincrement; }; @@ -123,5 +141,14 @@ int SetupAutomaticHighLevelFunctions( void * dev ); // Useful for converting numbers like 0x, etc. int64_t SimpleReadNumberInt( const char * number, int64_t defaultNumber ); +// For drivers to call +int DefaultVoidHighLevelState( void * dev ); + +// GDBSever Functions +int SetupGDBServer( void * dev ); +int PollGDBServer( void * dev ); +int IsGDBServerInShadowHaltState( void * dev ); +void ExitGDBServer( void * dev ); + #endif diff --git a/minichlink/pgm-esp32s2-ch32xx.c b/minichlink/pgm-esp32s2-ch32xx.c index 212f66f51f83e92089231caefc96deb8b7ce335d..27869ff4f479ed067c6f3eae814bca0c1e64c4f4 100644 --- a/minichlink/pgm-esp32s2-ch32xx.c +++ b/minichlink/pgm-esp32s2-ch32xx.c @@ -252,6 +252,7 @@ int ESPVoidHighLevelState( void * dev ) struct ESP32ProgrammerStruct * eps = (struct ESP32ProgrammerStruct *)dev; Write2LE( eps, 0x05fe ); ESPFlushLLCommands( dev ); + DefaultVoidHighLevelState( dev ); return 0; }