2056 lines
60 KiB
C
2056 lines
60 KiB
C
// The rest of the code, Copyright 2023 Charles Lohr
|
|
// Freely licensable under the MIT/x11, NewBSD Licenses, or
|
|
// public domain where applicable.
|
|
|
|
// TODO: Can we make a unified DMPROG for reading + writing?
|
|
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include <stdlib.h>
|
|
#include <getopt.h>
|
|
#include "terminalhelp.h"
|
|
#include "minichlink.h"
|
|
#include "../ch32v003fun/ch32v003fun.h"
|
|
|
|
#if defined(WINDOWS) || defined(WIN32) || defined(_WIN32)
|
|
#ifndef _SYNCHAPI_H_
|
|
void Sleep(uint32_t dwMilliseconds);
|
|
#endif
|
|
#else
|
|
#include <unistd.h>
|
|
#endif
|
|
|
|
static int64_t StringToMemoryAddress( const char * number ) __attribute__((used));
|
|
static void StaticUpdatePROGBUFRegs( void * dev ) __attribute__((used));
|
|
int DefaultReadBinaryBlob( void * dev, uint32_t address_to_read_from, uint32_t read_size, uint8_t * blob );
|
|
|
|
void TestFunction(void * v );
|
|
struct MiniChlinkFunctions MCF;
|
|
|
|
void * MiniCHLinkInitAsDLL( struct MiniChlinkFunctions ** MCFO, const init_hints_t* init_hints )
|
|
{
|
|
void * dev = 0;
|
|
|
|
const char * specpgm = init_hints->specific_programmer;
|
|
if( specpgm )
|
|
{
|
|
if( strcmp( specpgm, "linke" ) == 0 )
|
|
dev = TryInit_WCHLinkE();
|
|
else if( strcmp( specpgm, "esp32s2chfun" ) == 0 )
|
|
dev = TryInit_ESP32S2CHFUN();
|
|
else if( strcmp( specpgm, "nchlink" ) == 0 )
|
|
dev = TryInit_NHCLink042();
|
|
else if( strcmp( specpgm, "b003boot" ) == 0 )
|
|
dev = TryInit_B003Fun();
|
|
else if( strcmp( specpgm, "ardulink" ) == 0 )
|
|
dev = TryInit_B003Fun();
|
|
}
|
|
else
|
|
{
|
|
if( (dev = TryInit_WCHLinkE()) )
|
|
{
|
|
fprintf( stderr, "Found WCH Link\n" );
|
|
}
|
|
else if( (dev = TryInit_ESP32S2CHFUN()) )
|
|
{
|
|
fprintf( stderr, "Found ESP32S2 Programmer\n" );
|
|
}
|
|
else if ((dev = TryInit_NHCLink042()))
|
|
{
|
|
fprintf( stderr, "Found NHC-Link042 Programmer\n" );
|
|
}
|
|
else if ((dev = TryInit_B003Fun()))
|
|
{
|
|
fprintf( stderr, "Found B003Fun Bootloader\n" );
|
|
}
|
|
else if ( init_hints->serial_port && (dev = TryInit_Ardulink(init_hints)))
|
|
{
|
|
fprintf( stderr, "Found Ardulink Programmer\n" );
|
|
}
|
|
}
|
|
|
|
if( !dev )
|
|
{
|
|
fprintf( stderr, "Error: Could not initialize any supported programmers\n" );
|
|
return 0;
|
|
}
|
|
|
|
struct InternalState * iss = calloc( 1, sizeof( struct InternalState ) );
|
|
((struct ProgrammerStructBase*)dev)->internal = iss;
|
|
iss->ram_base = 0x20000000;
|
|
iss->ram_size = 4*0x400; // minimal size for CH32V003
|
|
iss->sector_size = 64;
|
|
iss->flash_size = 16384;
|
|
iss->target_chip_type = 0;
|
|
|
|
SetupAutomaticHighLevelFunctions( dev );
|
|
|
|
if( MCFO )
|
|
{
|
|
*MCFO = &MCF;
|
|
}
|
|
return dev;
|
|
}
|
|
|
|
#if !defined( MINICHLINK_AS_LIBRARY ) && !defined( MINICHLINK_IMPORT )
|
|
int main( int argc, char ** argv )
|
|
{
|
|
int i;
|
|
|
|
if( argc > 1 && argv[1][0] == '-' && argv[1][1] == 'h' )
|
|
{
|
|
goto help;
|
|
}
|
|
init_hints_t hints;
|
|
memset(&hints, 0, sizeof(hints));
|
|
|
|
// Scan for possible hints.
|
|
for( i = 0; i < argc; i++ )
|
|
{
|
|
char * v = argv[i];
|
|
if( strncmp( v, "-c", 2 ) == 0 )
|
|
{
|
|
i++;
|
|
if( i < argc )
|
|
hints.serial_port = argv[i];
|
|
}
|
|
else if( strncmp( v, "-C", 2 ) == 0 )
|
|
{
|
|
i++;
|
|
if( i < argc )
|
|
hints.specific_programmer = argv[i];
|
|
}
|
|
}
|
|
|
|
void * dev = MiniCHLinkInitAsDLL( 0, &hints );
|
|
if( !dev )
|
|
{
|
|
fprintf( stderr, "Error: Could not initialize any supported programmers\n" );
|
|
return -32;
|
|
}
|
|
|
|
int status;
|
|
int must_be_end = 0;
|
|
|
|
int skip_startup =
|
|
(argc > 1 && argv[1][0] == '-' && argv[1][1] == 'u' ) |
|
|
(argc > 1 && argv[1][0] == '-' && argv[1][1] == 'h' ) |
|
|
(argc > 1 && argv[1][0] == '-' && argv[1][1] == 't' ) |
|
|
(argc > 1 && argv[1][0] == '-' && argv[1][1] == 'f' ) |
|
|
(argc > 1 && argv[1][0] == '-' && argv[1][1] == 'X' );
|
|
|
|
if( !skip_startup && MCF.SetupInterface )
|
|
{
|
|
if( MCF.SetupInterface( dev ) < 0 )
|
|
{
|
|
fprintf( stderr, "Could not setup interface.\n" );
|
|
return -33;
|
|
}
|
|
printf( "Interface Setup\n" );
|
|
}
|
|
|
|
// TestFunction( dev );
|
|
|
|
int iarg = 1;
|
|
const char * lastcommand = 0;
|
|
for( ; iarg < argc; iarg++ )
|
|
{
|
|
char * argchar = argv[iarg];
|
|
|
|
lastcommand = argchar;
|
|
if( argchar[0] != '-' )
|
|
{
|
|
fprintf( stderr, "Error: Need prefixing - before commands\n" );
|
|
goto help;
|
|
}
|
|
if( must_be_end )
|
|
{
|
|
fprintf( stderr, "Error: the command '%c' cannot be followed by other commands.\n", must_be_end );
|
|
return -1;
|
|
}
|
|
|
|
keep_going:
|
|
switch( argchar[1] )
|
|
{
|
|
default:
|
|
fprintf( stderr, "Error: Unknown command %c\n", argchar[1] );
|
|
case 'h':
|
|
goto help;
|
|
case '3':
|
|
if( MCF.Control3v3 )
|
|
MCF.Control3v3( dev, 1 );
|
|
else
|
|
goto unimplemented;
|
|
break;
|
|
case '5':
|
|
if( MCF.Control5v )
|
|
MCF.Control5v( dev, 1 );
|
|
else
|
|
goto unimplemented;
|
|
break;
|
|
case 't':
|
|
if( MCF.Control3v3 )
|
|
MCF.Control3v3( dev, 0 );
|
|
else
|
|
goto unimplemented;
|
|
break;
|
|
case 'f':
|
|
if( MCF.Control5v )
|
|
MCF.Control5v( dev, 0 );
|
|
else
|
|
goto unimplemented;
|
|
break;
|
|
case 'C': // For specifying programmer
|
|
case 'c':
|
|
// COM port or programmer argument already parsed previously
|
|
// we still need to skip the next argument
|
|
iarg+=1;
|
|
if( iarg >= argc )
|
|
{
|
|
fprintf( stderr, "-c/C argument required 2 arguments\n" );
|
|
goto unimplemented;
|
|
}
|
|
break;
|
|
case 'u':
|
|
if( MCF.Unbrick )
|
|
MCF.Unbrick( dev );
|
|
else
|
|
goto unimplemented;
|
|
break;
|
|
case 'U':
|
|
// Unlock Bootloader
|
|
if( InternalUnlockBootloader( dev ) )
|
|
goto unimplemented;
|
|
break;
|
|
case 'b': //reBoot
|
|
if( !MCF.HaltMode || MCF.HaltMode( dev, HALT_MODE_REBOOT ) )
|
|
goto unimplemented;
|
|
break;
|
|
case 'B': //reBoot into Bootloader
|
|
if( !MCF.HaltMode || MCF.HaltMode( dev, HALT_MODE_GO_TO_BOOTLOADER ) )
|
|
goto unimplemented;
|
|
break;
|
|
case 'e': //rEsume
|
|
if( !MCF.HaltMode || MCF.HaltMode( dev, HALT_MODE_RESUME ) )
|
|
goto unimplemented;
|
|
break;
|
|
case 'E': //Erase whole chip.
|
|
if( MCF.HaltMode ) MCF.HaltMode( dev, HALT_MODE_HALT_AND_RESET );
|
|
if( !MCF.Erase || MCF.Erase( dev, 0, 0, 1 ) )
|
|
goto unimplemented;
|
|
break;
|
|
case 'a':
|
|
if( !MCF.HaltMode || MCF.HaltMode( dev, HALT_MODE_HALT_AND_RESET ) )
|
|
goto unimplemented;
|
|
break;
|
|
case 'A': // Halt without reboot
|
|
if( !MCF.HaltMode || MCF.HaltMode( dev, HALT_MODE_HALT_BUT_NO_RESET ) )
|
|
goto unimplemented;
|
|
break;
|
|
|
|
// disable NRST pin (turn it into a GPIO)
|
|
case 'd': // see "RSTMODE" in datasheet
|
|
if( MCF.HaltMode ) MCF.HaltMode( dev, HALT_MODE_HALT_AND_RESET );
|
|
if( MCF.ConfigureNRSTAsGPIO )
|
|
MCF.ConfigureNRSTAsGPIO( dev, 0 );
|
|
else
|
|
goto unimplemented;
|
|
break;
|
|
case 'D': // see "RSTMODE" in datasheet
|
|
if( MCF.HaltMode ) MCF.HaltMode( dev, HALT_MODE_HALT_AND_RESET );
|
|
if( MCF.ConfigureNRSTAsGPIO )
|
|
MCF.ConfigureNRSTAsGPIO( dev, 1 );
|
|
else
|
|
goto unimplemented;
|
|
break;
|
|
case 'p':
|
|
if( MCF.HaltMode ) MCF.HaltMode( dev, HALT_MODE_HALT_AND_RESET );
|
|
if( MCF.ConfigureReadProtection )
|
|
MCF.ConfigureReadProtection( dev, 0 );
|
|
else
|
|
goto unimplemented;
|
|
break;
|
|
case 'P':
|
|
if( MCF.HaltMode ) MCF.HaltMode( dev, HALT_MODE_HALT_AND_RESET );
|
|
if( MCF.ConfigureReadProtection )
|
|
MCF.ConfigureReadProtection( dev, 1 );
|
|
else
|
|
goto unimplemented;
|
|
break;
|
|
case 'G':
|
|
case 'T':
|
|
{
|
|
if( !MCF.PollTerminal )
|
|
goto unimplemented;
|
|
|
|
if( argchar[1] == 'G' && SetupGDBServer( dev ) )
|
|
{
|
|
fprintf( stderr, "Error: can't start GDB server\n" );
|
|
return -1;
|
|
}
|
|
if( argchar[1] == 'G' )
|
|
{
|
|
fprintf( stderr, "GDBServer Running\n" );
|
|
}
|
|
else if( argchar[1] == 'T' )
|
|
{
|
|
// In case we aren't running already.
|
|
MCF.HaltMode( dev, 2 );
|
|
}
|
|
|
|
CaptureKeyboardInput();
|
|
|
|
uint32_t appendword = 0;
|
|
do
|
|
{
|
|
uint8_t buffer[256];
|
|
if( !IsGDBServerInShadowHaltState( dev ) )
|
|
{
|
|
// Handle keyboard input.
|
|
if( appendword == 0 )
|
|
{
|
|
int i;
|
|
for( i = 0; i < 3; i++ )
|
|
{
|
|
if( !IsKBHit() ) break;
|
|
appendword |= ReadKBByte() << (i*8+8);
|
|
}
|
|
appendword |= i+4; // Will go into DATA0.
|
|
}
|
|
int r = MCF.PollTerminal( dev, buffer, sizeof( buffer ), appendword, 0 );
|
|
if( r == -1 )
|
|
{
|
|
// Other end ack'd without printf.
|
|
appendword = 0;
|
|
}
|
|
else if( r < 0 )
|
|
{
|
|
fprintf( stderr, "Terminal dead. code %d\n", r );
|
|
return -32;
|
|
}
|
|
else if( r > 0 )
|
|
{
|
|
fwrite( buffer, r, 1, stdout );
|
|
fflush( stdout );
|
|
// Otherwise it's basically just an ack for appendword.
|
|
appendword = 0;
|
|
}
|
|
}
|
|
|
|
if( argchar[1] == 'G' )
|
|
{
|
|
PollGDBServer( dev );
|
|
}
|
|
} while( 1 );
|
|
|
|
// Currently unreachable - consider reachable-ing
|
|
if( argchar[1] == 'G' )
|
|
ExitGDBServer( dev );
|
|
break;
|
|
}
|
|
case 's':
|
|
{
|
|
iarg+=2;
|
|
if( iarg >= argc )
|
|
{
|
|
fprintf( stderr, "Debug set commands require 2 parameters, a register and a value.\n" );
|
|
goto unimplemented;
|
|
}
|
|
|
|
uint32_t datareg = SimpleReadNumberInt( argv[iarg-1], DMDATA0 );
|
|
uint32_t value = SimpleReadNumberInt( argv[iarg], 0 );
|
|
|
|
if( MCF.WriteReg32 && MCF.FlushLLCommands )
|
|
{
|
|
MCF.FlushLLCommands( dev );
|
|
MCF.WriteReg32( dev, datareg, value );
|
|
MCF.FlushLLCommands( dev );
|
|
}
|
|
else
|
|
goto unimplemented;
|
|
break;
|
|
}
|
|
case 'm':
|
|
{
|
|
iarg+=1;
|
|
if( iarg >= argc )
|
|
{
|
|
fprintf( stderr, "Debug get commands require 1 parameter, a register.\n" );
|
|
fprintf( stderr, "One of the following:\n"
|
|
" DMDATA0 0x04\n"
|
|
" DMDATA1 0x05\n"
|
|
" DMCONTROL 0x10\n"
|
|
" DMSTATUS 0x11\n"
|
|
" DMHARTINFO 0x12\n"
|
|
" DMABSTRACTCS 0x16\n"
|
|
" DMCOMMAND 0x17\n"
|
|
" DMABSTRACTAUTO 0x18\n"
|
|
" DMPROGBUF0 0x20\n"
|
|
" DMPROGBUF1 0x21\n"
|
|
" DMPROGBUF2 0x22\n"
|
|
" DMPROGBUF3 0x23\n"
|
|
" DMPROGBUF4 0x24\n"
|
|
" DMPROGBUF5 0x25\n"
|
|
" DMPROGBUF6 0x26\n"
|
|
" DMPROGBUF7 0x27\n"
|
|
" DMCPBR 0x7C\n"
|
|
" DMCFGR 0x7D\n"
|
|
" DMSHDWCFGR 0x7E\n" );
|
|
|
|
goto unimplemented;
|
|
}
|
|
|
|
uint32_t datareg = SimpleReadNumberInt( argv[iarg], DMDATA0 );
|
|
|
|
if( MCF.ReadReg32 && MCF.FlushLLCommands )
|
|
{
|
|
uint32_t value;
|
|
int ret = MCF.ReadReg32( dev, datareg, &value );
|
|
printf( "REGISTER %02x: %08x, %d\n", datareg, value, ret );
|
|
}
|
|
else
|
|
goto unimplemented;
|
|
break;
|
|
}
|
|
case 'i':
|
|
{
|
|
if( MCF.PrintChipInfo )
|
|
MCF.PrintChipInfo( dev );
|
|
else
|
|
goto unimplemented;
|
|
break;
|
|
}
|
|
case 'X':
|
|
{
|
|
iarg++;
|
|
if( iarg >= argc )
|
|
{
|
|
fprintf( stderr, "Vendor command requires an actual command\n" );
|
|
goto unimplemented;
|
|
}
|
|
if( MCF.VendorCommand )
|
|
if( MCF.VendorCommand( dev, argv[iarg++] ) )
|
|
goto unimplemented;
|
|
break;
|
|
}
|
|
case 'r':
|
|
{
|
|
if( MCF.HaltMode ) MCF.HaltMode( dev, HALT_MODE_HALT_BUT_NO_RESET ); //No need to reboot.
|
|
|
|
if( argchar[2] != 0 )
|
|
{
|
|
fprintf( stderr, "Error: can't have char after paramter field\n" );
|
|
goto help;
|
|
}
|
|
iarg++;
|
|
argchar = 0; // Stop advancing
|
|
if( iarg + 2 >= argc )
|
|
{
|
|
fprintf( stderr, "Error: missing file for -o.\n" );
|
|
goto help;
|
|
}
|
|
const char * fname = argv[iarg++];
|
|
uint64_t offset = StringToMemoryAddress( argv[iarg++] );
|
|
|
|
uint64_t amount = SimpleReadNumberInt( argv[iarg], -1 );
|
|
if( offset > 0xffffffff || amount > 0xffffffff )
|
|
{
|
|
fprintf( stderr, "Error: memory value request out of range\n" );
|
|
return -9;
|
|
}
|
|
|
|
FILE * f = 0;
|
|
int hex = 0;
|
|
if( strcmp( fname, "-" ) == 0 )
|
|
f = stdout;
|
|
else if( strcmp( fname, "+" ) == 0 )
|
|
f = stdout, hex = 1;
|
|
else
|
|
f = fopen( fname, "wb" );
|
|
if( !f )
|
|
{
|
|
fprintf( stderr, "Error: can't open write file \"%s\"\n", fname );
|
|
return -9;
|
|
}
|
|
uint8_t * readbuff = malloc( amount );
|
|
|
|
if( MCF.ReadBinaryBlob )
|
|
{
|
|
if( MCF.ReadBinaryBlob( dev, offset, amount, readbuff ) < 0 )
|
|
{
|
|
fprintf( stderr, "Fault reading device\n" );
|
|
return -12;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
goto unimplemented;
|
|
}
|
|
|
|
printf( "Read %d bytes\n", (int)amount );
|
|
|
|
if( hex )
|
|
{
|
|
int i;
|
|
for( i = 0; i < amount; i++ )
|
|
{
|
|
if( ( i & 0xf ) == 0 )
|
|
{
|
|
if( i != 0 ) printf( "\n" );
|
|
printf( "%08x: ", (uint32_t)(offset + i) );
|
|
}
|
|
printf( "%02x ", readbuff[i] );
|
|
}
|
|
printf( "\n" );
|
|
}
|
|
else
|
|
fwrite( readbuff, amount, 1, f );
|
|
|
|
free( readbuff );
|
|
|
|
if( f != stdout ) fclose( f );
|
|
break;
|
|
}
|
|
case 'w':
|
|
{
|
|
struct InternalState * iss = (struct InternalState*)(((struct ProgrammerStructBase*)dev)->internal);
|
|
if( argchar[2] != 0 ) goto help;
|
|
iarg++;
|
|
argchar = 0; // Stop advancing
|
|
if( iarg + 1 >= argc ) goto help;
|
|
|
|
// Write binary.
|
|
int len = 0;
|
|
uint8_t * image = 0;
|
|
const char * fname = argv[iarg++];
|
|
|
|
if( fname[0] == '-' )
|
|
{
|
|
len = strlen( fname + 1 );
|
|
image = (uint8_t*)strdup( fname + 1 );
|
|
status = 1;
|
|
}
|
|
else if( fname[0] == '+' )
|
|
{
|
|
int hl = strlen( fname+1 );
|
|
if( hl & 1 )
|
|
{
|
|
fprintf( stderr, "Error: hex input doesn't align to chars correctly.\n" );
|
|
return -32;
|
|
}
|
|
len = hl/2;
|
|
image = malloc( len );
|
|
int i;
|
|
for( i = 0; i < len; i ++ )
|
|
{
|
|
char c1 = fname[i*2+1];
|
|
char c2 = fname[i*2+2];
|
|
int v1, v2;
|
|
if( c1 >= '0' && c1 <= '9' ) v1 = c1 - '0';
|
|
else if( c1 >= 'a' && c1 <= 'f' ) v1 = c1 - 'a' + 10;
|
|
else if( c1 >= 'A' && c1 <= 'F' ) v1 = c1 - 'A' + 10;
|
|
else
|
|
{
|
|
fprintf( stderr, "Error: Bad hex\n" );
|
|
return -32;
|
|
}
|
|
|
|
if( c2 >= '0' && c2 <= '9' ) v2 = c2 - '0';
|
|
else if( c2 >= 'a' && c2 <= 'f' ) v2 = c2 - 'a' + 10;
|
|
else if( c2 >= 'A' && c2 <= 'F' ) v2 = c2 - 'A' + 10;
|
|
else
|
|
{
|
|
fprintf( stderr, "Error: Bad hex\n" );
|
|
return -32;
|
|
}
|
|
image[i] = (v1<<4) | v2;
|
|
}
|
|
status = 1;
|
|
}
|
|
else
|
|
{
|
|
FILE * f = fopen( fname, "rb" );
|
|
if( !f )
|
|
{
|
|
fprintf( stderr, "Error: Could not open %s\n", fname );
|
|
return -55;
|
|
}
|
|
fseek( f, 0, SEEK_END );
|
|
len = ftell( f );
|
|
fseek( f, 0, SEEK_SET );
|
|
image = malloc( len );
|
|
status = fread( image, len, 1, f );
|
|
fclose( f );
|
|
}
|
|
|
|
uint64_t offset = StringToMemoryAddress( argv[iarg] );
|
|
if( offset > 0xffffffff )
|
|
{
|
|
fprintf( stderr, "Error: Invalid offset (%s)\n", argv[iarg] );
|
|
exit( -44 );
|
|
}
|
|
if( status != 1 )
|
|
{
|
|
fprintf( stderr, "Error: File I/O Fault.\n" );
|
|
exit( -10 );
|
|
}
|
|
if( len > iss->flash_size )
|
|
{
|
|
fprintf( stderr, "Error: Image for CH32V003 too large (%d)\n", len );
|
|
exit( -9 );
|
|
}
|
|
|
|
|
|
int is_flash = IsAddressFlash( offset );
|
|
//if( MCF.HaltMode ) MCF.HaltMode( dev, is_flash ? HALT_MODE_HALT_AND_RESET : HALT_MODE_HALT_BUT_NO_RESET );
|
|
if( MCF.HaltMode && is_flash )
|
|
{
|
|
if ( offset == 0x1ffff000 ) MCF.HaltMode( dev, HALT_MODE_HALT_BUT_NO_RESET ); // do not reset if writing bootloader, even if it is considered flash memory
|
|
else MCF.HaltMode( dev, HALT_MODE_HALT_AND_RESET );
|
|
}
|
|
|
|
if( MCF.WriteBinaryBlob )
|
|
{
|
|
if( MCF.WriteBinaryBlob( dev, offset, len, image ) )
|
|
{
|
|
fprintf( stderr, "Error: Fault writing image.\n" );
|
|
return -13;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
goto unimplemented;
|
|
}
|
|
|
|
printf( "Image written.\n" );
|
|
|
|
free( image );
|
|
break;
|
|
}
|
|
|
|
}
|
|
if( argchar && argchar[2] != 0 ) { argchar++; goto keep_going; }
|
|
}
|
|
|
|
if( MCF.FlushLLCommands )
|
|
MCF.FlushLLCommands( dev );
|
|
|
|
if( MCF.Exit )
|
|
MCF.Exit( dev );
|
|
|
|
return 0;
|
|
|
|
help:
|
|
fprintf( stderr, "Usage: minichlink [args]\n" );
|
|
fprintf( stderr, " single-letter args may be combined, i.e. -3r\n" );
|
|
fprintf( stderr, " multi-part args cannot.\n" );
|
|
fprintf( stderr, " -3 Enable 3.3V\n" );
|
|
fprintf( stderr, " -5 Enable 5V\n" );
|
|
fprintf( stderr, " -t Disable 3.3V\n" );
|
|
fprintf( stderr, " -f Disable 5V\n" );
|
|
fprintf( stderr, " -c [serial port for Ardulink, try /dev/ttyACM0 or COM11 etc]\n" );
|
|
fprintf( stderr, " -C [specified programmer, eg. b003boot, ardulink, esp32s2chfun]\n" );
|
|
fprintf( stderr, " -u Clear all code flash - by power off (also can unbrick)\n" );
|
|
fprintf( stderr, " -E Erase chip\n" );
|
|
fprintf( stderr, " -b Reboot out of Halt\n" );
|
|
fprintf( stderr, " -e Resume from halt\n" );
|
|
fprintf( stderr, " -a Reboot into Halt\n" );
|
|
fprintf( stderr, " -A Go into Halt without reboot\n" );
|
|
fprintf( stderr, " -D Configure NRST as GPIO\n" );
|
|
fprintf( stderr, " -d Configure NRST as NRST\n" );
|
|
fprintf( stderr, " -i Show chip info\n" );
|
|
fprintf( stderr, " -s [debug register] [value]\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\n" );
|
|
fprintf( stderr, " -p Disable Read Protection\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 (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;
|
|
|
|
unimplemented:
|
|
fprintf( stderr, "Error: Command '%s' unimplemented on this programmer.\n", lastcommand );
|
|
return -1;
|
|
}
|
|
#endif
|
|
|
|
#if defined(WINDOWS) || defined(WIN32) || defined(_WIN32)
|
|
#define strtoll _strtoi64
|
|
#endif
|
|
|
|
int64_t SimpleReadNumberInt( const char * number, int64_t defaultNumber )
|
|
{
|
|
if( !number || !number[0] ) return defaultNumber;
|
|
int radix = 10;
|
|
if( number[0] == '0' )
|
|
{
|
|
char nc = number[1];
|
|
number+=2;
|
|
if( nc == 0 ) return 0;
|
|
else if( nc == 'x' ) radix = 16;
|
|
else if( nc == 'b' ) radix = 2;
|
|
else { number--; radix = 8; }
|
|
}
|
|
char * endptr;
|
|
uint64_t ret = strtoll( number, &endptr, radix );
|
|
if( endptr == number )
|
|
{
|
|
return defaultNumber;
|
|
}
|
|
else
|
|
{
|
|
return ret;
|
|
}
|
|
}
|
|
|
|
static int64_t StringToMemoryAddress( const char * number )
|
|
{
|
|
uint32_t base = 0;
|
|
|
|
if( strncmp( number, "flash", 5 ) == 0 ) base = 0x08000000, number += 5;
|
|
if( strncmp( number, "launcher", 8 ) == 0 ) base = 0x1FFFF000, number += 8;
|
|
if( strncmp( number, "bootloader", 10 ) == 0 ) base = 0x1FFFF000, number += 10;
|
|
if( strncmp( number, "option", 6 ) == 0 ) base = 0x1FFFF800, number += 6;
|
|
if( strncmp( number, "user", 4 ) == 0 ) base = 0x1FFFF800, number += 4;
|
|
if( strncmp( number, "ram", 3 ) == 0 ) base = 0x20000000, number += 3;
|
|
|
|
if( base )
|
|
{
|
|
if( *number != '+' )
|
|
return base;
|
|
number++;
|
|
return base + SimpleReadNumberInt( number, 0 );
|
|
}
|
|
return SimpleReadNumberInt( number, -1 );
|
|
}
|
|
|
|
static int DefaultWaitForFlash( void * dev )
|
|
{
|
|
uint32_t rw, timeout = 0;
|
|
do
|
|
{
|
|
rw = 0;
|
|
MCF.ReadWord( dev, (intptr_t)&FLASH->STATR, &rw ); // FLASH_STATR => 0x4002200C
|
|
if( timeout++ > 100 ) return -1;
|
|
} while(rw & 3); // BSY flag for 003, or WRBSY for other processors.
|
|
|
|
if( rw & FLASH_STATR_WRPRTERR )
|
|
{
|
|
fprintf( stderr, "Memory Protection Error\n" );
|
|
return -44;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int DefaultWaitForDoneOp( void * dev, int ignore )
|
|
{
|
|
int r;
|
|
uint32_t rrv;
|
|
|
|
do
|
|
{
|
|
r = MCF.ReadReg32( dev, DMABSTRACTCS, &rrv );
|
|
if( r ) return r;
|
|
}
|
|
while( rrv & (1<<12) );
|
|
|
|
if( (rrv >> 8 ) & 7 )
|
|
{
|
|
if( !ignore )
|
|
{
|
|
const char * errortext = 0;
|
|
switch( (rrv>>8)&7 )
|
|
{
|
|
case 1: errortext = "Command in execution"; break;
|
|
case 2: errortext = "Abstract Command Unsupported"; break;
|
|
case 3: errortext = "Execption executing Abstract Command"; break;
|
|
case 4: errortext = "Processor not halted."; break;
|
|
case 5: errortext = "Bus Error"; break;
|
|
case 6: errortext = "Parity Bit"; break;
|
|
default: errortext = "Other Error"; break;
|
|
}
|
|
|
|
uint32_t temp;
|
|
MCF.ReadReg32( dev, DMSTATUS, &temp );
|
|
fprintf( stderr, "Fault writing memory (DMABSTRACTS = %08x) (%s) DMSTATUS: %08x\n", rrv, errortext, temp );
|
|
}
|
|
MCF.WriteReg32( dev, DMABSTRACTCS, 0x00000700 );
|
|
return -9;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int DefaultSetupInterface( void * dev )
|
|
{
|
|
struct InternalState * iss = (struct InternalState*)(((struct ProgrammerStructBase*)dev)->internal);
|
|
|
|
if( MCF.Control3v3 ) MCF.Control3v3( dev, 1 );
|
|
MCF.DelayUS( dev, 16000 );
|
|
MCF.WriteReg32( dev, DMSHDWCFGR, 0x5aa50000 | (1<<10) ); // Shadow Config Reg
|
|
MCF.WriteReg32( dev, DMCFGR, 0x5aa50000 | (1<<10) ); // CFGR (1<<10 == Allow output from slave)
|
|
MCF.WriteReg32( dev, DMCFGR, 0x5aa50000 | (1<<10) ); // Bug in silicon? If coming out of cold boot, and we don't do our little "song and dance" this has to be called.
|
|
|
|
// Read back chip status. This is really basic.
|
|
uint32_t reg = 0;
|
|
int r = MCF.ReadReg32( dev, DMSTATUS, ® );
|
|
if( r >= 0 )
|
|
{
|
|
// Valid R.
|
|
if( reg == 0x00000000 || reg == 0xffffffff )
|
|
{
|
|
fprintf( stderr, "Error: Setup chip failed. Got code %08x\n", reg );
|
|
return -9;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
fprintf( stderr, "Error: Could not read chip code.\n" );
|
|
return r;
|
|
}
|
|
|
|
iss->statetag = STTAG( "STRT" );
|
|
return 0;
|
|
}
|
|
|
|
static void StaticUpdatePROGBUFRegs( void * dev )
|
|
{
|
|
uint32_t rr;
|
|
if( MCF.ReadReg32( dev, DMHARTINFO, &rr ) )
|
|
{
|
|
fprintf( stderr, "Error: Could not get hart info.\n" );
|
|
return;
|
|
}
|
|
uint32_t data0offset = 0xe0000000 | ( rr & 0x7ff );
|
|
MCF.WriteReg32( dev, DMDATA0, data0offset ); // DATA0's location in memory.
|
|
MCF.WriteReg32( dev, DMCOMMAND, 0x0023100a ); // Copy data to x10
|
|
MCF.WriteReg32( dev, DMDATA0, data0offset + 4 ); // DATA1's location in memory.
|
|
MCF.WriteReg32( dev, DMCOMMAND, 0x0023100b ); // Copy data to x11
|
|
MCF.WriteReg32( dev, DMDATA0, 0x40022010 ); // FLASH->CTLR
|
|
MCF.WriteReg32( dev, DMCOMMAND, 0x0023100c ); // Copy data to x12
|
|
MCF.WriteReg32( dev, DMDATA0, CR_PAGE_PG|CR_BUF_LOAD);
|
|
MCF.WriteReg32( dev, DMCOMMAND, 0x0023100d ); // Copy data to x13
|
|
}
|
|
|
|
int InternalUnlockBootloader( void * dev )
|
|
{
|
|
if( !MCF.WriteWord ) return -99;
|
|
int ret = 0;
|
|
uint32_t OBTKEYR;
|
|
ret |= MCF.WriteWord( dev, 0x40022028, 0x45670123 ); //(FLASH_BOOT_MODEKEYP)
|
|
ret |= MCF.WriteWord( dev, 0x40022028, 0xCDEF89AB ); //(FLASH_BOOT_MODEKEYP)
|
|
ret |= MCF.ReadWord( dev, 0x40022008, &OBTKEYR ); //(FLASH_OBTKEYR)
|
|
if( ret )
|
|
{
|
|
fprintf( stderr, "Error operating with OBTKEYR\n" );
|
|
return -1;
|
|
}
|
|
if( OBTKEYR & (1<<15) )
|
|
{
|
|
fprintf( stderr, "Error: Could not unlock boot section (%08x)\n", OBTKEYR );
|
|
}
|
|
OBTKEYR |= (1<<14); // Configure for boot-to-bootload.
|
|
ret |= MCF.WriteWord( dev, 0x40022008, OBTKEYR );
|
|
ret |= MCF.ReadWord( dev, 0x40022008, &OBTKEYR ); //(FLASH_OBTKEYR)
|
|
printf( "FLASH_OBTKEYR = %08x (%d)\n", OBTKEYR, ret );
|
|
return ret;
|
|
}
|
|
|
|
|
|
int InternalIsMemoryErased( struct InternalState * iss, uint32_t address )
|
|
{
|
|
if(( address & 0xff000000 ) != 0x08000000 ) return 0;
|
|
int sector = (address & 0xffffff) / iss->sector_size;
|
|
if( sector >= MAX_FLASH_SECTORS )
|
|
return 0;
|
|
else
|
|
return iss->flash_sector_status[sector];
|
|
}
|
|
|
|
void InternalMarkMemoryNotErased( struct InternalState * iss, uint32_t address )
|
|
{
|
|
if(( address & 0xff000000 ) != 0x08000000 ) return;
|
|
int sector = (address & 0xffffff) / iss->sector_size;
|
|
if( sector < MAX_FLASH_SECTORS )
|
|
iss->flash_sector_status[sector] = 0;
|
|
}
|
|
|
|
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);
|
|
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.
|
|
// sh x8,0(x9) // Write to the address.
|
|
MCF.WriteReg32( dev, DMPROGBUF0, 0x00849023 );
|
|
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, 0 );
|
|
iss->currentstateval = -1;
|
|
|
|
if( ret ) fprintf( stderr, "Fault on DefaultWriteHalfWord\n" );
|
|
return ret;
|
|
}
|
|
|
|
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);
|
|
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.
|
|
// lh x8,0(x9) // Write to the address.
|
|
MCF.WriteReg32( dev, DMPROGBUF0, 0x00049403 ); // lh 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, 0 );
|
|
iss->currentstateval = -1;
|
|
|
|
if( ret ) fprintf( stderr, "Fault on DefaultReadHalfWord\n" );
|
|
|
|
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.
|
|
|
|
// 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, 0 );
|
|
if( ret ) fprintf( stderr, "Fault on DefaultWriteByte\n" );
|
|
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, 0 );
|
|
if( ret ) fprintf( stderr, "Fault on DefaultReadByte\n" );
|
|
iss->currentstateval = -1;
|
|
|
|
uint32_t rr;
|
|
ret |= MCF.ReadReg32( dev, DMDATA0, &rr );
|
|
*data = rr;
|
|
return ret;
|
|
}
|
|
|
|
|
|
static int DefaultWriteWord( void * dev, uint32_t address_to_write, uint32_t data )
|
|
{
|
|
struct InternalState * iss = (struct InternalState*)(((struct ProgrammerStructBase*)dev)->internal);
|
|
int ret = 0;
|
|
|
|
int is_flash = IsAddressFlash( address_to_write );
|
|
|
|
if( iss->statetag != STTAG( "WRSQ" ) || is_flash != iss->lastwriteflags )
|
|
{
|
|
int did_disable_req = 0;
|
|
if( iss->statetag != STTAG( "WRSQ" ) )
|
|
{
|
|
MCF.WriteReg32( dev, DMABSTRACTAUTO, 0x00000000 ); // Disable Autoexec.
|
|
did_disable_req = 1;
|
|
// Different address, so we don't need to re-write all the program regs.
|
|
// c.lw x9,0(x11) // Get the address to write to.
|
|
// c.sw x8,0(x9) // Write to the address.
|
|
MCF.WriteReg32( dev, DMPROGBUF0, 0xc0804184 );
|
|
// c.addi x9, 4
|
|
// c.sw x9,0(x11)
|
|
MCF.WriteReg32( dev, DMPROGBUF1, 0xc1840491 );
|
|
|
|
if( iss->statetag != STTAG( "RDSQ" ) )
|
|
{
|
|
StaticUpdatePROGBUFRegs( dev );
|
|
}
|
|
}
|
|
|
|
if( iss->lastwriteflags != is_flash || iss->statetag != STTAG( "WRSQ" ) )
|
|
{
|
|
// If we are doing flash, we have to ack, otherwise we don't want to ack.
|
|
if( is_flash )
|
|
{
|
|
// After writing to memory, also hit up page load flag.
|
|
// c.sw x13,0(x12) // Acknowledge the page write.
|
|
// c.ebreak
|
|
MCF.WriteReg32( dev, DMPROGBUF2, 0x9002c214 );
|
|
}
|
|
else
|
|
{
|
|
MCF.WriteReg32( dev, DMPROGBUF2, 0x00019002 ); // c.ebreak
|
|
}
|
|
}
|
|
|
|
MCF.WriteReg32( dev, DMDATA1, address_to_write );
|
|
MCF.WriteReg32( dev, DMDATA0, data );
|
|
|
|
if( did_disable_req )
|
|
{
|
|
MCF.WriteReg32( dev, DMCOMMAND, 0x00271008 ); // Copy data to x8, and execute program.
|
|
MCF.WriteReg32( dev, DMABSTRACTAUTO, 1 ); // Enable Autoexec.
|
|
}
|
|
iss->lastwriteflags = is_flash;
|
|
|
|
iss->statetag = STTAG( "WRSQ" );
|
|
iss->currentstateval = address_to_write;
|
|
|
|
if( is_flash )
|
|
{
|
|
ret |= MCF.WaitForDoneOp( dev, 0 );
|
|
if( ret ) fprintf( stderr, "Fault on DefaultWriteWord Part 1\n" );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if( address_to_write != iss->currentstateval )
|
|
{
|
|
MCF.WriteReg32( dev, DMABSTRACTAUTO, 0 ); // Disable Autoexec.
|
|
MCF.WriteReg32( dev, DMDATA1, address_to_write );
|
|
MCF.WriteReg32( dev, DMABSTRACTAUTO, 1 ); // Enable Autoexec.
|
|
}
|
|
MCF.WriteReg32( dev, DMDATA0, data );
|
|
if( is_flash )
|
|
{
|
|
// XXX TODO: This likely can be a very short delay.
|
|
// XXX POSSIBLE OPTIMIZATION REINVESTIGATE.
|
|
ret |= MCF.WaitForDoneOp( dev, 0 );
|
|
if( ret ) fprintf( stderr, "Fault on DefaultWriteWord Part 2\n" );
|
|
}
|
|
else
|
|
{
|
|
ret |= MCF.WaitForDoneOp( dev, 0 );
|
|
if( ret ) fprintf( stderr, "Fault on DefaultWriteWord Part 3\n" );
|
|
}
|
|
}
|
|
|
|
|
|
iss->currentstateval += 4;
|
|
|
|
return ret;
|
|
}
|
|
|
|
int DefaultWriteBinaryBlob( void * dev, uint32_t address_to_write, uint32_t blob_size, uint8_t * blob )
|
|
{
|
|
// NOTE IF YOU FIX SOMETHING IN THIS FUNCTION PLEASE ALSO UPDATE THE PROGRAMMERS.
|
|
// this is only fallback functionality for really realy basic programmers.
|
|
|
|
uint32_t rw;
|
|
struct InternalState * iss = (struct InternalState*)(((struct ProgrammerStructBase*)dev)->internal);
|
|
int sectorsize = iss->sector_size;
|
|
|
|
// We can't write into flash when mapped to 0x00000000
|
|
if( address_to_write < 0x01000000 )
|
|
address_to_write |= 0x08000000;
|
|
|
|
int is_flash = IsAddressFlash( address_to_write );
|
|
|
|
if( blob_size == 0 ) return 0;
|
|
|
|
if( is_flash && !iss->flash_unlocked )
|
|
{
|
|
if( ( rw = InternalUnlockFlash( dev, iss ) ) )
|
|
return rw;
|
|
}
|
|
|
|
// Special: For user data, need to write to it very carefully.
|
|
if( address_to_write > 0x1ffff7c0 && address_to_write < 0x20000000 )
|
|
{
|
|
if( !MCF.WriteHalfWord )
|
|
{
|
|
fprintf( stderr, "Error: to write this type of memory, half-word-writing is required\n" );
|
|
return -5;
|
|
}
|
|
|
|
uint32_t base = address_to_write & 0xffffffc0;
|
|
uint8_t block[64];
|
|
|
|
if( base != ((address_to_write+blob_size-1) & 0xffffffc0) )
|
|
{
|
|
fprintf( stderr, "Error: You cannot write across a 64-byte boundary when writing to option bytes\n" );
|
|
return -9;
|
|
}
|
|
|
|
MCF.ReadBinaryBlob( dev, base, 64, block );
|
|
|
|
uint32_t offset = address_to_write - base;
|
|
memcpy( block + offset, blob, blob_size );
|
|
|
|
uint32_t temp;
|
|
MCF.ReadWord( dev, 0x4002200c, &temp );
|
|
if( temp & 0x8000 )
|
|
{
|
|
MCF.WriteWord( dev, 0x40022004, 0x45670123 ); // KEYR
|
|
MCF.WriteWord( dev, 0x40022004, 0xCDEF89AB );
|
|
MCF.WriteWord( dev, 0x40022008, 0x45670123 ); // OBWRE
|
|
MCF.WriteWord( dev, 0x40022008, 0xCDEF89AB );
|
|
MCF.WriteWord( dev, 0x40022028, 0x45670123 ); //(FLASH_BOOT_MODEKEYP)
|
|
MCF.WriteWord( dev, 0x40022028, 0xCDEF89AB ); //(FLASH_BOOT_MODEKEYP)
|
|
MCF.ReadWord( dev, 0x40022010, &temp );
|
|
MCF.ReadWord( dev, 0x4002200c, &temp );
|
|
}
|
|
|
|
MCF.ReadWord( dev, 0x4002200c, &temp );
|
|
if( temp & 0x8000 )
|
|
{
|
|
fprintf( stderr, "Error: Critical memory zone is still locked out\n" );
|
|
return -10;
|
|
}
|
|
|
|
if( MCF.WaitForFlash ) MCF.WaitForFlash( dev );
|
|
|
|
MCF.ReadWord( dev, 0x40022010, &temp );
|
|
if( !(temp & (1<<9)) ) // Check OBWRE
|
|
{
|
|
fprintf( stderr, "Error: Option Byte Unlock Failed\n" );
|
|
return -10;
|
|
}
|
|
|
|
// Perform erase.
|
|
MCF.WriteWord( dev, 0x40022010, FLASH_CTLR_OPTER | FLASH_CTLR_OPTWRE );
|
|
MCF.WriteWord( dev, 0x40022010, FLASH_CTLR_OPTER | FLASH_CTLR_OPTWRE | FLASH_CTLR_STRT );
|
|
|
|
if( MCF.WaitForFlash ) MCF.WaitForFlash( dev );
|
|
|
|
MCF.ReadWord( dev, 0x4002200c, &temp );
|
|
if( temp & 0x10 )
|
|
{
|
|
fprintf( stderr, "WRPTRERR is set. Write failed\n" );
|
|
return -9;
|
|
}
|
|
|
|
int i;
|
|
for( i = 0; i < 8; i++ )
|
|
{
|
|
MCF.WriteWord( dev, 0x40022010, FLASH_CTLR_OPTPG | FLASH_CTLR_OPTWRE );
|
|
MCF.WriteWord( dev, 0x40022010, FLASH_CTLR_OPTPG | FLASH_CTLR_STRT | FLASH_CTLR_OPTWRE );
|
|
MCF.WriteHalfWord( dev, i*2+base, block[i*2+0] | (block[i*2+1]<<8) );
|
|
|
|
if( MCF.WaitForFlash ) MCF.WaitForFlash( dev );
|
|
MCF.ReadWord( dev, 0x4002200c, &temp );
|
|
if( temp & 0x10 )
|
|
{
|
|
fprintf( stderr, "WRPTRERR is set. Write failed\n" );
|
|
return -9;
|
|
}
|
|
}
|
|
if( MCF.WaitForFlash ) MCF.WaitForFlash( dev );
|
|
MCF.WriteWord( dev, 0x40022010, 0 );
|
|
|
|
return 0;
|
|
}
|
|
|
|
// Regardless of sector size, allow block write to do its thing if it can.
|
|
if( is_flash && MCF.BlockWrite64 && ( address_to_write & 0x3f ) == 0 && ( blob_size & 0x3f ) == 0 )
|
|
{
|
|
int i;
|
|
for( i = 0; i < blob_size; i+= 64 )
|
|
{
|
|
int r = MCF.BlockWrite64( dev, address_to_write + i, blob + i );
|
|
if( r )
|
|
{
|
|
fprintf( stderr, "Error writing block at memory %08x / Error: %d\n", address_to_write, r );
|
|
return r;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
uint8_t tempblock[sectorsize];
|
|
int sblock = address_to_write / sectorsize;
|
|
int eblock = ( address_to_write + blob_size + (sectorsize-1) ) / sectorsize;
|
|
int b;
|
|
int rsofar = 0;
|
|
|
|
for( b = sblock; b < eblock; b++ )
|
|
{
|
|
int offset_in_block = address_to_write - (b * sectorsize);
|
|
if( offset_in_block < 0 ) offset_in_block = 0;
|
|
int end_o_plus_one_in_block = ( address_to_write + blob_size ) - (b*sectorsize);
|
|
if( end_o_plus_one_in_block > sectorsize ) end_o_plus_one_in_block = sectorsize;
|
|
int base = b * sectorsize;
|
|
|
|
if( offset_in_block == 0 && end_o_plus_one_in_block == sectorsize )
|
|
{
|
|
if( MCF.BlockWrite64 )
|
|
{
|
|
int i;
|
|
for( i = 0; i < sectorsize/64; i++ )
|
|
{
|
|
int r = MCF.BlockWrite64( dev, base + i*64, blob + rsofar+i*64 );
|
|
rsofar += 64;
|
|
if( r )
|
|
{
|
|
fprintf( stderr, "Error writing block at memory %08x (error = %d)\n", base, r );
|
|
return r;
|
|
}
|
|
}
|
|
}
|
|
else // Block Write not avaialble
|
|
{
|
|
if( is_flash )
|
|
{
|
|
if( !InternalIsMemoryErased( iss, base ) )
|
|
MCF.Erase( dev, base, sectorsize, 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 < sectorsize/4; j++ )
|
|
{
|
|
uint32_t writeword;
|
|
memcpy( &writeword, blob + rsofar, 4 );
|
|
MCF.WriteWord( dev, j*4+base, writeword );
|
|
rsofar += 4;
|
|
}
|
|
|
|
if( is_flash )
|
|
{
|
|
MCF.WriteWord( dev, 0x40022014, base ); //0x40022014 -> FLASH->ADDR
|
|
if( MCF.PrepForLongOp ) MCF.PrepForLongOp( dev ); // Give the programmer a headsup this next operation could take a while.
|
|
MCF.WriteWord( dev, 0x40022010, CR_PAGE_PG|CR_STRT_Set ); // 0x40022010 -> FLASH->CTLR
|
|
if( MCF.WaitForFlash ) MCF.WaitForFlash( dev );
|
|
InternalMarkMemoryNotErased( iss, base );
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//Ok, we have to do something wacky.
|
|
if( is_flash )
|
|
{
|
|
MCF.ReadBinaryBlob( dev, base, sectorsize, tempblock );
|
|
|
|
// Permute tempblock
|
|
int tocopy = end_o_plus_one_in_block - offset_in_block;
|
|
memcpy( tempblock + offset_in_block, blob + rsofar, tocopy );
|
|
rsofar += tocopy;
|
|
|
|
if( MCF.BlockWrite64 )
|
|
{
|
|
int i;
|
|
for( i = 0; i < sectorsize/64; i++ )
|
|
{
|
|
int r = MCF.BlockWrite64( dev, base+i*64, tempblock+i*64 );
|
|
if( r ) return r;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if( !InternalIsMemoryErased( iss, base ) )
|
|
MCF.Erase( dev, base, sectorsize, 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 < sectorsize/4; 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
|
|
InternalMarkMemoryNotErased( iss, base );
|
|
}
|
|
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 < sectorsize; 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;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
MCF.FlushLLCommands( dev );
|
|
|
|
#if 0
|
|
{
|
|
uint8_t scratch[blob_size];
|
|
int rrr = DefaultReadBinaryBlob( dev, address_to_write, blob_size, scratch );
|
|
int i;
|
|
printf( "Read op: %d\n", rrr );
|
|
for( i = 0; i < blob_size; i++ )
|
|
{
|
|
if( scratch[i] != blob[i] )
|
|
{
|
|
printf( "DISAGREE: %04x\n", i );
|
|
i = (i & ~0x3f) + 0x40-1;
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
|
|
MCF.DelayUS( dev, 100 ); // Why do we need this? (We seem to need this on the WCH programmers?)
|
|
return 0;
|
|
timedout:
|
|
fprintf( stderr, "Timed out\n" );
|
|
return -5;
|
|
}
|
|
|
|
static int DefaultReadWord( void * dev, uint32_t address_to_read, uint32_t * data )
|
|
{
|
|
int r = 0;
|
|
struct InternalState * iss = (struct InternalState*)(((struct ProgrammerStructBase*)dev)->internal);
|
|
|
|
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 );
|
|
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 );
|
|
|
|
if( iss->statetag != STTAG( "WRSQ" ) )
|
|
{
|
|
StaticUpdatePROGBUFRegs( dev );
|
|
}
|
|
MCF.WriteReg32( dev, DMABSTRACTAUTO, 1 ); // Enable Autoexec (different kind of autoinc than outer autoinc)
|
|
iss->autoincrement = autoincrement;
|
|
}
|
|
|
|
MCF.WriteReg32( dev, DMDATA1, address_to_read );
|
|
MCF.WriteReg32( dev, DMCOMMAND, 0x00241000 );
|
|
|
|
iss->statetag = STTAG( "RDSQ" );
|
|
iss->currentstateval = address_to_read;
|
|
|
|
r |= MCF.WaitForDoneOp( dev, 0 );
|
|
if( r ) fprintf( stderr, "Fault on DefaultReadWord Part 1\n" );
|
|
}
|
|
|
|
if( iss->autoincrement )
|
|
iss->currentstateval += 4;
|
|
|
|
r |= MCF.ReadReg32( dev, DMDATA0, data );
|
|
|
|
if( iss->currentstateval == iss->ram_base + iss->ram_size )
|
|
MCF.WaitForDoneOp( dev, 1 ); // Ignore any post-errors.
|
|
return r;
|
|
}
|
|
|
|
int InternalUnlockFlash( void * dev, struct InternalState * iss )
|
|
{
|
|
int ret = 0;
|
|
uint32_t rw;
|
|
ret = MCF.ReadWord( dev, 0x40022010, &rw ); // FLASH->CTLR = 0x40022010
|
|
if( rw & 0x8080 )
|
|
{
|
|
ret = MCF.WriteWord( dev, 0x40022004, 0x45670123 ); // FLASH->KEYR = 0x40022004
|
|
if( ret ) goto reterr;
|
|
ret = MCF.WriteWord( dev, 0x40022004, 0xCDEF89AB );
|
|
if( ret ) goto reterr;
|
|
ret = MCF.WriteWord( dev, 0x40022008, 0x45670123 ); // OBKEYR = 0x40022008 // For user word unlocking
|
|
if( ret ) goto reterr;
|
|
ret = MCF.WriteWord( dev, 0x40022008, 0xCDEF89AB );
|
|
if( ret ) goto reterr;
|
|
ret = MCF.WriteWord( dev, 0x40022024, 0x45670123 ); // MODEKEYR = 0x40022024
|
|
if( ret ) goto reterr;
|
|
ret = MCF.WriteWord( dev, 0x40022024, 0xCDEF89AB );
|
|
if( ret ) goto reterr;
|
|
|
|
ret = MCF.ReadWord( dev, 0x40022010, &rw ); // FLASH->CTLR = 0x40022010
|
|
if( ret ) goto reterr;
|
|
|
|
if( rw & 0x8080 )
|
|
{
|
|
fprintf( stderr, "Error: Flash is not unlocked (CTLR = %08x)\n", rw );
|
|
return -9;
|
|
}
|
|
}
|
|
iss->flash_unlocked = 1;
|
|
return 0;
|
|
reterr:
|
|
fprintf( stderr, "Error unlocking flash, got code %d from underlying system\n", ret );
|
|
return ret;
|
|
}
|
|
|
|
int DefaultErase( void * dev, uint32_t address, uint32_t length, int type )
|
|
{
|
|
struct InternalState * iss = (struct InternalState*)(((struct ProgrammerStructBase*)dev)->internal);
|
|
uint32_t rw;
|
|
|
|
if( !iss->flash_unlocked )
|
|
{
|
|
if( ( rw = InternalUnlockFlash( dev, iss ) ) )
|
|
return rw;
|
|
}
|
|
|
|
if( type == 1 )
|
|
{
|
|
// Whole-chip flash
|
|
iss->statetag = STTAG( "XXXX" );
|
|
printf( "Whole-chip erase\n" );
|
|
if( MCF.WriteWord( dev, (intptr_t)&FLASH->CTLR, 0 ) ) goto flashoperr;
|
|
if( MCF.WriteWord( dev, (intptr_t)&FLASH->CTLR, FLASH_CTLR_MER ) ) goto flashoperr;
|
|
if( MCF.PrepForLongOp ) MCF.PrepForLongOp( dev ); // Give the programmer a headsup this next operation could take a while.
|
|
if( MCF.WriteWord( dev, (intptr_t)&FLASH->CTLR, CR_STRT_Set|FLASH_CTLR_MER ) ) goto flashoperr;
|
|
rw = MCF.WaitForDoneOp( dev, 0 );
|
|
if( MCF.WaitForFlash && MCF.WaitForFlash( dev ) ) { fprintf( stderr, "Error: Wait for flash error.\n" ); return -11; }
|
|
MCF.VoidHighLevelState( dev );
|
|
memset( iss->flash_sector_status, 1, sizeof( iss->flash_sector_status ) );
|
|
}
|
|
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( ( chunk_to_erase & 0xff000000 ) == 0x08000000 )
|
|
{
|
|
int sector = ( chunk_to_erase & 0x00ffffff ) / iss->sector_size;
|
|
if( sector < MAX_FLASH_SECTORS )
|
|
{
|
|
iss->flash_sector_status[sector] = 1;
|
|
}
|
|
}
|
|
|
|
// Step 4: set PAGE_ER of FLASH_CTLR(0x40022010)
|
|
if( MCF.WriteWord( dev, (intptr_t)&FLASH->CTLR, CR_PAGE_ER ) ) goto flashoperr; // Actually FTER
|
|
// Step 5: Write the first address of the fast erase page to the FLASH_ADDR register.
|
|
if( MCF.WriteWord( dev, (intptr_t)&FLASH->ADDR, chunk_to_erase ) ) goto flashoperr;
|
|
// Step 6: Set the STAT bit of FLASH_CTLR register to '1' to initiate a fast page erase (64 bytes) action.
|
|
if( MCF.PrepForLongOp ) MCF.PrepForLongOp( dev ); // Give the programmer a headsup this next operation could take a while.
|
|
if( MCF.WriteWord( dev, (intptr_t)&FLASH->CTLR, CR_STRT_Set | CR_PAGE_ER ) ) goto flashoperr;
|
|
if( MCF.WaitForFlash && MCF.WaitForFlash( dev ) ) return -99;
|
|
chunk_to_erase+=iss->sector_size;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
flashoperr:
|
|
fprintf( stderr, "Error: Flash operation error\n" );
|
|
return -93;
|
|
}
|
|
|
|
int DefaultReadBinaryBlob( void * dev, uint32_t address_to_read_from, uint32_t read_size, uint8_t * blob )
|
|
{
|
|
uint32_t rpos = address_to_read_from;
|
|
uint32_t rend = address_to_read_from + read_size;
|
|
|
|
while( rpos < rend )
|
|
{
|
|
int r;
|
|
int remain = rend - rpos;
|
|
|
|
if( ( rpos & 3 ) == 0 && remain >= 4 )
|
|
{
|
|
uint32_t rw;
|
|
r = MCF.ReadWord( dev, rpos, &rw );
|
|
//printf( "RW: %d %08x %08x\n", r, rpos, rw );
|
|
if( r ) return r;
|
|
int rem = remain;
|
|
if( rem > 4 ) rem = 4;
|
|
memcpy( blob, &rw, rem);
|
|
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;
|
|
}
|
|
}
|
|
}
|
|
int r = MCF.WaitForDoneOp( dev, 0 );
|
|
if( r ) fprintf( stderr, "Fault on DefaultReadBinaryBlob\n" );
|
|
return r;
|
|
}
|
|
|
|
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 )
|
|
{
|
|
struct InternalState * iss = (struct InternalState*)(((struct ProgrammerStructBase*)dev)->internal);
|
|
switch ( mode )
|
|
{
|
|
case HALT_MODE_HALT_BUT_NO_RESET: // Don't reboot.
|
|
case HALT_MODE_HALT_AND_RESET:
|
|
MCF.WriteReg32( dev, DMSHDWCFGR, 0x5aa50000 | (1<<10) ); // Shadow Config Reg
|
|
MCF.WriteReg32( dev, DMCFGR, 0x5aa50000 | (1<<10) ); // CFGR (1<<10 == Allow output from slave)
|
|
MCF.WriteReg32( dev, DMCFGR, 0x5aa50000 | (1<<10) ); // Bug in silicon? If coming out of cold boot, and we don't do our little "song and dance" this has to be called.
|
|
MCF.WriteReg32( dev, DMCONTROL, 0x80000001 ); // Make the debug module work properly.
|
|
if( mode == HALT_MODE_HALT_AND_RESET ) MCF.WriteReg32( dev, DMCONTROL, 0x80000003 ); // Reboot.
|
|
MCF.WriteReg32( dev, DMCONTROL, 0x80000001 ); // Re-initiate a halt request.
|
|
// MCF.WriteReg32( dev, DMCONTROL, 0x00000001 ); // Clear Halt Request. This is recommended, but not doing it seems more stable.
|
|
// Sometimes, even if the processor is halted but the MSB is clear, it will spuriously start?
|
|
MCF.FlushLLCommands( dev );
|
|
break;
|
|
case HALT_MODE_REBOOT:
|
|
MCF.WriteReg32( dev, DMCONTROL, 0x80000001 ); // Make the debug module work properly.
|
|
MCF.WriteReg32( dev, DMCONTROL, 0x80000001 ); // Initiate a halt request.
|
|
MCF.WriteReg32( dev, DMCONTROL, 0x80000003 ); // Reboot.
|
|
MCF.WriteReg32( dev, DMCONTROL, 0x40000001 ); // resumereq
|
|
MCF.FlushLLCommands( dev );
|
|
break;
|
|
case HALT_MODE_RESUME:
|
|
MCF.WriteReg32( dev, DMSHDWCFGR, 0x5aa50000 | (1<<10) ); // Shadow Config Reg
|
|
MCF.WriteReg32( dev, DMCFGR, 0x5aa50000 | (1<<10) ); // CFGR (1<<10 == Allow output from slave)
|
|
MCF.WriteReg32( dev, DMCFGR, 0x5aa50000 | (1<<10) ); // Bug in silicon? If coming out of cold boot, and we don't do our little "song and dance" this has to be called.
|
|
|
|
MCF.WriteReg32( dev, DMCONTROL, 0x40000001 ); // resumereq
|
|
MCF.FlushLLCommands( dev );
|
|
break;
|
|
case HALT_MODE_GO_TO_BOOTLOADER:
|
|
MCF.WriteReg32( dev, DMCONTROL, 0x80000001 ); // Make the debug module work properly.
|
|
MCF.WriteReg32( dev, DMCONTROL, 0x80000001 ); // Initiate a halt request.
|
|
|
|
MCF.WriteWord( dev, (intptr_t)&FLASH->KEYR, FLASH_KEY1 );
|
|
MCF.WriteWord( dev, (intptr_t)&FLASH->KEYR, FLASH_KEY2 );
|
|
MCF.WriteWord( dev, (intptr_t)&FLASH->BOOT_MODEKEYR, FLASH_KEY1 );
|
|
MCF.WriteWord( dev, (intptr_t)&FLASH->BOOT_MODEKEYR, FLASH_KEY2 );
|
|
MCF.WriteWord( dev, (intptr_t)&FLASH->STATR, 1<<14 );
|
|
MCF.WriteWord( dev, (intptr_t)&FLASH->CTLR, CR_LOCK_Set );
|
|
|
|
MCF.WriteReg32( dev, DMCONTROL, 0x80000003 ); // Reboot.
|
|
MCF.WriteReg32( dev, DMCONTROL, 0x40000001 ); // resumereq
|
|
MCF.FlushLLCommands( dev );
|
|
break;
|
|
default:
|
|
fprintf( stderr, "Error: Unknown halt mode %d\n", mode );
|
|
}
|
|
|
|
iss->flash_unlocked = 0;
|
|
iss->processor_in_mode = mode;
|
|
|
|
// In case processor halt process needs to complete, i.e. if it was in the middle of a flash op.
|
|
MCF.DelayUS( dev, 3000 );
|
|
|
|
return 0;
|
|
}
|
|
|
|
// Returns positive if received text, or request for input.
|
|
// Returns -1 if nothing was printed but received data.
|
|
// Returns negative if error.
|
|
// Returns 0 if no text waiting.
|
|
// maxlen MUST be at least 8 characters. We null terminate.
|
|
int DefaultPollTerminal( void * dev, uint8_t * buffer, int maxlen, uint32_t leaveflagA, int leaveflagB )
|
|
{
|
|
struct InternalState * iss = (struct InternalState*)(((struct ProgrammerStructBase*)dev)->internal);
|
|
int r;
|
|
uint32_t rr;
|
|
if( iss->statetag != STTAG( "TERM" ) )
|
|
{
|
|
MCF.WriteReg32( dev, DMABSTRACTAUTO, 0x00000000 ); // Disable Autoexec.
|
|
iss->statetag = STTAG( "TERM" );
|
|
}
|
|
r = MCF.ReadReg32( dev, DMDATA0, &rr );
|
|
if( r < 0 ) return r;
|
|
if( maxlen < 8 ) return -9;
|
|
|
|
// DMDATA1:
|
|
// bit 7 = host-acknowledge.
|
|
if( rr & 0x80 )
|
|
{
|
|
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 = MCF.ReadReg32( dev, DMDATA1, &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;
|
|
}
|
|
if( leaveflagA ) MCF.WriteReg32( dev, DMDATA1, leaveflagB );
|
|
MCF.WriteReg32( dev, DMDATA0, leaveflagA ); // Write that we acknowledge the data.
|
|
if( num_printf_chars == 0 ) return -1; // was acked?
|
|
if( num_printf_chars < 0 ) num_printf_chars = 0;
|
|
return num_printf_chars;
|
|
}
|
|
else
|
|
{
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
int DefaultUnbrick( void * dev )
|
|
{
|
|
printf( "Entering Unbrick Mode\n" );
|
|
MCF.Control3v3( dev, 0 );
|
|
|
|
MCF.DelayUS( dev, 60000 );
|
|
MCF.DelayUS( dev, 60000 );
|
|
MCF.DelayUS( dev, 60000 );
|
|
MCF.DelayUS( dev, 60000 );
|
|
MCF.Control3v3( dev, 1 );
|
|
MCF.DelayUS( dev, 100 );
|
|
MCF.FlushLLCommands( dev );
|
|
printf( "Connection starting\n" );
|
|
int timeout = 0;
|
|
int max_timeout = 500;
|
|
uint32_t ds = 0;
|
|
for( timeout = 0; timeout < max_timeout; timeout++ )
|
|
{
|
|
MCF.DelayUS( dev, 10 );
|
|
MCF.WriteReg32( dev, DMSHDWCFGR, 0x5aa50000 | (1<<10) ); // Shadow Config Reg
|
|
MCF.WriteReg32( dev, DMCFGR, 0x5aa50000 | (1<<10) ); // CFGR (1<<10 == Allow output from slave)
|
|
MCF.WriteReg32( dev, DMCFGR, 0x5aa50000 | (1<<10) ); // Bug in silicon? If coming out of cold boot, and we don't do our little "song and dance" this has to be called.
|
|
MCF.WriteReg32( dev, DMCONTROL, 0x80000001 ); // Make the debug module work properly.
|
|
MCF.WriteReg32( dev, DMCONTROL, 0x80000001 ); // Initiate a halt request.
|
|
MCF.WriteReg32( dev, DMCONTROL, 0x80000001 ); // No, really make sure.
|
|
MCF.WriteReg32( dev, DMCONTROL, 0x80000001 );
|
|
MCF.FlushLLCommands( dev );
|
|
int r = MCF.ReadReg32( dev, DMSTATUS, &ds );
|
|
if( r )
|
|
{
|
|
fprintf( stderr, "Error: Could not read DMSTATUS from programmers (%d)\n", r );
|
|
return -99;
|
|
}
|
|
MCF.FlushLLCommands( dev );
|
|
if( ds != 0xffffffff && ds != 0x00000000 ) break;
|
|
}
|
|
|
|
// Make sure we are in halt.
|
|
MCF.WriteReg32( dev, DMCONTROL, 0x80000001 ); // Make the debug module work properly.
|
|
MCF.WriteReg32( dev, DMCONTROL, 0x80000001 ); // Initiate a halt request.
|
|
MCF.WriteReg32( dev, DMCONTROL, 0x80000001 ); // No, really make sure.
|
|
MCF.WriteReg32( dev, DMCONTROL, 0x80000001 );
|
|
|
|
int r = MCF.ReadReg32( dev, DMSTATUS, &ds );
|
|
printf( "DMStatus After Halt: /%d/%08x\n", r, ds );
|
|
|
|
// Many times we would clear the halt request, but in this case, we want to just leave it here, to prevent it from booting.
|
|
// TODO: Experiment and see if this is needed/wanted in cases. NOTE: If you don't clear halt request, progarmmers can get stuck.
|
|
// MCF.WriteReg32( dev, DMCONTROL, 0x00000001 ); // Clear Halt Request.
|
|
|
|
// After more experimentation, it appaers to work best by not clearing the halt request.
|
|
MCF.FlushLLCommands( dev );
|
|
|
|
// Override all option bytes and reset to factory settings, unlocking all flash sections.
|
|
uint8_t option_data[] = { 0xa5, 0x5a, 0x97, 0x68, 0x00, 0xff, 0x00, 0xff, 0xff, 0x00, 0xff, 0x00 };
|
|
if( MCF.WriteBinaryBlob != DefaultWriteBinaryBlob )
|
|
{
|
|
fprintf( stderr, "Warning, using nonstandard WriteBinaryBlob. Unbrick may not work.\n" );
|
|
}
|
|
MCF.WriteBinaryBlob(dev, 0x1ffff800, sizeof( option_data ), option_data );
|
|
|
|
MCF.DelayUS( dev, 20000 );
|
|
|
|
if( timeout == max_timeout )
|
|
{
|
|
fprintf( stderr, "Timed out trying to unbrick\n" );
|
|
return -5;
|
|
}
|
|
MCF.Erase( dev, 0, 0, 1);
|
|
MCF.FlushLLCommands( dev );
|
|
return -5;
|
|
}
|
|
|
|
int DefaultConfigureNRSTAsGPIO( void * dev, int one_if_yes_gpio )
|
|
{
|
|
fprintf( stderr, "Error: DefaultConfigureNRSTAsGPIO does not work via the programmer here. Please see the demo \"optionbytes\"\n" );
|
|
return -5;
|
|
}
|
|
|
|
int DefaultConfigureReadProtection( void * dev, int one_if_yes_protect )
|
|
{
|
|
fprintf( stderr, "Error: DefaultConfigureReadProtection does not work via the programmer here. Please see the demo \"optionbytes\"\n" );
|
|
return -5;
|
|
}
|
|
|
|
int DefaultPrintChipInfo( void * dev )
|
|
{
|
|
uint32_t reg;
|
|
MCF.HaltMode( dev, HALT_MODE_HALT_BUT_NO_RESET );
|
|
|
|
if( MCF.ReadWord( dev, 0x1FFFF800, ® ) ) goto fail;
|
|
printf( "USER/RDPR : %04x/%04x\n", reg>>16, reg&0xFFFF );
|
|
if( MCF.ReadWord( dev, 0x1FFFF804, ® ) ) goto fail;
|
|
printf( "DATA1/DATA0: %04x/%04x\n", reg>>16, reg&0xFFFF );
|
|
if( MCF.ReadWord( dev, 0x1FFFF808, ® ) ) goto fail;
|
|
printf( "WRPR1/WRPR0: %04x/%04x\n", reg>>16, reg&0xFFFF );
|
|
if( MCF.ReadWord( dev, 0x1FFFF80c, ® ) ) goto fail;
|
|
printf( "WRPR3/WRPR2: %04x/%04x\n", reg>>16, reg&0xFFFF );
|
|
if( MCF.ReadWord( dev, 0x1FFFF7E0, ® ) ) goto fail;
|
|
printf( "Flash Size: %d kB\n", (reg&0xffff) );
|
|
if( MCF.ReadWord( dev, 0x1FFFF7E8, ® ) ) goto fail;
|
|
printf( "R32_ESIG_UNIID1: %08x\n", reg );
|
|
if( MCF.ReadWord( dev, 0x1FFFF7EC, ® ) ) goto fail;
|
|
printf( "R32_ESIG_UNIID2: %08x\n", reg );
|
|
if( MCF.ReadWord( dev, 0x1FFFF7F0, ® ) ) goto fail;
|
|
printf( "R32_ESIG_UNIID3: %08x\n", reg );
|
|
return 0;
|
|
fail:
|
|
fprintf( stderr, "Error: Failed to get chip details\n" );
|
|
return -11;
|
|
}
|
|
|
|
int DefaultVoidHighLevelState( void * dev )
|
|
{
|
|
struct InternalState * iss = (struct InternalState*)(((struct ProgrammerStructBase*)dev)->internal);
|
|
iss->statetag = STTAG( "VOID" );
|
|
return 0;
|
|
}
|
|
|
|
int DefaultDelayUS( void * dev, int us )
|
|
{
|
|
#if defined(WINDOWS) || defined(WIN32) || defined(_WIN32)
|
|
Sleep( (us+9999) / 1000 );
|
|
#else
|
|
usleep( us );
|
|
#endif
|
|
return 0;
|
|
}
|
|
|
|
int SetupAutomaticHighLevelFunctions( void * dev )
|
|
{
|
|
// Will populate high-level functions from low-level functions.
|
|
if( MCF.WriteReg32 == 0 && MCF.ReadReg32 == 0 && MCF.WriteWord == 0 ) return -5;
|
|
|
|
// Else, TODO: Build the high level functions from low level functions.
|
|
// If a high-level function alrady exists, don't override.
|
|
|
|
if( !MCF.SetupInterface )
|
|
MCF.SetupInterface = DefaultSetupInterface;
|
|
if( !MCF.WriteBinaryBlob )
|
|
MCF.WriteBinaryBlob = DefaultWriteBinaryBlob;
|
|
if( !MCF.ReadBinaryBlob )
|
|
MCF.ReadBinaryBlob = DefaultReadBinaryBlob;
|
|
if( !MCF.WriteWord )
|
|
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 )
|
|
MCF.HaltMode = DefaultHaltMode;
|
|
if( !MCF.PollTerminal )
|
|
MCF.PollTerminal = DefaultPollTerminal;
|
|
if( !MCF.WaitForFlash )
|
|
MCF.WaitForFlash = DefaultWaitForFlash;
|
|
if( !MCF.WaitForDoneOp )
|
|
MCF.WaitForDoneOp = DefaultWaitForDoneOp;
|
|
if( !MCF.PrintChipInfo )
|
|
MCF.PrintChipInfo = DefaultPrintChipInfo;
|
|
if( !MCF.Unbrick )
|
|
MCF.Unbrick = DefaultUnbrick;
|
|
if( !MCF.ConfigureNRSTAsGPIO )
|
|
MCF.ConfigureNRSTAsGPIO = DefaultConfigureNRSTAsGPIO;
|
|
if( !MCF.VoidHighLevelState )
|
|
MCF.VoidHighLevelState = DefaultVoidHighLevelState;
|
|
if( !MCF.DelayUS )
|
|
MCF.DelayUS = DefaultDelayUS;
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
|
|
|
|
void TestFunction(void * dev )
|
|
{
|
|
uint32_t rv;
|
|
int r;
|
|
MCF.WriteReg32( dev, DMCONTROL, 0x80000001 ); // Make the debug module work properly.
|
|
MCF.WriteReg32( dev, DMCONTROL, 0x80000001 ); // Initiate a halt request.
|
|
MCF.WriteReg32( dev, DMCONTROL, 0x00000001 ); // Clear Halt Request.
|
|
|
|
r = MCF.WriteWord( dev, 0x20000100, 0xdeadbeef );
|
|
r = MCF.WriteWord( dev, 0x20000104, 0xcafed0de );
|
|
r = MCF.WriteWord( dev, 0x20000108, 0x12345678 );
|
|
r = MCF.WriteWord( dev, 0x20000108, 0x00b00d00 );
|
|
r = MCF.WriteWord( dev, 0x20000104, 0x33334444 );
|
|
|
|
r = MCF.ReadWord( dev, 0x20000100, &rv );
|
|
printf( "**>>> %d %08x\n", r, rv );
|
|
r = MCF.ReadWord( dev, 0x20000104, &rv );
|
|
printf( "**>>> %d %08x\n", r, rv );
|
|
r = MCF.ReadWord( dev, 0x20000108, &rv );
|
|
printf( "**>>> %d %08x\n", r, rv );
|
|
|
|
|
|
r = MCF.ReadWord( dev, 0x00000300, &rv );
|
|
printf( "F %d %08x\n", r, rv );
|
|
r = MCF.ReadWord( dev, 0x00000304, &rv );
|
|
printf( "F %d %08x\n", r, rv );
|
|
r = MCF.ReadWord( dev, 0x00000308, &rv );
|
|
printf( "F %d %08x\n", r, rv );
|
|
|
|
uint8_t buffer[256];
|
|
int i;
|
|
for( i = 0; i < 256; i++ ) buffer[i] = 0;
|
|
MCF.WriteBinaryBlob( dev, 0x08000300, 256, buffer );
|
|
MCF.ReadBinaryBlob( dev, 0x08000300, 256, buffer );
|
|
for( i = 0; i < 256; i++ )
|
|
{
|
|
printf( "%02x ", buffer[i] );
|
|
if( (i & 0xf) == 0xf ) printf( "\n" );
|
|
}
|
|
|
|
for( i = 0; i < 256; i++ ) buffer[i] = i;
|
|
MCF.WriteBinaryBlob( dev, 0x08000300, 256, buffer );
|
|
MCF.ReadBinaryBlob( dev, 0x08000300, 256, buffer );
|
|
for( i = 0; i < 256; i++ )
|
|
{
|
|
printf( "%02x ", buffer[i] );
|
|
if( (i & 0xf) == 0xf ) printf( "\n" );
|
|
}
|
|
}
|
|
|
|
|
|
|