From b4770bd4f1eb352f0bb21c7e5b6d73d40a6e521d Mon Sep 17 00:00:00 2001 From: cnlohr Date: Tue, 7 Jan 2025 22:18:50 -0500 Subject: [PATCH 1/4] Add interrupt test, for testing performance of EXTI interrupts in different scenarios --- examples_v30x/interrupt_timing_test/Makefile | 14 ++ .../interrupt_timing_test/funconfig.h | 12 ++ .../interrupt_timing_test.c | 135 ++++++++++++++++++ 3 files changed, 161 insertions(+) create mode 100644 examples_v30x/interrupt_timing_test/Makefile create mode 100644 examples_v30x/interrupt_timing_test/funconfig.h create mode 100644 examples_v30x/interrupt_timing_test/interrupt_timing_test.c diff --git a/examples_v30x/interrupt_timing_test/Makefile b/examples_v30x/interrupt_timing_test/Makefile new file mode 100644 index 00000000..33c3892a --- /dev/null +++ b/examples_v30x/interrupt_timing_test/Makefile @@ -0,0 +1,14 @@ +all : flash + +TARGET:=interrupt_timing_test +TARGET_MCU:=CH32V305 +TARGET_MCU_PACKAGE:=CH32V305RBT6 + +CFLAGS+= -Wno-volatile-register-var -ffixed-s6 -ffixed-s7 -ffixed-s8 -ffixed-s9 -ffixed-s10 -ffixed-s11 + +include ../../ch32v003fun/ch32v003fun.mk + +flash : cv_flash +clean : cv_clean + + diff --git a/examples_v30x/interrupt_timing_test/funconfig.h b/examples_v30x/interrupt_timing_test/funconfig.h new file mode 100644 index 00000000..416db179 --- /dev/null +++ b/examples_v30x/interrupt_timing_test/funconfig.h @@ -0,0 +1,12 @@ +#ifndef _FUNCONFIG_H +#define _FUNCONFIG_H + +#define FUNCONF_ENABLE_HPE 1 +#define CH32V30x 1 + +#define NAKED_TEST 1 +#define USE_VTF 1 + + +#endif + diff --git a/examples_v30x/interrupt_timing_test/interrupt_timing_test.c b/examples_v30x/interrupt_timing_test/interrupt_timing_test.c new file mode 100644 index 00000000..f845f9a8 --- /dev/null +++ b/examples_v30x/interrupt_timing_test/interrupt_timing_test.c @@ -0,0 +1,135 @@ +// Interrupt timing test, to see how long it takes to service interrupts. +// +// This expects Pin A4 hard connected to A5, and will signal on A6. +// +// +// PLEASE SEE TEST CONFIGURATION IN funconfig.h ! +// +// Genral notes about the NAKED_TEST: +// This uses fixed register allocation (Please see Makefile for -ffixed-[register] +// Please note that you can normally access these registers from C, but it becomes ... dangerous. +// You should write C helper functions to access them. +// +// RESULTS (comparing from line drop on PA5 to line rise on PA6), using Saleae Pro 8, 500MSps, 3.3V +// Default behavior is 133ns +// Using HPE interrupts, 117ns (FUNCONF_ENABLE_HPE = 1) +// Naked (With or without HPE): 88ns +// Using the VTF + naked + (with or without HPE): 74ns +// +// Conclusion: +// Using the HPE shaves off 3 cycles. +// Using the Naked interrupts saves about 4 cycles +// Using the Vector-Table-Free Interrupt (VTF) System saves about 2 cycles. +// Overall, you can reply to interrupts in about 10-11 cycles, depending on when the interrupt falls. + +#include "ch32v003fun.h" +#include + +volatile int ok = 0; + +#if NAKED_TEST + +register volatile unsigned global_spare asm ("s6"); +register volatile void * global_ok_addr asm ("s7"); +register volatile void * global_GPIOA asm ("s8"); +register volatile void * global_EXTI asm ("s9"); +register volatile unsigned global_APIN asm ("s10"); +register volatile unsigned global_EXITPIN asm ("s11"); + +void EXTI4_IRQHandler( void ) __attribute__((naked)); + +void EXTI4_IRQHandler( void ) +{ + + asm volatile( +" sw s10, 0x10(s8) /* 0x10 is the offset of BSHR*/\n\ + sw s11, 0x14(s9) /* 0x14 is the offset of INTFR*/\n\ + lw s6, 0(s7)\n\ + addi s6,s6,1\n\ + sw s6, 0(s7)\n\ + mret\ + " : : : ); +} + +void SetupRegisters( void ) +{ + asm volatile( "\ + mv s7, %[okaddr]\n\ + li s8, %[global_GPIOA]\n\ + li s9, %[global_EXTI]\n\ + li s10, 1<<6\n\ + li s11, 1<<4\n\ + " : : [okaddr]"r"(&ok), [global_GPIOA]"g"(GPIOA), [global_EXTI]"g"(EXTI) ); + + /* + // We can't actually set these reliably because GCC might optimize them out. + global_spare = 0; // s6 + global_ok_addr = &ok; // s7 + global_GPIOA = GPIOA; // s8 + global_EXTI = EXTI; // s9 + global_APIN = 1<<6; // s10 + global_EXITPIN = 1<<4; // s11 + */ +} + +#else + +void EXTI4_IRQHandler( void ) INTERRUPT_DECORATOR; + +void EXTI4_IRQHandler( void ) +{ + funDigitalWrite( PA6, FUN_HIGH ); + EXTI->INTFR = EXTI_Line4; + ok++; +#if FUNCONF_ENABLE_HPE + asm volatile( "\ + mret\ + " : : : ); +#endif +} + +#endif + +int main() +{ + + SystemInit(); + funGpioInitAll(); + RCC->APB2PCENR |= RCC_APB2Periph_AFIO; + + funPinMode( PA4, GPIO_CFGLR_IN_FLOAT ); + funPinMode( PA5, GPIO_CFGLR_OUT_50Mhz_PP ); + funPinMode( PA6, GPIO_CFGLR_OUT_50Mhz_PP ); + funDigitalWrite( PA5, FUN_LOW ); + funDigitalWrite( PA6, FUN_LOW ); + + // Configure the IO as an interrupt. + AFIO->EXTICR[1] = AFIO_EXTICR2_EXTI4_PA; + EXTI->INTENR = EXTI_INTENR_MR4; // Enable EXT4 + EXTI->FTENR = EXTI_FTENR_TR4; // Rising edge trigger + +#if NAKED_TEST + SetupRegisters(); +#endif + +#if USE_VTF + SetVTFIRQ( (uintptr_t)&EXTI4_IRQHandler, EXTI4_IRQn, 0, ENABLE ); +#endif + + // enable interrupt + NVIC_EnableIRQ( EXTI4_IRQn ); + + ok = 0; + printf( "Testing\n" ); + while( 1 ) + { + printf( "Mark\n" ); + funDigitalWrite( PA5, FUN_LOW ); + printf( "Done %d\n", ok ); + funDigitalWrite( PA6, FUN_LOW ); + funDigitalWrite( PA5, FUN_HIGH ); + Delay_Ms(1); + } + +} + From ff10797eaa1bda18dfd347d4f1a8eb5ecc53e4d1 Mon Sep 17 00:00:00 2001 From: cnlohr Date: Tue, 7 Jan 2025 22:28:16 -0500 Subject: [PATCH 2/4] Add note about running from RAM --- .../interrupt_timing_test/funconfig.h | 1 + .../interrupt_timing_test.c | 23 ++++++++++++++----- 2 files changed, 18 insertions(+), 6 deletions(-) diff --git a/examples_v30x/interrupt_timing_test/funconfig.h b/examples_v30x/interrupt_timing_test/funconfig.h index 416db179..771e6d93 100644 --- a/examples_v30x/interrupt_timing_test/funconfig.h +++ b/examples_v30x/interrupt_timing_test/funconfig.h @@ -5,6 +5,7 @@ #define CH32V30x 1 #define NAKED_TEST 1 +#define INTERRUPT_IN_RAM 1 #define USE_VTF 1 diff --git a/examples_v30x/interrupt_timing_test/interrupt_timing_test.c b/examples_v30x/interrupt_timing_test/interrupt_timing_test.c index f845f9a8..ad1d28fb 100644 --- a/examples_v30x/interrupt_timing_test/interrupt_timing_test.c +++ b/examples_v30x/interrupt_timing_test/interrupt_timing_test.c @@ -11,15 +11,20 @@ // You should write C helper functions to access them. // // RESULTS (comparing from line drop on PA5 to line rise on PA6), using Saleae Pro 8, 500MSps, 3.3V -// Default behavior is 133ns -// Using HPE interrupts, 117ns (FUNCONF_ENABLE_HPE = 1) -// Naked (With or without HPE): 88ns -// Using the VTF + naked + (with or without HPE): 74ns +// +// Interrupt in Flash: +// Default behavior is 133ns +// Using HPE interrupts, 117ns (FUNCONF_ENABLE_HPE = 1) +// Naked (With or without HPE): 88ns +// Using the VTF + naked + (with or without HPE): 74ns +// Interrupt in RAM, Using Naked VTF, no HPE. +// Running from RAM about 81ns. // // Conclusion: // Using the HPE shaves off 3 cycles. // Using the Naked interrupts saves about 4 cycles // Using the Vector-Table-Free Interrupt (VTF) System saves about 2 cycles. +// Running from RAM adds about 1 cycle. // Overall, you can reply to interrupts in about 10-11 cycles, depending on when the interrupt falls. #include "ch32v003fun.h" @@ -27,6 +32,12 @@ volatile int ok = 0; +#if INTERRUPT_IN_RAM +#define EXTRA_INTERRUPT_DECORATOR __attribute__((section(".sdata2.interrupts"))) +#else +#define EXTRA_INTERRUPT_DECORATOR +#endif + #if NAKED_TEST register volatile unsigned global_spare asm ("s6"); @@ -36,7 +47,7 @@ register volatile void * global_EXTI asm ("s9"); register volatile unsigned global_APIN asm ("s10"); register volatile unsigned global_EXITPIN asm ("s11"); -void EXTI4_IRQHandler( void ) __attribute__((naked)); +void EXTI4_IRQHandler( void ) __attribute__((naked)) EXTRA_INTERRUPT_DECORATOR; void EXTI4_IRQHandler( void ) { @@ -74,7 +85,7 @@ void SetupRegisters( void ) #else -void EXTI4_IRQHandler( void ) INTERRUPT_DECORATOR; +void EXTI4_IRQHandler( void ) INTERRUPT_DECORATOR EXTRA_INTERRUPT_DECORATOR; void EXTI4_IRQHandler( void ) { From 2e981ddaf82563c94d6f01a7ecca59e9ef237c69 Mon Sep 17 00:00:00 2001 From: cnlohr Date: Wed, 8 Jan 2025 00:09:42 -0500 Subject: [PATCH 3/4] Add overclocking test --- .../interrupt_timing_test/funconfig.h | 4 +- .../interrupt_timing_test.c | 52 +++++++++++++++++++ 2 files changed, 55 insertions(+), 1 deletion(-) diff --git a/examples_v30x/interrupt_timing_test/funconfig.h b/examples_v30x/interrupt_timing_test/funconfig.h index 771e6d93..dda95efd 100644 --- a/examples_v30x/interrupt_timing_test/funconfig.h +++ b/examples_v30x/interrupt_timing_test/funconfig.h @@ -5,9 +5,11 @@ #define CH32V30x 1 #define NAKED_TEST 1 -#define INTERRUPT_IN_RAM 1 +#define INTERRUPT_IN_RAM 0 #define USE_VTF 1 +#define OVERCLOCK 0 + #endif diff --git a/examples_v30x/interrupt_timing_test/interrupt_timing_test.c b/examples_v30x/interrupt_timing_test/interrupt_timing_test.c index ad1d28fb..aa229eb3 100644 --- a/examples_v30x/interrupt_timing_test/interrupt_timing_test.c +++ b/examples_v30x/interrupt_timing_test/interrupt_timing_test.c @@ -127,6 +127,58 @@ int main() SetVTFIRQ( (uintptr_t)&EXTI4_IRQHandler, EXTI4_IRQn, 0, ENABLE ); #endif +#if OVERCLOCK + EXTEND->CTR = ( EXTEND->CTR & (~(3<<10)) ) | 3<<10; // Turning it "to 1.0V according to datasheet" seems to make it most reliable. + + // Switch processor back to HSI so we don't eat dirt. + RCC->CFGR0 = (RCC->CFGR0 & ~RCC_SW) | RCC_SW_HSI; + + // Keep the HSI on but turn on the HSE. + RCC->CTLR = RCC_HSION | RCC_HSEON; + + // It's soemthing like this: https://cnlohr.github.io/microclockoptimizer/?chipSelect=ch32vx05_7%2Cd8w&HSI=1,8&HSE=0,4&PREDIV2=1,0&PLL2CLK=1,7&PLL2VCO=0,72&PLL3CLK=1,1&PLL3VCO=0,100&PREDIV1SRC=1,0&PREDIV1=1,2&PLLSRC=1,0&PLL=1,4&PLLVCO=0,144&SYSCLK=1,2& + // Assuming PLL_MUL_REG = 4 (so it's not overclocking) + // ALSO-SIDE-NOTE: I think there's a /2 going on somewhere. + + // Setup clock tree. + RCC->CFGR2 |= + (0<CFGR0 = ( RCC->CFGR0 & ~(0xf<<18)) | (PLL_MUL_REG<<18) | RCC_PLLSRC; + + // Power on PLLs + RCC->CTLR |= RCC_PLL3ON | RCC_PLL2ON; + int timeout; + + for( timeout = 10000; timeout > 0; timeout--) if (RCC->CTLR & RCC_PLL2RDY) break; + if( timeout == 0 ) goto lockfail; + printf( "NEXT\n" ); + + RCC->CTLR |= RCC_PLLON; + for( timeout = 10000; timeout > 0; timeout--) if (RCC->CTLR & RCC_PLLRDY) break; + if( timeout == 0 ) goto lockfail; + + RCC->CFGR0 = (RCC->CFGR0 & ~RCC_SW) | RCC_SW_PLL; + printf( "RCC->CTLR = %08x\n", RCC->CTLR ); + printf( "RCC->CFGR0 = %08x\n", RCC->CFGR0 ); + + goto success; +lockfail: + printf( "FAILED TO LOCK\n" ); +success: +#endif + // enable interrupt NVIC_EnableIRQ( EXTI4_IRQn ); From 67f9e691b4f5db8652f553ac1b34f4465b33bd41 Mon Sep 17 00:00:00 2001 From: cnlohr Date: Wed, 8 Jan 2025 00:18:50 -0500 Subject: [PATCH 4/4] Update with more notes, re: overclocking --- .../interrupt_timing_test/interrupt_timing_test.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/examples_v30x/interrupt_timing_test/interrupt_timing_test.c b/examples_v30x/interrupt_timing_test/interrupt_timing_test.c index aa229eb3..7b72c2e8 100644 --- a/examples_v30x/interrupt_timing_test/interrupt_timing_test.c +++ b/examples_v30x/interrupt_timing_test/interrupt_timing_test.c @@ -136,25 +136,25 @@ int main() // Keep the HSI on but turn on the HSE. RCC->CTLR = RCC_HSION | RCC_HSEON; - // It's soemthing like this: https://cnlohr.github.io/microclockoptimizer/?chipSelect=ch32vx05_7%2Cd8w&HSI=1,8&HSE=0,4&PREDIV2=1,0&PLL2CLK=1,7&PLL2VCO=0,72&PLL3CLK=1,1&PLL3VCO=0,100&PREDIV1SRC=1,0&PREDIV1=1,2&PLLSRC=1,0&PLL=1,4&PLLVCO=0,144&SYSCLK=1,2& + // It's soemthing like this: https://cnlohr.github.io/microclockoptimizer/?chipSelect=ch32vx05_7%2Cd8w&HSI=1,8&HSE=0,8&PREDIV2=1,0&PLL2CLK=1,7&PLL2VCO=0,144&PLL3CLK=0,1&PLL3VCO=0,200&PREDIV1SRC=1,0&PREDIV1=1,2&PLLSRC=1,0&PLL=1,4&PLLVCO=0,144&SYSCLK=1,2& // Assuming PLL_MUL_REG = 4 (so it's not overclocking) - // ALSO-SIDE-NOTE: I think there's a /2 going on somewhere. // Setup clock tree. RCC->CFGR2 |= (0<CFGR0 = ( RCC->CFGR0 & ~(0xf<<18)) | (PLL_MUL_REG<<18) | RCC_PLLSRC; // Power on PLLs