Skip to content

Instantly share code, notes, and snippets.

@monte-monte
Created May 30, 2024 12:32
Show Gist options
  • Save monte-monte/93ed992b8e96dd4345cc7e4ec1891141 to your computer and use it in GitHub Desktop.
Save monte-monte/93ed992b8e96dd4345cc7e4ec1891141 to your computer and use it in GitHub Desktop.
Updated ch32v003_swio.h to use with ESP32-C3/C6 and modern ESP-IDF 5.1+
// CH32V003 SWIO minimal reference implementation for bit banged IO
// on the ESP32-S2.
//
// Copyright 2023 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 perfect. Expect to tweak some things.
// DoSongAndDanceToEnterPgmMode is almost completely untested.
// This is the weird song-and-dance that the WCH LinkE does when
// connecting to a CH32V003 part with unknown state. This is probably
// incorrect, but isn't really needed unless things get really cursed.
#ifndef _CH32V003_SWIO_H
#define _CH32V003_SWIO_H
#define MAX_IN_TIMEOUT 1000
// 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.
#define R_GLITCH_HIGH
// You should interface to this file via these functions
struct SWIOState
{
// Set these before calling any functions
int t1coeff;
int pinmask;
// Zero the rest of the structure.
uint32_t statetag;
uint32_t lastwriteflags;
uint32_t currentstateval;
uint32_t flash_unlocked;
uint32_t autoincrement;
};
#define STTAG( x ) (*((uint32_t*)(x)))
#define IRAM IRAM_ATTR
static int DoSongAndDanceToEnterPgmMode( struct SWIOState * state );
static void MCFWriteReg32( struct SWIOState * state, uint8_t command, uint32_t value ) IRAM;
static int MCFReadReg32( struct SWIOState * state, uint8_t command, uint32_t * value ) IRAM;
// More advanced functions built on lower level PHY.
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 Write64Block( struct SWIOState * iss, uint32_t address_to_write, uint8_t * data );
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 DMDATA0 0x04
#define DMDATA1 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
#define FLASH_STATR_WRPRTERR ((uint8_t)0x10)
#define CR_PAGE_PG ((uint32_t)0x00010000)
#define CR_BUF_LOAD ((uint32_t)0x00040000)
#define FLASH_CTLR_MER ((uint16_t)0x0004) /* Mass Erase */
#define CR_STRT_Set ((uint32_t)0x00000040)
#define CR_PAGE_ER ((uint32_t)0x00020000)
#define CR_BUF_RST ((uint32_t)0x00080000)
static inline void Send1Bit( int t1coeff, int pinmask ) IRAM;
static inline void Send0Bit( int t1coeff, int pinmask ) IRAM;
static inline int ReadBit( struct SWIOState * state ) IRAM;
static inline void PrecDelay( int delay )
{
#ifdef __XTENSA__
asm volatile(
"1: addi %[delay], %[delay], -1\n"
" bbci %[delay], 31, 1b\n" : [delay]"+r"(delay) );
#endif
#ifdef __riscv
asm volatile(
"1: addi %[delay], %[delay], -1\n"
"bne %[delay], x0, 1b\n" :[delay]"+r"(delay) );
#endif
}
// TODO: Add continuation (bypass) functions.
// TODO: Consider adding parity bit (though it seems rather useless)
// All three bit functions assume bus state will be in
// GPIO.out_w1ts.val = pinmask;
// GPIO.enable_w1ts.val = pinmask;
// when they are called.
static inline void Send1Bit( int t1coeff, int pinmask )
{
// Low for a nominal period of time.
// High for a nominal period of time.
GPIO.out_w1tc.val = pinmask;
PrecDelay( t1coeff );
GPIO.out_w1ts.val = pinmask;
PrecDelay( t1coeff );
}
static inline void Send0Bit( int t1coeff, int pinmask )
{
// Low for a LONG period of time.
// High for a nominal period of time.
int longwait = t1coeff*4;
GPIO.out_w1tc.val = pinmask;
PrecDelay( longwait );
GPIO.out_w1ts.val = pinmask;
PrecDelay( t1coeff );
}
// returns 0 if 0
// returns 1 if 1
// returns 2 if timeout.
static inline int ReadBit( struct SWIOState * state )
{
int t1coeff = state->t1coeff;
int pinmask = state->pinmask;
// Drive low, very briefly. Let drift high.
// See if CH32V003 is holding low.
int timeout = 0;
int ret = 0;
int medwait = t1coeff * 2;
GPIO.out_w1tc.val = pinmask;
PrecDelay( t1coeff );
GPIO.enable_w1tc.val = pinmask;
GPIO.out_w1ts.val = pinmask;
#ifdef R_GLITCH_HIGH
int halfwait = t1coeff / 2;
PrecDelay( halfwait );
GPIO.enable_w1ts.val = pinmask;
GPIO.enable_w1tc.val = pinmask;
PrecDelay( halfwait );
#else
PrecDelay( medwait );
#endif
ret = GPIO.in.val;
#ifdef R_GLITCH_HIGH
if( !(ret & pinmask) )
{
// Wait if still low.
PrecDelay( medwait );
GPIO.enable_w1ts.val = pinmask;
GPIO.enable_w1tc.val = pinmask;
}
#endif
for( timeout = 0; timeout < MAX_IN_TIMEOUT; timeout++ )
{
if( GPIO.in.val & pinmask )
{
GPIO.enable_w1ts.val = pinmask;
int fastwait = t1coeff / 2;
PrecDelay( fastwait );
return !!(ret & pinmask);
}
}
// Force high anyway so, though hazarded, we can still move along.
GPIO.enable_w1ts.val = pinmask;
return 2;
}
static void MCFWriteReg32( struct SWIOState * state, uint8_t command, uint32_t value )
{
int t1coeff = state->t1coeff;
int pinmask = state->pinmask;
GPIO.out_w1ts.val = pinmask;
GPIO.enable_w1ts.val = pinmask;
portDISABLE_INTERRUPTS();
Send1Bit( t1coeff, pinmask );
uint32_t mask;
for( mask = 1<<6; mask; mask >>= 1 )
{
if( command & mask )
Send1Bit(t1coeff, pinmask);
else
Send0Bit(t1coeff, pinmask);
}
Send1Bit( t1coeff, pinmask );
for( mask = 1<<31; mask; mask >>= 1 )
{
if( value & mask )
Send1Bit(t1coeff, pinmask);
else
Send0Bit(t1coeff, pinmask);
}
portENABLE_INTERRUPTS();
esp_rom_delay_us(8); // Sometimes 2 is too short.
}
// returns 0 if no error, otherwise error.
static int MCFReadReg32( struct SWIOState * state, uint8_t command, uint32_t * value )
{
int t1coeff = state->t1coeff;
int pinmask = state->pinmask;
GPIO.out_w1ts.val = pinmask;
GPIO.enable_w1ts.val = pinmask;
portDISABLE_INTERRUPTS();
Send1Bit( t1coeff, pinmask );
int i;
uint32_t mask;
for( mask = 1<<6; mask; mask >>= 1 )
{
if( command & mask )
Send1Bit(t1coeff, pinmask);
else
Send0Bit(t1coeff, pinmask);
}
Send0Bit( t1coeff, pinmask );
uint32_t rval = 0;
for( i = 0; i < 32; i++ )
{
rval <<= 1;
int r = ReadBit( state );
if( r == 1 )
rval |= 1;
if( r == 2 )
{
portENABLE_INTERRUPTS();
return -1;
}
}
*value = rval;
portENABLE_INTERRUPTS();
esp_rom_delay_us(8); // Sometimes 2 is too short.
return 0;
}
static inline void ExecuteTimePairs( struct SWIOState * state, const uint16_t * pairs, int numpairs, int iterations )
{
int t1coeff = state->t1coeff;
int pinmask = state->pinmask;
int j, k;
for( k = 0; k < iterations; k++ )
{
const uint16_t * tp = pairs;
for( j = 0; j < numpairs; j++ )
{
int t1v = t1coeff * (*(tp++))-1;
GPIO.out_w1tc.val = pinmask;
PrecDelay( t1v );
GPIO.out_w1ts.val = pinmask;
t1v = t1coeff * (*(tp++))-1;
PrecDelay( t1v );
}
}
}
// Returns 0 if chips is present
// Returns 1 if chip is not present
// Returns 2 if there was a bus fault.
static int DoSongAndDanceToEnterPgmMode( struct SWIOState * state )
{
int pinmask = state->pinmask;
// XXX MOSTLY UNTESTED!!!!
static const uint16_t timepairs1[] ={
32, 12, // 8.2us / 3.1us
36, 12, // 9.2us / 3.1us
392, 366 // 102.3us / 95.3us
};
// Repeat for 199x
static const uint16_t timepairs2[] ={
15, 12, // 4.1us / 3.1us
32, 12, // 8.2us / 3.1us
36, 12, // 9.3us / 3.1us
392, 366 // 102.3us / 95.3us
};
// Repeat for 10x
static const uint16_t timepairs3[] ={
15, 807, // 4.1us / 210us
24, 8, // 6.3us / 2us
32, 8, // 8.4us / 2us
24, 10, // 6.2us / 2.4us
20, 8, // 5.2us / 2.1us
239, 8, // 62.3us / 2.1us
32, 20, // 8.4us / 5.3us
8, 32, // 2.2us / 8.4us
24, 8, // 6.3us / 2.1us
32, 8, // 8.4us / 2.1us
26, 7, // 6.9us / 1.7us
20, 8, // 5.2us / 2.1us
239, 8, // 62.3us / 2.1us
32, 20, // 8.4us / 5.3us
8, 22, // 2us / 5.3us
24, 8, // 6.3us 2.1us
24, 8, // 6.3us 2.1us
31, 6, // 8us / 1.6us
25, 307, // 6.6us / 80us
};
portDISABLE_INTERRUPTS();
ExecuteTimePairs( state, timepairs1, 3, 1 );
ExecuteTimePairs( state, timepairs2, 4, 199 );
ExecuteTimePairs( state, timepairs3, 19, 10 );
// THIS IS WRONG!!!! THIS IS NOT A PRESENT BIT.
int present = ReadBit( state ); // Actually here t1coeff, for this should be *= 8!
GPIO.enable_w1ts.val = pinmask;
GPIO.out_w1tc.val = pinmask;
esp_rom_delay_us( 2000 );
GPIO.out_w1ts.val = pinmask;
portENABLE_INTERRUPTS();
esp_rom_delay_us( 1 );
return present;
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Higher level functions
static int WaitForFlash( struct SWIOState * iss )
{
struct SWIOState * dev = iss;
uint32_t rw, timeout = 0;
do
{
rw = 0;
ReadWord( dev, 0x4002200C, &rw ); // FLASH_STATR => 0x4002200C
} while( (rw & 1) && timeout++ < 200); // 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;
struct SWIOState * dev = iss;
do
{
r = MCFReadReg32( dev, DMABSTRACTCS, &rrv );
if( r ) return r;
}
while( rrv & (1<<12) );
if( (rrv >> 8 ) & 7 )
{
MCFWriteReg32( dev, DMABSTRACTCS, 0x00000700 );
ret = -33;
}
return ret;
}
static void StaticUpdatePROGBUFRegs( struct SWIOState * dev )
{
MCFWriteReg32( dev, DMDATA0, 0xe00000f4 ); // DATA0's location in memory.
MCFWriteReg32( dev, DMCOMMAND, 0x0023100a ); // Copy data to x10
MCFWriteReg32( dev, DMDATA0, 0xe00000f8 ); // DATA1's location in memory.
MCFWriteReg32( dev, DMCOMMAND, 0x0023100b ); // Copy data to x11
MCFWriteReg32( dev, DMDATA0, 0x40022010 ); //FLASH->CTLR
MCFWriteReg32( dev, DMCOMMAND, 0x0023100c ); // Copy data to x12
MCFWriteReg32( dev, DMDATA0, CR_PAGE_PG|CR_BUF_LOAD);
MCFWriteReg32( dev, DMCOMMAND, 0x0023100d ); // Copy data to x13
}
static void ResetInternalProgrammingState( struct SWIOState * iss )
{
iss->statetag = 0;
iss->lastwriteflags = 0;
iss->currentstateval = 0;
iss->flash_unlocked = 0;
iss->autoincrement = 0;
}
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 )
{
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->statetag != STTAG( "WRSQ" ) )
{
StaticUpdatePROGBUFRegs( dev );
}
MCFWriteReg32( dev, DMABSTRACTAUTO, 1 ); // Enable Autoexec (not autoincrement)
iss->autoincrement = autoincrement;
}
MCFWriteReg32( dev, DMDATA1, address_to_read );
MCFWriteReg32( dev, DMCOMMAND, 0x00241000 ); // Only execute.
iss->statetag = STTAG( "RDSQ" );
iss->currentstateval = address_to_read;
WaitForDoneOp( dev );
}
if( iss->autoincrement )
iss->currentstateval += 4;
return MCFReadReg32( dev, DMDATA0, data );
}
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 & 0x1FFFF800 ) == 0x1FFFF000 )
{
// 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;
// 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.
MCFWriteReg32( dev, DMPROGBUF0, 0xc0804184 );
// c.addi x9, 4
// c.sw x9,0(x11)
MCFWriteReg32( 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
MCFWriteReg32( dev, DMPROGBUF2, 0x9002c214 );
}
else
{
MCFWriteReg32( dev, DMPROGBUF2, 0x00019002 ); // c.ebreak
}
}
MCFWriteReg32( dev, DMDATA1, address_to_write );
MCFWriteReg32( dev, DMDATA0, data );
if( did_disable_req )
{
MCFWriteReg32( dev, DMCOMMAND, 0x00271008 ); // Copy data to x8, and execute program.
MCFWriteReg32( dev, DMABSTRACTAUTO, 1 ); // Enable Autoexec.
}
iss->lastwriteflags = is_flash;
iss->statetag = STTAG( "WRSQ" );
iss->currentstateval = address_to_write;
if( is_flash )
ret |= WaitForDoneOp( dev );
}
else
{
if( address_to_write != iss->currentstateval )
{
MCFWriteReg32( dev, DMABSTRACTAUTO, 0 ); // Disable Autoexec.
MCFWriteReg32( dev, DMDATA1, address_to_write );
MCFWriteReg32( dev, DMABSTRACTAUTO, 1 ); // Enable Autoexec.
}
MCFWriteReg32( dev, DMDATA0, data );
if( is_flash )
{
// XXX TODO: This likely can be a very short delay.
// XXX POSSIBLE OPTIMIZATION REINVESTIGATE.
ret |= WaitForDoneOp( dev );
}
else
{
ret |= WaitForDoneOp( dev );
}
}
iss->currentstateval += 4;
return 0;
}
static int UnlockFlash( struct SWIOState * iss )
{
struct SWIOState * dev = iss;
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->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" );
printf( "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 Write64Block( struct SWIOState * iss, uint32_t address_to_write, uint8_t * blob )
{
struct SWIOState * dev = iss;
int blob_size = 64;
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 & 0x1FFFF800) == 0x1FFFF000 )
{
// 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;
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);
WriteWord( dev, 0x40022010, CR_PAGE_PG ); // THIS IS REQUIRED, (intptr_t)&FLASH->CTLR = 0x40022010 (PG Performs quick page programming operations.)
WriteWord( dev, 0x40022010, CR_BUF_RST | CR_PAGE_PG ); // (intptr_t)&FLASH->CTLR = 0x40022010
int j;
for( j = 0; j < 16; j++ )
{
int index = (wp-address_to_write);
uint32_t data = 0xffffffff;
if( index + 3 < blob_size )
data = ((uint32_t*)blob)[index/4];
else if( (int32_t)(blob_size - index) > 0 )
{
memcpy( &data, &blob[index], blob_size - index );
}
WriteWord( dev, wp, data );
//if( (rw = WaitForFlash( dev ) ) ) return rw;
wp += 4;
}
WriteWord( dev, 0x40022014, group );
WriteWord( dev, 0x40022010, CR_PAGE_PG|CR_STRT_Set ); // R32_FLASH_CTLR
if( (rw = WaitForFlash( dev ) ) ) return rw;
}
else
{
int index = (wp-address_to_write);
uint32_t data = 0xffffffff;
if( index + 3 < blob_size )
data = ((uint32_t*)blob)[index/4];
else if( (int32_t)(blob_size - index) > 0 )
memcpy( &data, &blob[index], blob_size - index );
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, DMDATA0, &rr );
if( r < 0 ) return r;
if( maxlen < 8 ) return -9;
// DMDATA1:
// 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, 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;
ret = num_printf_chars;
}
if( leavevalA )
{
MCFWriteReg32( dev, DMDATA1, leavevalB );
}
MCFWriteReg32( dev, DMDATA0, leavevalA ); // Write that we acknowledge the data.
return ret;
}
else
{
return 0;
}
}
#endif // _CH32V003_SWIO_H
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment