diff --git a/README.md b/README.md index 95d0ace963f9abe23b676beee54473ed2a37da40..0b66a4cbdc0013a89bf11c8b607de4e762c0fca1 100644 --- a/README.md +++ b/README.md @@ -162,3 +162,4 @@ You can open a github ticket or join my Discord in the #ch32v003fun channel. htt * http://www.wch-ic.com/downloads/CH32V003RM_PDF.html Technical Reference Manual * http://www.wch-ic.com/downloads/CH32V003DS0_PDF.html Datasheet * https://github.com/CaiB/CH32V003-Architecture-Exploration/blob/main/InstructionTypes.md Details for the compressed ISA on this chip. + * The CH32V003 has xv extensions, you can use this customized version of rvcodec.js to work with its opcodes: https://xw.macyler.moe/ diff --git a/ch32v003fun/xw_ext.inc b/ch32v003fun/xw_ext.inc new file mode 100644 index 0000000000000000000000000000000000000000..ca210702fc69d9d31b87d9f69ddaf004c17e0b0c --- /dev/null +++ b/ch32v003fun/xw_ext.inc @@ -0,0 +1,57 @@ +/* +Encoder for some of the proprietary 'XW' RISC-V instructions present on the QingKe RV32 processor. +Examples: + XW_C_LBU(a3, a1, 27); // c.xw.lbu a3, 27(a1) + XW_C_SB(a0, s0, 13); // c.xw.sb a0, 13(s0) + + XW_C_LHU(a5, a5, 38); // c.xw.lhu a5, 38(a5) + XW_C_SH(a2, s1, 14); // c.xw.sh a2, 14(s1) +*/ + +// Let us do some compile-time error checking. +#define ASM_ASSERT(COND) .if (!(COND)); .err; .endif + +// Integer encodings of the possible compressed registers. +#define C_s0 0 +#define C_s1 1 +#define C_a0 2 +#define C_a1 3 +#define C_a2 4 +#define C_a3 5 +#define C_a4 6 +#define C_a5 7 + +// register to encoding +#define REG2I(X) (C_ ## X) + +// XW opcodes +#define XW_OP_LBUSP 0b1000000000000000 +#define XW_OP_STSP 0b1000000001000000 + +#define XW_OP_LHUSP 0b1000000000100000 +#define XW_OP_SHSP 0b1000000001100000 + +#define XW_OP_LBU 0b0010000000000000 +#define XW_OP_SB 0b1010000000000000 + +#define XW_OP_LHU 0b0010000000000010 +#define XW_OP_SH 0b1010000000000010 + +// The two different XW encodings supported at the moment. +#define XW_ENCODE1(OP, R1, R2, IMM) ASM_ASSERT((IMM) >= 0 && (IMM) < 32); .2byte ((OP) | (REG2I(R1) << 2) | (REG2I(R2) << 7) | \ + (((IMM) & 0b1) << 12) | (((IMM) & 0b110) << (5 - 1)) | (((IMM) & 0b11000) << (10 - 3))) + +#define XW_ENCODE2(OP, R1, R2, IMM) ASM_ASSERT((IMM) >= 0 && (IMM) < 32); .2byte ((OP) | (REG2I(R1) << 2) | (REG2I(R2) << 7) | \ + (((IMM) & 0b11) << 5) | (((IMM) & 0b11100) << (10 - 2)) + +// Compressed load byte, zero-extend result +#define XW_C_LBU(RD, RS, IMM) XW_ENCODE1(XW_OP_LBU, RD, RS, IMM) + +// Compressed store byte +#define XW_C_SB(RS1, RS2, IMM) XW_ENCODE1(XW_OP_SB, RS1, RS2, IMM) + +// Compressed load half, zero-extend result +#define XW_C_LHU(RD, RS, IMM) ASM_ASSERT(((IMM) & 1) == 0); XW_ENCODE2(XW_OP_LHU, RD, RS, ((IMM) >> 1))) + +// Compressed store half +#define XW_C_SH(RS1, RS2, IMM) ASM_ASSERT(((IMM) & 1) == 0); XW_ENCODE2(XW_OP_SH, RS1, RS2, ((IMM) >> 1))) diff --git a/examples/optiondata/Makefile b/examples/optiondata/Makefile new file mode 100644 index 0000000000000000000000000000000000000000..5ad2b8e5ed62673531a142e2a44fa1e7ebf78b08 --- /dev/null +++ b/examples/optiondata/Makefile @@ -0,0 +1,8 @@ +all : flash + +TARGET:=optiondata + +include ../../ch32v003fun/ch32v003fun.mk + +flash : cv_flash +clean : cv_clean diff --git a/examples/optiondata/README.md b/examples/optiondata/README.md new file mode 100644 index 0000000000000000000000000000000000000000..840aee6057895eb3696b9e18407e37190a9f87cf --- /dev/null +++ b/examples/optiondata/README.md @@ -0,0 +1,11 @@ +# Write to the user fields in the Option Data flash + +This is a simple example code for writing to the two 8-bit data0/data1 bytes that are a part of the Flash "User-selected words" (aka Option bytes). + +To reduce the code footpint no timeouts are implemented in the code. It simply waits for the busy-flag to be cleaed. The two wait-loops could be replaced with a Delay_Ms(10) if speed is not an issue. + +The two bytes could be used for remembering user settings between power-offs. + +After flashing you can test it with the command +`../../minichlink/minichlink -a -b -T` +that will Halt, Reboot and enter Terminal mode. For each run the count will increment by one. diff --git a/examples/optiondata/optiondata.c b/examples/optiondata/optiondata.c new file mode 100644 index 0000000000000000000000000000000000000000..a3c8f273c0a07d9416ff30963cacea91651b6b17 --- /dev/null +++ b/examples/optiondata/optiondata.c @@ -0,0 +1,74 @@ +// +// Example code for writing to the two 8-bit data0/data1 bytes that are +// a part of the Flash "User-selected words" (aka Option bytes). +// To reduce the code footpint no timeouts are implemented in the code. It simply +// waits for the busy-flag to be cleaed. The two wait-loops could be replaced +// with a Delay_Ms(10) if speed is not an issue. +// +// June 13, 2023 Mats Engstrom (github.com/mengstr) +// + +#define SYSTEM_CORE_CLOCK 48000000 + +#include "ch32v003fun.h" +#include <stdio.h> +void FlashOptionData(uint8_t data0, uint8_t data1) { + volatile uint16_t hold[6]; // array to hold current values while erasing + + // The entire 64 byte data block of the "User-selected words" will be erased + // so we need to keep a copy of the content for re-writing after erase. + // Save a few (20) bytes code space by moving 32 bits at a time. + // hold[0]=OB->RDPR; + // hold[1]=OB->USER; + // hold[2]=data0; + // hold[3]=data1; + // hold[4]=OB->WRPR0; + // hold[5]=OB->WRPR1; + uint32_t *hold32p=(uint32_t *)hold; + uint32_t *ob32p=(uint32_t *)OB_BASE; + hold32p[0]=ob32p[0]; // Copy RDPR and USER + hold32p[1]=data0+(data1<<16); // Copy in the two Data values to be written + hold32p[2]=ob32p[2]; // Copy WRPR0 and WEPR1 + + // Unlock both the general Flash and the User-selected words + FLASH->KEYR = FLASH_KEY1; + FLASH->KEYR = FLASH_KEY2; + FLASH->OBKEYR = FLASH_KEY1; + FLASH->OBKEYR = FLASH_KEY2; + + FLASH->CTLR |= CR_OPTER_Set; // OBER RW Perform user-selected word erasure + FLASH->CTLR |= CR_STRT_Set; // STRT RW1 Start. Set 1 to start an erase action,hw automatically clears to 0 + while (FLASH->STATR & FLASH_BUSY); // Wait for flash operation to be done + FLASH->CTLR &= CR_OPTER_Reset; // Disable erasure mode + + // Write the held values back one-by-one + FLASH->CTLR |= CR_OPTPG_Set; // OBG RW Perform user-selected word programming + uint16_t *ob16p=(uint16_t *)OB_BASE; + for (int i=0;i<sizeof(hold)/sizeof(hold[0]); i++) { + ob16p[i]=hold[i]; + while (FLASH->STATR & FLASH_BUSY); // Wait for flash operation to be done + } + FLASH->CTLR &= CR_OPTPG_Reset; // Disable programming mode + + FLASH->CTLR|=CR_LOCK_Set; // Lock flash memories again + + return; +} + + + +// +// +// +int main() { + SystemInit48HSI(); + SetupDebugPrintf(); + Delay_Ms(250); + + uint8_t bootcnt=OB->Data0; + bootcnt++; + FlashOptionData(bootcnt,0); + printf("Boot count is %d\n",bootcnt); + + for(;;); +} diff --git a/minichlink/minichlink.c b/minichlink/minichlink.c index 35b5a4720a6b9e1f88c9eaeb8bc3a3dc77eba821..4444013cc9d0f7f76679bf39f09db98d6eec1510 100644 --- a/minichlink/minichlink.c +++ b/minichlink/minichlink.c @@ -13,6 +13,7 @@ #if defined(WINDOWS) || defined(WIN32) || defined(_WIN32) #define DISABLE_ARDULINK +void Sleep(uint32_t dwMilliseconds); #else #include <unistd.h> #endif @@ -201,6 +202,20 @@ keep_going: else goto unimplemented; break; + case 'p': + if( MCF.HaltMode ) MCF.HaltMode( dev, 0 ); + if( MCF.ConfigureReadProtection ) + MCF.ConfigureReadProtection( dev, 0 ); + else + goto unimplemented; + break; + case 'P': + if( MCF.HaltMode ) MCF.HaltMode( dev, 0 ); + if( MCF.ConfigureReadProtection ) + MCF.ConfigureReadProtection( dev, 1 ); + else + goto unimplemented; + break; case 'G': case 'T': { @@ -315,7 +330,7 @@ keep_going: goto unimplemented; break; } - case 'p': + case 'i': { if( MCF.PrintChipInfo ) MCF.PrintChipInfo( dev ); @@ -553,12 +568,13 @@ help: 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 (UNTESTED)\n" ); -// fprintf( stderr, " -p Disable Read Protection (UNTESTED)\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" ); @@ -1786,19 +1802,25 @@ int DefaultConfigureNRSTAsGPIO( void * dev, int one_if_yes_gpio ) #endif } +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, 5 ); if( MCF.ReadWord( dev, 0x1FFFF800, ® ) ) goto fail; - printf( "USER/RDPR: %08x\n", reg ); -/* if( MCF.ReadWord( dev, 0x1FFFF804, ® ) ) goto fail; - printf( "NDATA: %08x\n", reg ); + 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( "WRPR01: %08x\n", reg ); + printf( "WRPR1/WRPR0: %04x/%04x\n", reg>>16, reg&0xFFFF ); if( MCF.ReadWord( dev, 0x1FFFF80c, ® ) ) goto fail; - printf( "WRPR23: %08x\n", reg );*/ + 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; diff --git a/minichlink/minichlink.h b/minichlink/minichlink.h index b212d85223ce9252923640a28cf7276c6af44a18..6a974ff230635fa24a2a769512d61c356dc3013c 100644 --- a/minichlink/minichlink.h +++ b/minichlink/minichlink.h @@ -23,6 +23,7 @@ struct MiniChlinkFunctions int (*HaltMode)( void * dev, int mode ); //0 for halt, 1 for reset, 2 for resume int (*ConfigureNRSTAsGPIO)( void * dev, int one_if_yes_gpio ); + int (*ConfigureReadProtection)( void * dev, int one_if_yes_protect ); // No boundary or limit rules. Must support any combination of alignment and size. int (*WriteBinaryBlob)( void * dev, uint32_t address_to_write, uint32_t blob_size, uint8_t * blob ); diff --git a/minichlink/pgm-wch-linke.c b/minichlink/pgm-wch-linke.c index e314875464e5545833cd1d8dab7092c723f31f42..70c43eba8571425820b95f1c484f6f02ba1fefd1 100644 --- a/minichlink/pgm-wch-linke.c +++ b/minichlink/pgm-wch-linke.c @@ -309,6 +309,21 @@ static int LEConfigureNRSTAsGPIO( void * d, int one_if_yes_gpio ) return 0; } +static int LEConfigureReadProtection( void * d, int one_if_yes_protect ) +{ + libusb_device_handle * dev = ((struct LinkEProgrammerStruct*)d)->devh; + + if( one_if_yes_protect ) + { + wch_link_multicommands( (libusb_device_handle *)dev, 2, 11, "\x81\x06\x08\x03\xf7\xff\xff\xff\xff\xff\xff", 4, "\x81\x0b\x01\x01" ); + } + else + { + wch_link_multicommands( (libusb_device_handle *)dev, 2, 11, "\x81\x06\x08\x02\xf7\xff\xff\xff\xff\xff\xff", 4, "\x81\x0b\x01\x01" ); + } + return 0; +} + int LEExit( void * d ) { libusb_device_handle * dev = ((struct LinkEProgrammerStruct*)d)->devh; @@ -337,6 +352,7 @@ void * TryInit_WCHLinkE() MCF.Control5v = LEControl5v; MCF.Unbrick = LEUnbrick; MCF.ConfigureNRSTAsGPIO = LEConfigureNRSTAsGPIO; + MCF.ConfigureReadProtection = LEConfigureReadProtection; MCF.Exit = LEExit; return ret;