It's so annoying when chip errata get fixed when there is a 100% effective workaround, but the "fix" means you now have two chip versions in the wild which behave differently...
CH32V203C8T6
First chip, marking 31170TC11 - came off a devboard, I suspect older
PORTC bits 15-13 are controlled by bits 2..0 in all the GPIOC control registers
Second chip, from JLCPCB assembly, marking 31171TF38
Also one from Aliexpress, marked 31171TG20
PORTC behaves as you would expect, bits 15..13 controlling PC15..13
For outputs a workaround is simply to write both bits to the IO control regs so it works on either version.
Inputs appear to read "0" in the unimplemented bits, so ORing together probably works on both versions.
If you need to identify the version, you can check which bits of GPIOD->OUTDR read back as 1 when you write "1" to both sets of bits (15..13 and 2..0)
I had a look at the HAL source, and it appears to be aware of this, defining a runtime variable "MCU_Version" as 1 for the first version above, and adjusting the bit positions used by GPIO calls accordingly.
None of the other peripheral libraries appear to have a reference to this,so looks like it only affects GPIOC
#if defined (CH32V20x_D6)
uint8_t MCU_Version = 0;
#endif
#if defined (CH32V20x_D6)
if(((*(uint32_t *) 0x40022030) & 0x0F000000) == 0)
{
MCU_Version = 1;
}
void GPIO_Write(GPIO_TypeDef *GPIOx, uint16_t PortVal)
{
#if defined (CH32V20x_D6)
if((GPIOx == GPIOC) && MCU_Version){
PortVal = PortVal >> 13;
}
#endif
GPIOx->OUTDR = PortVal;
}