1097 lines
32 KiB
C
1097 lines
32 KiB
C
// CH32V003 SWIO / CH32Vxxx / CH32Xxxx bit banging minimal reference
|
|
// implementation for bit banged IO or primitive IO.
|
|
//
|
|
// You will need to implement some of the basic functions. Example
|
|
// implementations for the functions on the ESP32-S2 are included.
|
|
//
|
|
// Copyright 2023-2024 <>< Charles Lohr, May be licensed under the MIT/x11 or
|
|
// NewBSD licenses. You choose. (Can be included in commercial and copyleft work)
|
|
//
|
|
// This file is original work.
|
|
//
|
|
// Mostly tested, though, not may not be perfect. Expect to tweak some things.
|
|
// The 003 code is pretty hardened, the other code may not be.
|
|
//
|
|
// Originally the only resources used to produce this file were publicly available docs
|
|
// and playing with the chips. The only leveraged tool was this:
|
|
// https://github.com/perigoso/sigrok-rvswd
|
|
|
|
#ifndef _WCH_RVSWD_RVSWIO_H
|
|
#define _WCH_RVSWD_RVSWIO_H
|
|
|
|
#include <stdint.h>
|
|
#include <string.h>
|
|
|
|
// This is a hacky thing, but if you are laaaaazzzyyyy and don't want to add a 10k
|
|
// resistor, youcan do this. It glitches the line high very, very briefly.
|
|
// Enable for when you don't have a 10k pull-upand are relying on the internal pull-up.
|
|
// WARNING: If you set this, you should set the drive current to 5mA. This is
|
|
// only useful / needed if you are using the built-in functions.
|
|
//#define R_GLITCH_HIGH
|
|
//#define RUNNING_ON_ESP32S2
|
|
//#define BB_PRINTF_DEBUG uprintf
|
|
|
|
// You should interface to this file via these functions
|
|
|
|
enum RiscVChip {
|
|
CHIP_UNKNOWN = 0x00,
|
|
CHIP_CH32V10x = 0x01,
|
|
CHIP_CH57x = 0x02,
|
|
CHIP_CH56x = 0x03,
|
|
CHIP_CH32V20x = 0x05,
|
|
CHIP_CH32V30x = 0x06,
|
|
CHIP_CH58x = 0x07,
|
|
CHIP_CH32V003 = 0x09,
|
|
CHIP_CH59x = 0x0b,
|
|
CHIP_CH643 = 0x0c,
|
|
CHIP_CH32X03x = 0x0d,
|
|
CHIP_CH32L10x = 0x0e,
|
|
CHIP_CH564 = 0x0f,
|
|
|
|
CHIP_CH32V00x = 0x4e,
|
|
|
|
CHIP_CH570 = 0x8b,
|
|
CHIP_CH585 = 0x4b,
|
|
CHIP_CH645 = 0x46,
|
|
CHIP_CH641 = 0x49,
|
|
CHIP_CH32V317 = 0x86,
|
|
CHIP_CH32M030 = 0x8e,
|
|
};
|
|
|
|
enum MemoryArea {
|
|
DEFAULT_AREA = 0,
|
|
PROGRAM_AREA = 1,
|
|
BOOTLOADER_AREA = 2,
|
|
OPTIONS_AREA = 3,
|
|
EEPROM_AREA = 4,
|
|
RAM_AREA = 5,
|
|
};
|
|
|
|
struct SWIOState
|
|
{
|
|
int opmode; // 0 for undefined, 1 for SWIO, 2 for SWD
|
|
enum RiscVChip target_chip_type;
|
|
int sectorsize;
|
|
|
|
// Zero the rest of the structure.
|
|
uint32_t statetag;
|
|
uint32_t lastwriteflags;
|
|
uint32_t currentstateval;
|
|
uint32_t flash_unlocked;
|
|
uint32_t autoincrement;
|
|
uint32_t clock_set;
|
|
uint32_t no_autoexec;
|
|
};
|
|
|
|
#define STTAG( x ) (*((uint32_t*)(x)))
|
|
|
|
|
|
// Provided Basic functions
|
|
static inline void ConfigureIOForRVSWD(void);
|
|
static inline void ConfigureIOForRVSWIO(void);
|
|
static void MCFWriteReg32( struct SWIOState * state, uint8_t command, uint32_t value );
|
|
static int MCFReadReg32( struct SWIOState * state, uint8_t command, uint32_t * value );
|
|
|
|
// More advanced functions built on lower level PHY.
|
|
static int InitializeSWDSWIO( struct SWIOState * state );
|
|
static int ReadWord( struct SWIOState * state, uint32_t word, uint32_t * ret );
|
|
static int WriteWord( struct SWIOState * state, uint32_t word, uint32_t val );
|
|
static int WaitForFlash( struct SWIOState * state );
|
|
static int WaitForDoneOp( struct SWIOState * state );
|
|
static int WriteBlock( struct SWIOState * iss, uint32_t address_to_write, uint8_t * data, uint8_t len, uint8_t erase );
|
|
static int UnlockFlash( struct SWIOState * iss );
|
|
static int EraseFlash( struct SWIOState * iss, uint32_t address, uint32_t length, int type );
|
|
static void ResetInternalProgrammingState( struct SWIOState * iss );
|
|
static int PollTerminal( struct SWIOState * iss, uint8_t * buffer, int maxlen, uint32_t leavevalA, uint32_t leavevalB );
|
|
|
|
|
|
#define BDMDATA0 0x04
|
|
#define BDMDATA1 0x05
|
|
#define DMCONTROL 0x10
|
|
#define DMSTATUS 0x11
|
|
#define DMHARTINFO 0x12
|
|
#define DMABSTRACTCS 0x16
|
|
#define DMCOMMAND 0x17
|
|
#define DMABSTRACTAUTO 0x18
|
|
#define DMPROGBUF0 0x20
|
|
#define DMPROGBUF1 0x21
|
|
#define DMPROGBUF2 0x22
|
|
#define DMPROGBUF3 0x23
|
|
#define DMPROGBUF4 0x24
|
|
#define DMPROGBUF5 0x25
|
|
#define DMPROGBUF6 0x26
|
|
#define DMPROGBUF7 0x27
|
|
|
|
#define DMCPBR 0x7C
|
|
#define DMCFGR 0x7D
|
|
#define DMSHDWCFGR 0x7E
|
|
|
|
#ifndef __CH32V00x_H
|
|
#define FLASH_STATR_WRPRTERR ((uint8_t)0x10)
|
|
#define CR_PAGE_PG ((uint32_t)0x00010000)
|
|
#define CR_BUF_LOAD ((uint32_t)0x00040000)
|
|
#ifndef FLASH_CTLR_MER
|
|
#define FLASH_CTLR_MER ((uint16_t)0x0004) /* Mass Erase */
|
|
#endif
|
|
#define CR_STRT_Set ((uint32_t)0x00000040)
|
|
#define CR_PAGE_ER ((uint32_t)0x00020000)
|
|
#define CR_BUF_RST ((uint32_t)0x00080000)
|
|
#endif
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
// High level functions
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
static int InitializeSWDSWIO( struct SWIOState * state )
|
|
{
|
|
for( int timeout = 20; timeout; timeout-- )
|
|
{
|
|
//printf( "CFG FOR RV\n" );
|
|
ConfigureIOForRVSWIO();
|
|
//printf( "DONE CFG FOR RV\n" );
|
|
|
|
// Careful - don't halt the part, we might just want to attach for a debug printf or something.
|
|
state->target_chip_type = CHIP_UNKNOWN;
|
|
state->sectorsize = 64;
|
|
|
|
state->opmode = 1; // Try SWIO first
|
|
// First try to see if there is an 003.
|
|
MCFWriteReg32( state, DMSHDWCFGR, 0x5aa50000 | (1<<10) ); // Shadow Config Reg
|
|
MCFWriteReg32( state, DMCFGR, 0x5aa50000 | (1<<10) ); // CFGR (1<<10 == Allow output from slave)
|
|
MCFWriteReg32( state, DMSHDWCFGR, 0x5aa50000 | (1<<10) ); // Try twice just in case
|
|
MCFWriteReg32( state, DMCFGR, 0x5aa50000 | (1<<10) );
|
|
|
|
MCFWriteReg32( state, DMCONTROL, 0x00000001 );
|
|
MCFWriteReg32( state, DMCONTROL, 0x00000001 );
|
|
|
|
// See if we can see a chip here...
|
|
uint32_t value = 0;
|
|
int readdm = MCFReadReg32( state, DMCFGR, &value );
|
|
if( readdm == 0 && ( value & 0xffff0000 ) == ( 0x5aa50000 ) )
|
|
{
|
|
BB_PRINTF_DEBUG( "Found RVSWIO interface.\n" );
|
|
return 0;
|
|
}
|
|
|
|
//printf( "CFG FOR SWD\n" );
|
|
ConfigureIOForRVSWD();
|
|
//printf( "DONE CFG FOR SWD\n" );
|
|
|
|
//Otherwise Maybe it's SWD?
|
|
state->opmode = 2;
|
|
|
|
MCFWriteReg32( state, DMCONTROL, 0x00000001 );
|
|
MCFWriteReg32( state, DMCONTROL, 0x00000001 );
|
|
|
|
uint32_t dmstatus, dmcontrol;
|
|
if( MCFReadReg32( state, DMSTATUS, &dmstatus ) != 0 ||
|
|
MCFReadReg32( state, DMCONTROL, &dmcontrol ) != 0 )
|
|
{
|
|
//BB_PRINTF_DEBUG( "Could not read from RVSWD connection\n" );
|
|
state->opmode = 0;
|
|
continue;
|
|
}
|
|
|
|
// See if we can see a chip here...
|
|
if( ( ( ( dmstatus >> 8 ) & 0xf ) != 0x0c &&
|
|
( ( dmstatus >> 8 ) & 0xf ) != 0x03 ) ||
|
|
dmcontrol != 1 )
|
|
{
|
|
//BB_PRINTF_DEBUG( "DMSTATUS invalid (Probably no RVSWD chip)\n" );
|
|
state->opmode = 0;
|
|
continue;
|
|
}
|
|
|
|
MCFWriteReg32( state, DMABSTRACTCS, 0x08000302 ); // Clear out abstractcs register.
|
|
BB_PRINTF_DEBUG( "Found RVSWD interface\n" );
|
|
return 0;
|
|
}
|
|
//printf( "TIMEOUT\n" );
|
|
return -55;
|
|
}
|
|
|
|
static int DetermineChipTypeAndSectorInfo( struct SWIOState * iss, uint8_t * reply)
|
|
{
|
|
struct SWIOState * dev = iss;
|
|
if( iss->target_chip_type == CHIP_UNKNOWN || reply != NULL )
|
|
{
|
|
if( iss->opmode == 0 )
|
|
{
|
|
int r = InitializeSWDSWIO( iss );
|
|
if( r ) return 0x11;
|
|
}
|
|
uint32_t rr;
|
|
if( MCFReadReg32( dev, DMHARTINFO, &rr ) )
|
|
{
|
|
BB_PRINTF_DEBUG( "Error: Could not get hart info.\n" );
|
|
return 0x12;
|
|
}
|
|
|
|
MCFWriteReg32( dev, DMCONTROL, 0x80000001 ); // Make the debug module work properly.
|
|
MCFWriteReg32( dev, DMCONTROL, 0x80000001 ); // Initiate halt request.
|
|
|
|
// Tricky, this function needs to clean everything up because it may be used entering debugger.
|
|
uint32_t old_data0;
|
|
MCFReadReg32( dev, BDMDATA0, &old_data0 );
|
|
MCFWriteReg32( dev, DMCOMMAND, 0x00221008 ); // Copy data from x8.
|
|
uint32_t old_x8;
|
|
MCFReadReg32( dev, BDMDATA0, &old_x8 );
|
|
|
|
uint32_t marchid = 0;
|
|
|
|
MCFWriteReg32( dev, DMABSTRACTCS, 0x08000700 ); // Clear out any dmabstractcs errors.
|
|
|
|
MCFWriteReg32( dev, DMABSTRACTAUTO, 0x00000000 );
|
|
MCFWriteReg32( dev, DMCOMMAND, 0x00220000 | 0xf12 );
|
|
MCFWriteReg32( dev, DMCOMMAND, 0x00220000 | 0xf12 ); // Need to double-read, not sure why.
|
|
MCFReadReg32( dev, BDMDATA0, &marchid );
|
|
MCFWriteReg32( dev, DMPROGBUF0, 0x90024000 ); // c.ebreak <<== c.lw x8, 0(x8)
|
|
|
|
uint32_t chip_id = 0;
|
|
uint32_t vendor_bytes = 0;
|
|
uint32_t sevenf_id = 0;
|
|
int read_protection = 0;
|
|
uint8_t ch5xx = 0;
|
|
MCFReadReg32( dev, 0x7f, &sevenf_id );
|
|
|
|
if( sevenf_id == 0 )
|
|
{
|
|
// Need to load new progbuf because we're reading 1 byte now
|
|
MCFWriteReg32( dev, DMPROGBUF0, 0x00040403 ); // lb x8, 0(x8)
|
|
MCFWriteReg32( dev, DMPROGBUF1, 0x00100073 ); // c.ebreak
|
|
MCFWriteReg32( dev, BDMDATA0, 0x40001041 ); // Special chip ID location.
|
|
MCFWriteReg32( dev, DMCOMMAND, 0x00271008 ); // Copy data to x8, and execute.
|
|
WaitForDoneOp( dev );
|
|
MCFWriteReg32( dev, DMCOMMAND, 0x00221008 ); // Copy data from x8.
|
|
MCFReadReg32( dev, BDMDATA0, &chip_id );
|
|
chip_id = chip_id & 0xff;
|
|
|
|
// Looks like a CH32V103 or a CH56x
|
|
if( chip_id == 0 )
|
|
{
|
|
// First check for CH56x
|
|
MCFWriteReg32( dev, BDMDATA0, 0x40001001 );
|
|
MCFWriteReg32( dev, DMCOMMAND, 0x00271008 ); // Copy data to x8, and execute.
|
|
WaitForDoneOp( dev );
|
|
MCFWriteReg32( dev, DMCOMMAND, 0x00221008 ); // Copy data from x8.
|
|
MCFReadReg32( dev, BDMDATA0, &chip_id );
|
|
MCFWriteReg32( dev, BDMDATA0, 0x40001002 ); // Special chip ID location.
|
|
MCFWriteReg32( dev, DMCOMMAND, 0x00271008 ); // Copy data to x8, and execute.
|
|
WaitForDoneOp( dev );
|
|
MCFWriteReg32( dev, DMCOMMAND, 0x00221008 ); // Copy data from x8.
|
|
MCFReadReg32( dev, BDMDATA0, &vendor_bytes );
|
|
|
|
if( (vendor_bytes & 0xff) == 2 && ((chip_id & 0xff) == 65 || (chip_id & 0xff) == 69) )
|
|
{
|
|
iss->target_chip_type = CHIP_CH56x;
|
|
chip_id = 0x500 | (chip_id & 0xff);
|
|
goto chip_identified;
|
|
}
|
|
|
|
// Now actually check for CH32V103
|
|
MCFWriteReg32( dev, DMPROGBUF0, 0x90024000 ); // c.ebreak <<== c.lw x8, 0(x8)
|
|
MCFWriteReg32( dev, BDMDATA0, 0x1ffff880 ); // Special chip ID location.
|
|
MCFWriteReg32( dev, DMCOMMAND, 0x00271008 ); // Copy data to x8, and execute.
|
|
WaitForDoneOp( dev );
|
|
MCFWriteReg32( dev, DMCOMMAND, 0x00221008 ); // Copy data from x8.
|
|
MCFReadReg32( dev, BDMDATA0, &chip_id );
|
|
MCFWriteReg32( dev, BDMDATA0, 0x1ffff884 ); // Special chip ID location.
|
|
MCFWriteReg32( dev, DMCOMMAND, 0x00271008 ); // Copy data to x8, and execute.
|
|
WaitForDoneOp( dev );
|
|
MCFWriteReg32( dev, DMCOMMAND, 0x00221008 ); // Copy data from x8.
|
|
MCFReadReg32( dev, BDMDATA0, &vendor_bytes );
|
|
|
|
if( ((((vendor_bytes >> 16) & 0xff00) != 0x2500) && (((vendor_bytes >> 16) & 0xdf00) != 0x1500)) || chip_id != 0xdc78fe34 )
|
|
{
|
|
uint32_t flash_obr = 0;
|
|
MCFWriteReg32( dev, BDMDATA0, 0x4002201c ); // Special chip ID location.
|
|
MCFWriteReg32( dev, DMCOMMAND, 0x00271008 ); // Copy data to x8, and execute.
|
|
WaitForDoneOp( dev );
|
|
MCFWriteReg32( dev, DMCOMMAND, 0x00221008 ); // Copy data from x8.
|
|
MCFReadReg32( dev, BDMDATA0, &flash_obr );
|
|
|
|
if( (flash_obr & 3) == 2 )
|
|
{
|
|
iss->target_chip_type = CHIP_CH32V10x;
|
|
iss->no_autoexec = 1;
|
|
iss->sectorsize = 128;
|
|
read_protection = 1;
|
|
chip_id = (3 << 12) | 0x103;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
iss->target_chip_type = CHIP_CH32V10x;
|
|
iss->no_autoexec = 1;
|
|
iss->sectorsize = 128;
|
|
read_protection = -1;
|
|
chip_id = (3 << 12) | 0x103;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Check for CH5xx
|
|
read_protection = -1;
|
|
if( (chip_id & 0xf0) == 0x90 )
|
|
{
|
|
uint32_t sevenc = 0;
|
|
MCFReadReg32( dev, DMCPBR, &sevenc );
|
|
if((sevenc & 0x30000) == 0)
|
|
{
|
|
iss->target_chip_type = CHIP_CH59x;
|
|
iss->sectorsize = 256;
|
|
ch5xx = 1;
|
|
chip_id = 0x500 | chip_id;
|
|
}
|
|
else
|
|
{
|
|
iss->target_chip_type = CHIP_CH585;
|
|
iss->no_autoexec = 1;
|
|
iss->sectorsize = 256;
|
|
ch5xx = 1;
|
|
chip_id = 0x500 | 0x85;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if( chip_id == 0x70 || chip_id == 0x72 )
|
|
{
|
|
iss->target_chip_type = CHIP_CH570;
|
|
iss->sectorsize = 4096;
|
|
ch5xx = 1;
|
|
chip_id = 0x500 | chip_id;
|
|
}
|
|
else if( chip_id == 0x71 || chip_id == 0x73 )
|
|
{
|
|
iss->target_chip_type = CHIP_CH57x;
|
|
iss->no_autoexec = 1;
|
|
iss->sectorsize = 256;
|
|
ch5xx = 1;
|
|
chip_id = 0x500 | chip_id;
|
|
}
|
|
else if( chip_id == 0x82 || chip_id == 0x83 )
|
|
{
|
|
iss->target_chip_type = CHIP_CH58x;
|
|
iss->no_autoexec = 1;
|
|
iss->sectorsize = 256;
|
|
ch5xx = 1;
|
|
chip_id = 0x500 | chip_id;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
uint32_t masked_id = sevenf_id & 0xfff00000;
|
|
uint32_t masked_id2 = sevenf_id & 0xfff00f00;
|
|
if( masked_id == 0x3b00000 )
|
|
{
|
|
iss->target_chip_type = CHIP_CH32M030;
|
|
iss->sectorsize = 256;
|
|
chip_id = (2 << 12) | 0x30;
|
|
}
|
|
else if( masked_id == 0x56400000 )
|
|
{
|
|
iss->target_chip_type = CHIP_CH564;
|
|
iss->sectorsize = 256;
|
|
chip_id = 0x564;
|
|
}
|
|
else if( masked_id == 0x31700000 )
|
|
{
|
|
iss->target_chip_type = CHIP_CH32V317;
|
|
iss->sectorsize = 256;
|
|
chip_id = (3 << 12) | 0x317;
|
|
}
|
|
else if( masked_id == 0x00200000 || masked_id == 0x00400000 || masked_id == 0x00600000 || masked_id == 0x00700000 )
|
|
{
|
|
iss->target_chip_type = CHIP_CH32V00x;
|
|
iss->sectorsize = 256;
|
|
chip_id = (3 << 12) | (masked_id >> 20);
|
|
}
|
|
|
|
if( masked_id2 == 0x64100500 )
|
|
{
|
|
iss->target_chip_type = CHIP_CH641;
|
|
iss->sectorsize = 64;
|
|
chip_id = 0x641;
|
|
}
|
|
else if( masked_id2 == 0x64300600 )
|
|
{
|
|
iss->target_chip_type = CHIP_CH643;
|
|
iss->sectorsize = 256;
|
|
chip_id = 0x643;
|
|
}
|
|
else if( masked_id == 0x64500000 )
|
|
{
|
|
iss->target_chip_type = CHIP_CH645;
|
|
iss->sectorsize = 256;
|
|
chip_id = 0x645;
|
|
}
|
|
else if( masked_id2 == 0x3500600 )
|
|
{
|
|
iss->target_chip_type = CHIP_CH32X03x;
|
|
iss->sectorsize = 256;
|
|
chip_id = (4 << 12) | 0x35;
|
|
}
|
|
else if( masked_id2 == 0x10300700 )
|
|
{
|
|
iss->target_chip_type = CHIP_CH32L10x;
|
|
iss->sectorsize = 256;
|
|
chip_id = (1 << 12) | 0x103;
|
|
}
|
|
else if( masked_id2 == 0x300500 )
|
|
{
|
|
iss->target_chip_type = CHIP_CH32V003;
|
|
iss->sectorsize = 64;
|
|
chip_id = (3 << 12) | 0x3;
|
|
}
|
|
|
|
if( (sevenf_id & 0x20000500) == 0x20000500 || (sevenf_id & 0x30000500) == 0x30000500 )
|
|
{
|
|
switch ((sevenf_id & 0xfff00000) >> 20)
|
|
{
|
|
case 0x203:
|
|
case 0x208:
|
|
iss->target_chip_type = CHIP_CH32V20x;
|
|
iss->sectorsize = 256;
|
|
break;
|
|
case 0x303:
|
|
case 0x305:
|
|
case 0x307:
|
|
iss->target_chip_type = CHIP_CH32V30x;
|
|
iss->sectorsize = 256;
|
|
break;
|
|
}
|
|
chip_id = (3 << 12) | (masked_id >> 20);
|
|
}
|
|
}
|
|
|
|
chip_identified:
|
|
|
|
if( read_protection == 0 )
|
|
{
|
|
uint32_t one;
|
|
int two;
|
|
MCFWriteReg32( dev, BDMDATA0, 0x4002201c );
|
|
MCFWriteReg32( dev, DMCOMMAND, 0x00271008 ); // Copy data to x8, and execute.
|
|
WaitForDoneOp( dev );
|
|
MCFWriteReg32( dev, DMCOMMAND, 0x00221008 ); // Copy data from x8.
|
|
MCFReadReg32( dev, BDMDATA0, &one );
|
|
MCFWriteReg32( dev, BDMDATA0, 0x40022020 );
|
|
MCFWriteReg32( dev, DMCOMMAND, 0x00271008 ); // Copy data to x8, and execute.
|
|
WaitForDoneOp( dev );
|
|
MCFWriteReg32( dev, DMCOMMAND, 0x00221008 ); // Copy data from x8.
|
|
MCFReadReg32( dev, BDMDATA0, (uint32_t*)&two );
|
|
|
|
if( (one & 2) || two != -1 ) read_protection = 1;
|
|
}
|
|
|
|
if( reply != NULL )
|
|
{
|
|
reply[3] = sevenf_id >> 24;
|
|
reply[2] = sevenf_id >> 16;
|
|
reply[1] = sevenf_id >> 8;
|
|
reply[0] = sevenf_id;
|
|
reply[5] = chip_id >> 8;
|
|
reply[4] = chip_id;
|
|
if( read_protection == 1 ) reply[5] |= 0x80;
|
|
}
|
|
|
|
// Cleanup
|
|
MCFWriteReg32( dev, BDMDATA0, old_x8 );
|
|
MCFWriteReg32( dev, DMCOMMAND, 0x00231008 ); // Copy data to x8
|
|
MCFWriteReg32( dev, BDMDATA0, old_data0 );
|
|
iss->statetag = STTAG( "XXXX" );
|
|
#if CH5xx_SUPPORT
|
|
if( ch5xx ) ch5xx_set_clock( dev, 0 );
|
|
#endif
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int WaitForFlash( struct SWIOState * iss )
|
|
{
|
|
struct SWIOState * dev = iss;
|
|
|
|
if( DetermineChipTypeAndSectorInfo( iss, NULL ) ) return -9;
|
|
|
|
uint32_t rw, timeout = 0;
|
|
do
|
|
{
|
|
rw = 0;
|
|
ReadWord( dev, 0x4002200C, &rw ); // FLASH_STATR => 0x4002200C
|
|
} while( (rw & 1) && timeout++ < 2000); // BSY flag.
|
|
|
|
WriteWord( dev, 0x4002200C, 0 );
|
|
|
|
if( rw & FLASH_STATR_WRPRTERR )
|
|
return -44;
|
|
|
|
if( rw & 1 )
|
|
return -5;
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
static int WaitForDoneOp( struct SWIOState * iss )
|
|
{
|
|
int r;
|
|
uint32_t rrv;
|
|
int ret = 0;
|
|
int timeout = 1000;
|
|
struct SWIOState * dev = iss;
|
|
do
|
|
{
|
|
r = MCFReadReg32( dev, DMABSTRACTCS, &rrv );
|
|
if( r ) return r;
|
|
if( timeout-- == 0 ) return -8;
|
|
}
|
|
while( rrv & (1<<12) );
|
|
if( (rrv >> 8 ) & 7 )
|
|
{
|
|
MCFWriteReg32( dev, DMABSTRACTCS, 0x00000700 );
|
|
ret = -33;
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
static int StaticUpdatePROGBUFRegs( struct SWIOState * dev )
|
|
{
|
|
if( DetermineChipTypeAndSectorInfo( dev, NULL ) ) return -9;
|
|
|
|
MCFWriteReg32( dev, DMABSTRACTAUTO, 0 ); // Disable Autoexec.
|
|
uint32_t rr;
|
|
MCFReadReg32( dev, DMHARTINFO, &rr );
|
|
uint32_t data0offset = 0xe0000000 | ( rr & 0x7ff );
|
|
MCFWriteReg32( dev, BDMDATA0, data0offset ); // DATA0's location in memory. (hard code to 0xe00000f4 if only working on the 003)
|
|
MCFWriteReg32( dev, DMCOMMAND, 0x0023100a ); // Copy data to x10
|
|
MCFWriteReg32( dev, BDMDATA0, data0offset + 4 ); // DATA1's location in memory. (hard code to 0xe00000f8 if only working on the 003)
|
|
MCFWriteReg32( dev, DMCOMMAND, 0x0023100b ); // Copy data to x11
|
|
MCFWriteReg32( dev, BDMDATA0, 0x4002200c ); //FLASH->STATR (note add 4 to FLASH->CTLR)
|
|
MCFWriteReg32( dev, DMCOMMAND, 0x0023100c ); // Copy data to x12
|
|
|
|
// This is not even needed on the v20x/v30x chips. But it won't harm us to set the register for simplicity.
|
|
// v003 requires bufload every word.
|
|
// x035 requires bufload every word in spite of what the datasheet says.
|
|
// CR_PAGE_PG = FTPG = 0x00010000 | CR_BUF_LOAD = 0x00040000
|
|
MCFWriteReg32( dev, BDMDATA0, 0x00010000|0x00040000 );
|
|
|
|
MCFWriteReg32( dev, DMCOMMAND, 0x0023100d ); // Copy data to x13
|
|
return 0;
|
|
}
|
|
|
|
static void ResetInternalProgrammingState( struct SWIOState * iss )
|
|
{
|
|
iss->statetag = 0;
|
|
iss->lastwriteflags = 0;
|
|
iss->currentstateval = 0;
|
|
iss->flash_unlocked = 0;
|
|
iss->autoincrement = 0;
|
|
iss->clock_set = 0;
|
|
}
|
|
|
|
static int ReadByte( struct SWIOState * iss, uint32_t address_to_read, uint8_t * data )
|
|
{
|
|
struct SWIOState * dev = iss;
|
|
int ret = 0;
|
|
iss->statetag = STTAG( "XXXX" );
|
|
|
|
MCFWriteReg32( dev, DMABSTRACTAUTO, 0x00000000 ); // Disable Autoexec.
|
|
|
|
// Different address, so we don't need to re-write all the program regs.
|
|
// lb x8,0(x9) // Read from the address.
|
|
MCFWriteReg32( dev, DMPROGBUF0, 0x00048403 ); // lb x8, 0(x9)
|
|
MCFWriteReg32( dev, DMPROGBUF1, 0x00100073 ); // c.ebreak
|
|
|
|
MCFWriteReg32( dev, BDMDATA0, address_to_read );
|
|
MCFWriteReg32( dev, DMCOMMAND, 0x00231009 ); // Copy data to x9
|
|
MCFWriteReg32( dev, DMCOMMAND, 0x00241000 ); // Only execute.
|
|
MCFWriteReg32( dev, DMCOMMAND, 0x00221008 ); // Read x8 into DATA0.
|
|
|
|
ret |= WaitForDoneOp( dev );
|
|
iss->currentstateval = -1;
|
|
|
|
uint32_t rr;
|
|
ret |= MCFReadReg32( dev, BDMDATA0, &rr );
|
|
*data = rr;
|
|
return ret;
|
|
}
|
|
|
|
static int ReadWord( struct SWIOState * iss, uint32_t address_to_read, uint32_t * data )
|
|
{
|
|
struct SWIOState * dev = iss;
|
|
int autoincrement = 1;
|
|
|
|
if( address_to_read == 0x40022010 || address_to_read == 0x4002200C ) // Don't autoincrement when checking flash flag.
|
|
autoincrement = 0;
|
|
|
|
if( iss->statetag != STTAG( "RDSQ" ) || address_to_read != iss->currentstateval || autoincrement != iss->autoincrement )
|
|
{
|
|
if( iss->statetag != STTAG( "RDSQ" ) || autoincrement != iss->autoincrement )
|
|
{
|
|
if( iss->statetag != STTAG( "WRSQ" ) )
|
|
{
|
|
int r = StaticUpdatePROGBUFRegs( dev );
|
|
if( r ) return r;
|
|
}
|
|
|
|
MCFWriteReg32( dev, DMABSTRACTAUTO, 0 ); // Disable Autoexec.
|
|
|
|
// c.lw x8,0(x11) // Pull the address from DATA1
|
|
// c.lw x9,0(x8) // Read the data at that location.
|
|
MCFWriteReg32( dev, DMPROGBUF0, 0x40044180 );
|
|
if( autoincrement )
|
|
{
|
|
// c.addi x8, 4
|
|
// c.sw x9, 0(x10) // Write back to DATA0
|
|
|
|
MCFWriteReg32( dev, DMPROGBUF1, 0xc1040411 );
|
|
}
|
|
else
|
|
{
|
|
// c.nop
|
|
// c.sw x9, 0(x10) // Write back to DATA0
|
|
|
|
MCFWriteReg32( dev, DMPROGBUF1, 0xc1040001 );
|
|
}
|
|
// c.sw x8, 0(x11) // Write addy to DATA1
|
|
// c.ebreak
|
|
MCFWriteReg32( dev, DMPROGBUF2, 0x9002c180 );
|
|
if( !iss->no_autoexec )
|
|
MCFWriteReg32( dev, DMABSTRACTAUTO, 1 ); // Enable Autoexec (not autoincrement)
|
|
iss->autoincrement = autoincrement;
|
|
}
|
|
|
|
MCFWriteReg32( dev, BDMDATA1, address_to_read );
|
|
if( !iss->no_autoexec )
|
|
MCFWriteReg32( dev, DMCOMMAND, 0x00240000 ); // Execute.
|
|
|
|
iss->statetag = STTAG( "RDSQ" );
|
|
iss->currentstateval = address_to_read;
|
|
}
|
|
|
|
if( iss->autoincrement )
|
|
iss->currentstateval += 4;
|
|
|
|
if( iss->no_autoexec ) {
|
|
MCFWriteReg32( dev, DMCOMMAND, 0x00240000 );
|
|
}
|
|
|
|
// Only an issue if we are curising along very fast.
|
|
int r = WaitForDoneOp( dev );
|
|
if( r ) return r;
|
|
|
|
r = MCFReadReg32( dev, BDMDATA0, data );
|
|
return r;
|
|
}
|
|
|
|
static int WriteByte( struct SWIOState * iss, uint32_t address_to_write, uint8_t data )
|
|
{
|
|
struct SWIOState * dev = iss;
|
|
int ret = 0;
|
|
iss->statetag = STTAG( "XXXX" );
|
|
|
|
MCFWriteReg32( dev, DMABSTRACTAUTO, 0x00000000 ); // Disable Autoexec.
|
|
|
|
// Different address, so we don't need to re-write all the program regs.
|
|
// sh x8,0(x9) // Write to the address.
|
|
MCFWriteReg32( dev, DMPROGBUF0, 0x00848023 ); // sb x8, 0(x9)
|
|
MCFWriteReg32( dev, DMPROGBUF1, 0x00100073 ); // c.ebreak
|
|
|
|
MCFWriteReg32( dev, BDMDATA0, address_to_write );
|
|
MCFWriteReg32( dev, DMCOMMAND, 0x00231009 ); // Copy data to x9
|
|
MCFWriteReg32( dev, BDMDATA0, data );
|
|
MCFWriteReg32( dev, DMCOMMAND, 0x00271008 ); // Copy data to x8, and execute program.
|
|
|
|
ret |= WaitForDoneOp( dev );
|
|
iss->currentstateval = -1;
|
|
return ret;
|
|
}
|
|
|
|
static int WriteWord( struct SWIOState * iss, uint32_t address_to_write, uint32_t data )
|
|
{
|
|
struct SWIOState * dev = iss;
|
|
|
|
int ret = 0;
|
|
|
|
int is_flash = 0;
|
|
if( ( address_to_write & 0xff000000 ) == 0x08000000 || ( address_to_write & 0x1FFF0000 ) == 0x1FFF0000 )
|
|
{
|
|
// Is flash.
|
|
is_flash = 1;
|
|
}
|
|
|
|
if( iss->statetag != STTAG( "WRSQ" ) || is_flash != iss->lastwriteflags )
|
|
{
|
|
int did_disable_req = 0;
|
|
|
|
if( iss->statetag != STTAG( "WRSQ" ) )
|
|
{
|
|
MCFWriteReg32( dev, DMABSTRACTAUTO, 0x00000000 ); // Disable Autoexec.
|
|
did_disable_req = 1;
|
|
|
|
if( iss->statetag != STTAG( "RDSQ" ) )
|
|
{
|
|
int r = StaticUpdatePROGBUFRegs( dev );
|
|
if( r ) return r;
|
|
}
|
|
|
|
// Different address, so we don't need to re-write all the program regs.
|
|
// c.lw x8,0(x10) // Get the value to write.
|
|
// c.lw x9,0(x11) // Get the address to write to.
|
|
MCFWriteReg32( dev, DMPROGBUF0, 0x41844100 );
|
|
// c.sw x8,0(x9) // Write to the address.
|
|
// c.addi x9, 4
|
|
MCFWriteReg32( dev, DMPROGBUF1, 0x0491c080 );
|
|
}
|
|
|
|
if( is_flash && iss->target_chip_type == CHIP_CH32V10x)
|
|
{
|
|
// Special 16 bytes buffer write sequence for CH32V103
|
|
MCFWriteReg32( dev, DMPROGBUF2, 0x0001c184 ); // c.sw x9,0(x11); c.nop;
|
|
MCFWriteReg32( dev, DMPROGBUF3, 0x9002e391 ); // c.bnez x15, 4; c.ebreak;
|
|
MCFWriteReg32( dev, DMPROGBUF4, 0x4200c254 ); // c.sw x13,4(x12); c.lw x8,0(x12);
|
|
MCFWriteReg32( dev, DMPROGBUF5, 0xfc758805 ); // c.andi x8, 1; c.bnez x8, -4;
|
|
MCFWriteReg32( dev, DMPROGBUF6, 0x90024781 ); // c.li x15, 0; c.ebreak;
|
|
}
|
|
else if( is_flash )
|
|
{
|
|
// A little weird - we need to wait until the buf load is done here to continue.
|
|
// x12 = 0x4002200C (FLASH_STATR)
|
|
//
|
|
// c254 c.sw x13,4(x12) // Acknowledge the page write. (BUT ONLY ON x035 / v003)
|
|
// otherwise c.nop
|
|
// 4200 c.lw x8,0(x12) // Start checking to see when buf load is done.
|
|
// 8809 c.andi x8, 2 // Only look at WR_BSY (seems to be rather undocumented)
|
|
// 8805 c.andi x8, 1 // Only look at BSY if we're not on a v30x / v20x
|
|
// fc75 c.bnez x8, -4
|
|
// c.ebreak
|
|
MCFWriteReg32( dev, DMPROGBUF2, 0x0001c184 );
|
|
MCFWriteReg32( dev, DMPROGBUF3,
|
|
(iss->target_chip_type == CHIP_CH32V003 || iss->target_chip_type == CHIP_CH32V00x
|
|
|| iss->target_chip_type == CHIP_CH32X03x || iss->target_chip_type == CHIP_CH32L10x
|
|
|| iss->target_chip_type == CHIP_CH641 || iss->target_chip_type == CHIP_CH643) ?
|
|
0x4200c254 : 0x42000001 );
|
|
|
|
MCFWriteReg32( dev, DMPROGBUF4,
|
|
(iss->target_chip_type == CHIP_CH32V20x || iss->target_chip_type == CHIP_CH32V30x ) ?
|
|
0xfc758809 : 0xfc758805 );
|
|
|
|
MCFWriteReg32( dev, DMPROGBUF5, 0x90029002 );
|
|
}
|
|
else
|
|
{
|
|
MCFWriteReg32( dev, DMPROGBUF2, 0x9002c184 ); // c.sw x9,0(x11); c.ebreak;
|
|
}
|
|
|
|
MCFWriteReg32( dev, BDMDATA1, address_to_write );
|
|
MCFWriteReg32( dev, BDMDATA0, data );
|
|
|
|
if( iss->no_autoexec )
|
|
{
|
|
MCFWriteReg32( dev, DMCOMMAND, 0x00240000 ); // Execute.
|
|
}
|
|
else if( did_disable_req )
|
|
{
|
|
MCFWriteReg32( dev, DMCOMMAND, 0x00240000 ); // Execute.
|
|
MCFWriteReg32( dev, DMABSTRACTAUTO, 1 ); // Enable Autoexec.
|
|
}
|
|
|
|
iss->lastwriteflags = is_flash;
|
|
|
|
iss->statetag = STTAG( "WRSQ" );
|
|
iss->currentstateval = address_to_write;
|
|
}
|
|
else
|
|
{
|
|
if( address_to_write != iss->currentstateval )
|
|
{
|
|
MCFWriteReg32( dev, BDMDATA1, address_to_write );
|
|
}
|
|
MCFWriteReg32( dev, BDMDATA0, data );
|
|
if( iss->no_autoexec )
|
|
{
|
|
MCFWriteReg32( dev, DMCOMMAND, 0x00240000 ); // Execute.
|
|
}
|
|
}
|
|
if( is_flash )
|
|
ret |= WaitForDoneOp( dev );
|
|
|
|
iss->currentstateval += 4;
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int UnlockFlash( struct SWIOState * iss )
|
|
{
|
|
struct SWIOState * dev = iss;
|
|
|
|
if( DetermineChipTypeAndSectorInfo( iss, NULL ) ) return -9;
|
|
|
|
uint32_t rw;
|
|
ReadWord( dev, 0x40022010, &rw ); // FLASH->CTLR = 0x40022010
|
|
if( rw & 0x8080 )
|
|
{
|
|
|
|
WriteWord( dev, 0x40022004, 0x45670123 ); // FLASH->KEYR = 0x40022004
|
|
WriteWord( dev, 0x40022004, 0xCDEF89AB );
|
|
WriteWord( dev, 0x40022008, 0x45670123 ); // OBKEYR = 0x40022008
|
|
WriteWord( dev, 0x40022008, 0xCDEF89AB );
|
|
WriteWord( dev, 0x40022024, 0x45670123 ); // MODEKEYR = 0x40022024
|
|
WriteWord( dev, 0x40022024, 0xCDEF89AB );
|
|
|
|
ReadWord( dev, 0x40022010, &rw ); // FLASH->CTLR = 0x40022010
|
|
if( rw & 0x8080 )
|
|
{
|
|
return -9;
|
|
}
|
|
}
|
|
iss->statetag = STTAG( "XXXX" );
|
|
iss->flash_unlocked = 1;
|
|
return 0;
|
|
}
|
|
|
|
static int EraseFlash( struct SWIOState * iss, uint32_t address, uint32_t length, int type )
|
|
{
|
|
struct SWIOState * dev = iss;
|
|
|
|
uint32_t rw;
|
|
|
|
if( !iss->flash_unlocked )
|
|
{
|
|
if( ( rw = UnlockFlash( iss ) ) )
|
|
return rw;
|
|
}
|
|
|
|
if( type == 1 )
|
|
{
|
|
// Whole-chip flash
|
|
iss->statetag = STTAG( "XXXX" );
|
|
BB_PRINTF_DEBUG( "Whole-chip erase\n" );
|
|
WriteWord( dev, 0x40022010, 0 ); // FLASH->CTLR = 0x40022010
|
|
WriteWord( dev, 0x40022010, FLASH_CTLR_MER );
|
|
WriteWord( dev, 0x40022010, CR_STRT_Set|FLASH_CTLR_MER );
|
|
if( WaitForFlash( dev ) ) return -13;
|
|
WriteWord( dev, 0x40022010, 0 ); // FLASH->CTLR = 0x40022010
|
|
}
|
|
else
|
|
{
|
|
// 16.4.7, Step 3: Check the BSY bit of the FLASH_STATR register to confirm that there are no other programming operations in progress.
|
|
// skip (we make sure at the end)
|
|
|
|
int chunk_to_erase = address;
|
|
|
|
while( chunk_to_erase < address + length )
|
|
{
|
|
if( WaitForFlash( dev ) ) return -14;
|
|
|
|
// Step 4: set PAGE_ER of FLASH_CTLR(0x40022010)
|
|
WriteWord( dev, 0x40022010, CR_PAGE_ER ); // Actually FTER // FLASH->CTLR = 0x40022010
|
|
|
|
// Step 5: Write the first address of the fast erase page to the FLASH_ADDR register.
|
|
WriteWord( dev, 0x40022014, chunk_to_erase ); // FLASH->ADDR = 0x40022014
|
|
|
|
// Step 6: Set the STAT bit of FLASH_CTLR register to '1' to initiate a fast page erase (64 bytes) action.
|
|
WriteWord( dev, 0x40022010, CR_STRT_Set|CR_PAGE_ER ); // FLASH->CTLR = 0x40022010
|
|
if( WaitForFlash( dev ) ) return -15;
|
|
|
|
WriteWord( dev, 0x40022010, 0 ); // FLASH->CTLR = 0x40022010 (Disable any pending ops)
|
|
chunk_to_erase+=64;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int WriteBlock( struct SWIOState * iss, uint32_t address_to_write, uint8_t * blob, uint8_t len, uint8_t erase )
|
|
{
|
|
struct SWIOState * dev = iss;
|
|
|
|
if( DetermineChipTypeAndSectorInfo( iss, NULL ) ) return -9;
|
|
|
|
const int blob_size = len;
|
|
uint32_t wp = address_to_write;
|
|
uint32_t ew = wp + blob_size;
|
|
int group = -1;
|
|
int is_flash = 0;
|
|
int rw = 0;
|
|
|
|
if( (address_to_write & 0xff000000) == 0x08000000 || (address_to_write & 0xff000000) == 0x00000000 || (address_to_write & 0x1FFF0000) == 0x1FFF0000 )
|
|
{
|
|
// Need to unlock flash.
|
|
// Flash reg base = 0x40022000,
|
|
// FLASH_MODEKEYR => 0x40022024
|
|
// FLASH_KEYR => 0x40022004
|
|
|
|
if( !iss->flash_unlocked )
|
|
{
|
|
if( ( rw = UnlockFlash( dev ) ) )
|
|
return rw;
|
|
}
|
|
|
|
is_flash = 1;
|
|
|
|
if( erase )
|
|
{
|
|
// Only erase on first block in sector.
|
|
int block_in_sector = (wp & ( iss->sectorsize - 1 )) / blob_size;
|
|
int is_first_block = block_in_sector == 0;
|
|
|
|
if( is_first_block )
|
|
{
|
|
rw = EraseFlash( dev, address_to_write, blob_size, 0 );
|
|
if( rw ) return rw;
|
|
// 16.4.6 Main memory fast programming, Step 5
|
|
//if( WaitForFlash( dev ) ) return -11;
|
|
//WriteWord( dev, 0x40022010, FLASH_CTLR_BUF_RST );
|
|
//if( WaitForFlash( dev ) ) return -11;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* General Note:
|
|
Most flash operations take about 3ms to complete :(
|
|
*/
|
|
|
|
while( wp < ew )
|
|
{
|
|
if( is_flash )
|
|
{
|
|
group = (wp & 0xffffffc0);
|
|
|
|
int block_in_sector = (group & ( iss->sectorsize - 1 )) / blob_size;
|
|
int is_first_block = block_in_sector == 0;
|
|
int is_last_block = block_in_sector == (iss->sectorsize / blob_size - 1 );
|
|
|
|
if( is_first_block )
|
|
{
|
|
if( dev->target_chip_type == CHIP_CH32V20x || dev->target_chip_type == CHIP_CH32V30x )
|
|
{
|
|
// No bufrst on v20x, v30x
|
|
WaitForFlash( dev );
|
|
WriteWord( dev, 0x40022010, CR_PAGE_PG ); // THIS IS REQUIRED, (intptr_t)&FLASH->CTLR = 0x40022010
|
|
//FTPG == CR_PAGE_PG == ((uint32_t)0x00010000)
|
|
}
|
|
else
|
|
{
|
|
// V003, x035, maybe more.
|
|
WriteWord( dev, 0x40022010, CR_PAGE_PG ); // THIS IS REQUIRED, (intptr_t)&FLASH->CTLR = 0x40022010
|
|
WriteWord( dev, 0x40022010, CR_BUF_RST | CR_PAGE_PG ); // (intptr_t)&FLASH->CTLR = 0x40022010
|
|
}
|
|
WaitForFlash( dev );
|
|
}
|
|
|
|
int j;
|
|
for( j = 0; j < blob_size/4; j++ )
|
|
{
|
|
int index = (wp-address_to_write);
|
|
uint32_t data = 0xffffffff;
|
|
if( index + 3 < blob_size )
|
|
{
|
|
memcpy( &data, &blob[index], 4 );
|
|
}
|
|
else if( (int32_t)(blob_size - index) > 0 )
|
|
{
|
|
memcpy( &data, &blob[index], blob_size - index );
|
|
}
|
|
if( iss->target_chip_type == CHIP_CH32V10x && !((j+1)&3) )
|
|
{
|
|
// Signal to WriteWord that we need to do a buffer load
|
|
MCFWriteReg32( dev, BDMDATA0, 1 );
|
|
MCFWriteReg32( dev, DMCOMMAND, 0x0023100f );
|
|
}
|
|
WriteWord( dev, wp, data );
|
|
wp += 4;
|
|
}
|
|
|
|
|
|
if( ( iss->target_chip_type == CHIP_CH32V20x || iss->target_chip_type == CHIP_CH32V30x ) )
|
|
{
|
|
if( is_last_block )
|
|
{
|
|
WriteWord( dev, 0x40022010, CR_PAGE_PG | (1<<21) ); // Page Start
|
|
if( WaitForFlash( dev ) ) return -13;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Datasheet says the x03x needs to have this called every group-of-16, but that's not true, it should be every 16-words.
|
|
WriteWord( dev, 0x40022014, group ); //0x40022014 -> FLASH->ADDR
|
|
WriteWord( dev, 0x40022010, CR_PAGE_PG|CR_STRT_Set ); // 0x40022010 -> FLASH->CTLR
|
|
if( WaitForFlash( dev ) ) return -16;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
int index = (wp-address_to_write);
|
|
uint32_t data = 0xffffffff;
|
|
if( index + 3 < blob_size )
|
|
{
|
|
memcpy( &data, &blob[index], 4 );
|
|
}
|
|
else if( (int32_t)(blob_size - index) > 0 )
|
|
{
|
|
memcpy( &data, &blob[index], blob_size - index );
|
|
}
|
|
WriteWord( dev, wp, data );
|
|
wp += 4;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
// Polls up to 7 bytes of printf, and can leave a 7-bit flag for the CH32V003.
|
|
static int PollTerminal( struct SWIOState * iss, uint8_t * buffer, int maxlen, uint32_t leavevalA, uint32_t leavevalB )
|
|
{
|
|
struct SWIOState * dev = iss;
|
|
|
|
int r;
|
|
uint32_t rr;
|
|
if( iss->statetag != STTAG( "TERM" ) )
|
|
{
|
|
MCFWriteReg32( dev, DMABSTRACTAUTO, 0x00000000 ); // Disable Autoexec.
|
|
iss->statetag = STTAG( "TERM" );
|
|
}
|
|
r = MCFReadReg32( dev, BDMDATA0, &rr );
|
|
if( r < 0 ) return r;
|
|
|
|
if( maxlen < 8 ) return -9;
|
|
|
|
// BDMDATA1:
|
|
// bit 7 = host-acknowledge.
|
|
if( rr & 0x80 )
|
|
{
|
|
int ret = 0;
|
|
int num_printf_chars = (rr & 0xf)-4;
|
|
|
|
if( num_printf_chars > 0 && num_printf_chars <= 7)
|
|
{
|
|
if( num_printf_chars > 3 )
|
|
{
|
|
uint32_t r2;
|
|
r = MCFReadReg32( dev, BDMDATA1, &r2 );
|
|
memcpy( buffer+3, &r2, num_printf_chars - 3 );
|
|
}
|
|
int firstrem = num_printf_chars;
|
|
if( firstrem > 3 ) firstrem = 3;
|
|
memcpy( buffer, ((uint8_t*)&rr)+1, firstrem );
|
|
buffer[num_printf_chars] = 0;
|
|
ret = num_printf_chars;
|
|
}
|
|
if( leavevalA )
|
|
{
|
|
MCFWriteReg32( dev, BDMDATA1, leavevalB );
|
|
}
|
|
MCFWriteReg32( dev, BDMDATA0, leavevalA ); // Write that we acknowledge the data.
|
|
return ret;
|
|
}
|
|
else
|
|
{
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
#endif // _CH32V003_SWIO_H
|