From c802fcdcf821590f193608dda2233aecb722d2d6 Mon Sep 17 00:00:00 2001 From: emuzit <98307908+emuzit@users.noreply.github.com> Date: Sat, 30 Jul 2022 14:10:51 +0800 Subject: [PATCH] WCH CH569W-R0-1v0 evt board bsp port, first version (#6167) WCH CH569W-R0-1v0 evt board bsp port, first version dev/test under Ubuntu 20.04 toolchain from MounRiver_Studio_Community_Linux_x64_V120 tested drivers : SysTick, gpio, gpio interrupt, uart1 (RX interrupt, TX polling) libcpu/risc-v/SConscript : group includes rtconfig.CPU only if folder exists libcpu/risc-v/common/cpuport.c/rt_hw_context_switch_interrupt() : make it RT_WEAK for customization --- bsp/wch/risc-v/Libraries/Kconfig | 9 +- .../risc-v/Libraries/ch56x_drivers/SConscript | 28 + .../Libraries/ch56x_drivers/ch56x_gpio.c | 399 +++++++++++ .../Libraries/ch56x_drivers/ch56x_gpio.h | 216 ++++++ .../Libraries/ch56x_drivers/ch56x_pfic.c | 119 ++++ .../Libraries/ch56x_drivers/ch56x_pfic.h | 258 +++++++ .../Libraries/ch56x_drivers/ch56x_sys.c | 249 +++++++ .../Libraries/ch56x_drivers/ch56x_sys.h | 395 +++++++++++ .../Libraries/ch56x_drivers/ch56x_timer.c | 262 +++++++ .../Libraries/ch56x_drivers/ch56x_timer.h | 138 ++++ .../Libraries/ch56x_drivers/ch56x_uart.c | 343 +++++++++ .../Libraries/ch56x_drivers/ch56x_uart.h | 254 +++++++ .../Libraries/ch56x_drivers/ch56x_wdt.c | 241 +++++++ .../risc-v/Libraries/ch56x_drivers/isr_sp.h | 31 + bsp/wch/risc-v/Libraries/ch56x_drivers/soc.h | 152 ++++ .../risc-v/Libraries/ch56x_drivers/swi_gcc.S | 190 +++++ bsp/wch/risc-v/ch569w-evt/.config | 664 ++++++++++++++++++ bsp/wch/risc-v/ch569w-evt/Kconfig | 21 + bsp/wch/risc-v/ch569w-evt/SConscript | 13 + bsp/wch/risc-v/ch569w-evt/SConstruct | 38 + .../risc-v/ch569w-evt/applications/SConscript | 13 + bsp/wch/risc-v/ch569w-evt/applications/main.c | 231 ++++++ bsp/wch/risc-v/ch569w-evt/board/Kconfig | 64 ++ bsp/wch/risc-v/ch569w-evt/board/SConscript | 13 + bsp/wch/risc-v/ch569w-evt/board/board.c | 80 +++ bsp/wch/risc-v/ch569w-evt/board/board.h | 36 + .../ch569w-evt/board/linker_scripts/link.lds | 199 ++++++ bsp/wch/risc-v/ch569w-evt/board/startup_gcc.S | 174 +++++ .../risc-v/ch569w-evt/figures/ch569w-evt.jpeg | Bin 0 -> 114930 bytes bsp/wch/risc-v/ch569w-evt/rtconfig.h | 192 +++++ bsp/wch/risc-v/ch569w-evt/rtconfig.py | 62 ++ libcpu/risc-v/SConscript | 2 +- libcpu/risc-v/common/cpuport.c | 2 +- 33 files changed, 5085 insertions(+), 3 deletions(-) create mode 100644 bsp/wch/risc-v/Libraries/ch56x_drivers/SConscript create mode 100644 bsp/wch/risc-v/Libraries/ch56x_drivers/ch56x_gpio.c create mode 100644 bsp/wch/risc-v/Libraries/ch56x_drivers/ch56x_gpio.h create mode 100644 bsp/wch/risc-v/Libraries/ch56x_drivers/ch56x_pfic.c create mode 100644 bsp/wch/risc-v/Libraries/ch56x_drivers/ch56x_pfic.h create mode 100644 bsp/wch/risc-v/Libraries/ch56x_drivers/ch56x_sys.c create mode 100644 bsp/wch/risc-v/Libraries/ch56x_drivers/ch56x_sys.h create mode 100644 bsp/wch/risc-v/Libraries/ch56x_drivers/ch56x_timer.c create mode 100644 bsp/wch/risc-v/Libraries/ch56x_drivers/ch56x_timer.h create mode 100644 bsp/wch/risc-v/Libraries/ch56x_drivers/ch56x_uart.c create mode 100644 bsp/wch/risc-v/Libraries/ch56x_drivers/ch56x_uart.h create mode 100644 bsp/wch/risc-v/Libraries/ch56x_drivers/ch56x_wdt.c create mode 100644 bsp/wch/risc-v/Libraries/ch56x_drivers/isr_sp.h create mode 100644 bsp/wch/risc-v/Libraries/ch56x_drivers/soc.h create mode 100644 bsp/wch/risc-v/Libraries/ch56x_drivers/swi_gcc.S create mode 100644 bsp/wch/risc-v/ch569w-evt/.config create mode 100644 bsp/wch/risc-v/ch569w-evt/Kconfig create mode 100644 bsp/wch/risc-v/ch569w-evt/SConscript create mode 100644 bsp/wch/risc-v/ch569w-evt/SConstruct create mode 100644 bsp/wch/risc-v/ch569w-evt/applications/SConscript create mode 100644 bsp/wch/risc-v/ch569w-evt/applications/main.c create mode 100644 bsp/wch/risc-v/ch569w-evt/board/Kconfig create mode 100644 bsp/wch/risc-v/ch569w-evt/board/SConscript create mode 100644 bsp/wch/risc-v/ch569w-evt/board/board.c create mode 100644 bsp/wch/risc-v/ch569w-evt/board/board.h create mode 100644 bsp/wch/risc-v/ch569w-evt/board/linker_scripts/link.lds create mode 100644 bsp/wch/risc-v/ch569w-evt/board/startup_gcc.S create mode 100644 bsp/wch/risc-v/ch569w-evt/figures/ch569w-evt.jpeg create mode 100644 bsp/wch/risc-v/ch569w-evt/rtconfig.h create mode 100644 bsp/wch/risc-v/ch569w-evt/rtconfig.py diff --git a/bsp/wch/risc-v/Libraries/Kconfig b/bsp/wch/risc-v/Libraries/Kconfig index 6482c47a1d..3428efbab8 100644 --- a/bsp/wch/risc-v/Libraries/Kconfig +++ b/bsp/wch/risc-v/Libraries/Kconfig @@ -5,4 +5,11 @@ config SOC_RISCV_SERIES_CH32V103 bool select ARCH_RISCV select SOC_RISCV_FAMILY_CH32 - \ No newline at end of file + +config SOC_FAMILY_CH56X + bool + select ARCH_RISCV + +config SOC_SERIES_CH569 + bool + select SOC_FAMILY_CH56X diff --git a/bsp/wch/risc-v/Libraries/ch56x_drivers/SConscript b/bsp/wch/risc-v/Libraries/ch56x_drivers/SConscript new file mode 100644 index 0000000000..f936f62ceb --- /dev/null +++ b/bsp/wch/risc-v/Libraries/ch56x_drivers/SConscript @@ -0,0 +1,28 @@ +from building import * + +cwd = GetCurrentDir() + +src = Split(""" +ch56x_sys.c +swi_gcc.S +""") + +if GetDepend('SOC_SERIES_CH569'): + src += ['ch56x_pfic.c'] + if GetDepend('RT_USING_WDT'): + src += ['ch56x_wdt.c'] + +if GetDepend('RT_USING_HWTIMER'): + src += ['ch56x_timer.c'] + +if GetDepend('RT_USING_PIN'): + src += ['ch56x_gpio.c'] + +if GetDepend(['RT_USING_SERIAL', 'BSP_USING_UART']): + src += ['ch56x_uart.c'] + +path = [cwd] + +group = DefineGroup('Drivers', src, depend=[''], CPPPATH=path) + +Return('group') diff --git a/bsp/wch/risc-v/Libraries/ch56x_drivers/ch56x_gpio.c b/bsp/wch/risc-v/Libraries/ch56x_drivers/ch56x_gpio.c new file mode 100644 index 0000000000..94c2116d98 --- /dev/null +++ b/bsp/wch/risc-v/Libraries/ch56x_drivers/ch56x_gpio.c @@ -0,0 +1,399 @@ +/* + * Copyright (c) 2006-2022, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2022-07-15 Emuzit first version + */ +#include +#include +#include +#include "ch56x_gpio.h" +#include "isr_sp.h" + +struct port_info +{ + uint32_t pin_mark; + struct gpio_px_regs *regbase; +}; + +static const struct port_info pin_ports[GPIO_PORTS] = +{ + {GPIO_PA_PIN_MARK, (struct gpio_px_regs *)GPIO_REG_BASE_PA}, + {GPIO_PB_PIN_MARK, (struct gpio_px_regs *)GPIO_REG_BASE_PB}, +}; + +static struct rt_pin_irq_hdr pin_irq_hdr_table[8] = +{ + {-1, 0, RT_NULL, RT_NULL}, + {-1, 0, RT_NULL, RT_NULL}, + {-1, 0, RT_NULL, RT_NULL}, + {-1, 0, RT_NULL, RT_NULL}, + {-1, 0, RT_NULL, RT_NULL}, + {-1, 0, RT_NULL, RT_NULL}, + {-1, 0, RT_NULL, RT_NULL}, + {-1, 0, RT_NULL, RT_NULL}, +}; + + +#if defined(SOC_SERIES_CH569) +static int _gpio_pin_to_ibit(rt_base_t pin) +{ + /* gpio ext interrupt 7-0 : {PB15,PB12,PB11,PB4,PB3,PA4,PA3,PA2} + * not time critical, use linear search + */ + switch (pin) + { + case GET_PIN(A, 2): return 0; + case GET_PIN(A, 3): return 1; + case GET_PIN(A, 4): return 2; + case GET_PIN(B, 3): return 3; + case GET_PIN(B, 4): return 4; + case GET_PIN(B, 11): return 5; + case GET_PIN(B, 12): return 6; + case GET_PIN(B, 15): return 7; + } + return -1; +} + +#else + +static int _gpio_pin_to_ibit(rt_base_t pin) +{ + /* gpio ext interrupt 7-0 : {PB10,PB4,PA12,PA11,PA10,PA6,PA4,PA3} + * not time critical, use linear search + */ + switch (pin) + { + case GET_PIN(A, 3): return 0; + case GET_PIN(A, 4): return 1; + case GET_PIN(A, 6): return 2; + case GET_PIN(A, 10): return 3; + case GET_PIN(A, 11): return 4; + case GET_PIN(A, 12): return 5; + case GET_PIN(B, 4): return 6; + case GET_PIN(B, 10): return 7; + } + return -1; +} +#endif + +static struct gpio_px_regs *_gpio_px_regbase(rt_base_t pin) +{ + /* fixed linear mapping : 32 pins per port, for ports A,B,C,D... + */ + uint32_t port = (uint32_t)pin >> 5; + uint32_t bitpos = 1 << (pin & 0x1f); + + if (port < GPIO_PORTS && (pin_ports[port].pin_mark & bitpos)) + return pin_ports[port].regbase; + else + return RT_NULL; +} + +static void gpio_pin_mode(struct rt_device *device, rt_base_t pin, rt_base_t mode) +{ + volatile struct gpio_px_regs *px; + + uint32_t port = (uint32_t)pin >> 5; + uint32_t bitpos = 1 << (pin & 0x1f); + + if (port < GPIO_PORTS && (pin_ports[port].pin_mark & bitpos)) + px = pin_ports[port].regbase; + else + return; + + switch (mode) + { + case PIN_MODE_OUTPUT: + BITS_CLR(px->PD, bitpos); + BITS_SET(px->DIR, bitpos); + break; + case PIN_MODE_INPUT: + BITS_CLR(px->PU, bitpos); + BITS_CLR(px->PD, bitpos); + BITS_CLR(px->DIR, bitpos); + break; + case PIN_MODE_INPUT_PULLUP: + BITS_SET(px->PU, bitpos); + BITS_CLR(px->PD, bitpos); + BITS_CLR(px->DIR, bitpos); + break; + case PIN_MODE_INPUT_PULLDOWN: + BITS_CLR(px->PU, bitpos); + BITS_SET(px->PD, bitpos); + BITS_CLR(px->DIR, bitpos); + break; + case PIN_MODE_OUTPUT_OD: + BITS_SET(px->PD, bitpos); + BITS_SET(px->OUT, bitpos); + } +} + +static void gpio_pin_write(struct rt_device *device, rt_base_t pin, rt_base_t value) +{ + volatile struct gpio_px_regs *px; + + uint32_t port = (uint32_t)pin >> 5; + uint32_t bitpos = 1 << (pin & 0x1f); + + if (port < GPIO_PORTS && (pin_ports[port].pin_mark & bitpos)) + px = pin_ports[port].regbase; + else + return; + + if (value == 0) + BITS_CLR(px->OUT, bitpos); + else + BITS_SET(px->OUT, bitpos); +} + +static int gpio_pin_read(struct rt_device *device, rt_base_t pin) +{ + volatile struct gpio_px_regs *px; + + uint32_t port = (uint32_t)pin >> 5; + uint32_t bitpos = 1 << (pin & 0x1f); + + if (port < GPIO_PORTS && (pin_ports[port].pin_mark & bitpos)) + px = pin_ports[port].regbase; + else + return PIN_LOW; + + return (px->PIN & bitpos) ? PIN_HIGH : PIN_LOW; +} + +static rt_base_t gpio_pin_get(const char *name) +{ + int port, pin, sz, n; + + /* pin name is in the form "PX.nn" (X: A,B,C,D...; nn: 0~31) + * fixed linear mapping : 32 pins per port, for ports A,B,C,D... + */ + sz = rt_strlen(name); + if ((sz == 4 || sz == 5) && name[0] == 'P' && name[2] == '.') + { + port = name[1] - 'A'; + pin = name[3] - '0'; + if (0 <= port && port < GPIO_PORTS && 0 <= pin && pin <= 9) + { + if (sz == 5) + { + n = name[4] - '0'; + pin = (0 <= n && n <= 9) ? (pin * 10 + n) : 32; + } + if (pin < 32 && (pin_ports[port].pin_mark & (1 << pin))) + { + return port * 32 + pin; + } + } + } + + return -1; +} + +static rt_err_t gpio_pin_attach_irq(struct rt_device *device, rt_int32_t pin, + rt_uint32_t mode, void (*hdr)(void *args), void *args) +{ + rt_base_t level; + + int ibit; + + switch (mode) + { + case PIN_IRQ_MODE_RISING: + case PIN_IRQ_MODE_FALLING: + case PIN_IRQ_MODE_HIGH_LEVEL: + case PIN_IRQ_MODE_LOW_LEVEL: + break; + case PIN_IRQ_MODE_RISING_FALLING: + /* hardware not supported */ + default: + return -RT_EINVAL; + } + + ibit = _gpio_pin_to_ibit(pin); + if (ibit < 0) + return -RT_EINVAL; + + level = rt_hw_interrupt_disable(); + if (pin_irq_hdr_table[ibit].pin == pin && + pin_irq_hdr_table[ibit].mode == mode && + pin_irq_hdr_table[ibit].hdr == hdr && + pin_irq_hdr_table[ibit].args == args) + { + rt_hw_interrupt_enable(level); + return RT_EOK; + } + + if (pin_irq_hdr_table[ibit].pin >= 0) + { + rt_hw_interrupt_enable(level); + return -RT_EFULL; + } + + pin_irq_hdr_table[ibit].pin = pin; + pin_irq_hdr_table[ibit].mode = mode; + pin_irq_hdr_table[ibit].hdr = hdr; + pin_irq_hdr_table[ibit].args = args; + + rt_hw_interrupt_enable(level); + return RT_EOK; +} + +static rt_err_t gpio_pin_detach_irq(struct rt_device *device, rt_int32_t pin) +{ + rt_base_t level; + + int ibit; + + ibit = _gpio_pin_to_ibit(pin); + if (ibit < 0) + return -RT_EINVAL; + + level = rt_hw_interrupt_disable(); + if (pin_irq_hdr_table[ibit].pin < 0) + { + rt_hw_interrupt_enable(level); + return RT_EOK; + } + pin_irq_hdr_table[ibit].pin = -1; + pin_irq_hdr_table[ibit].mode = 0; + pin_irq_hdr_table[ibit].hdr = RT_NULL; + pin_irq_hdr_table[ibit].args = RT_NULL; + rt_hw_interrupt_enable(level); + return RT_EOK; +} + +static rt_err_t gpio_pin_irq_enable(struct rt_device *device, rt_base_t pin, + rt_uint32_t enabled) +{ + volatile struct gpio_registers *gpio; + + rt_base_t level, int_enable; + + int ibit, bitpos; + + ibit = _gpio_pin_to_ibit(pin); + if (ibit < 0) + return -RT_EINVAL; + bitpos = (1 << ibit); + + gpio = (struct gpio_registers *)GPIO_REG_BASE; + + if (enabled == PIN_IRQ_ENABLE) + { + level = rt_hw_interrupt_disable(); + if (pin_irq_hdr_table[ibit].pin != pin) + { + rt_hw_interrupt_enable(level); + return -RT_EINVAL; + } + + switch (pin_irq_hdr_table[ibit].mode) + { + case PIN_IRQ_MODE_RISING: + BITS_SET(gpio->INT_MODE.reg, bitpos); + BITS_SET(gpio->INT_POLAR.reg, bitpos); + break; + case PIN_IRQ_MODE_FALLING: + BITS_SET(gpio->INT_MODE.reg, bitpos); + BITS_CLR(gpio->INT_POLAR.reg, bitpos); + break; + case PIN_IRQ_MODE_HIGH_LEVEL: + BITS_CLR(gpio->INT_MODE.reg, bitpos); + BITS_SET(gpio->INT_POLAR.reg, bitpos); + break; + case PIN_IRQ_MODE_LOW_LEVEL: + BITS_CLR(gpio->INT_MODE.reg, bitpos); + BITS_CLR(gpio->INT_POLAR.reg, bitpos); + break; + case PIN_IRQ_MODE_RISING_FALLING: + default: + rt_hw_interrupt_enable(level); + return -RT_EINVAL; + } + + /* clear possible pending intr, then enable pin intr */ + int_enable = gpio->INT_ENABLE.reg; + gpio->INT_FLAG.reg = bitpos; + gpio->INT_ENABLE.reg = int_enable | bitpos; + /* enable GPIO_IRQn if this is the first enabled EXTIx */ + if (int_enable == 0) + { + rt_hw_interrupt_umask(GPIO_IRQn); + } + rt_hw_interrupt_enable(level); + } + else if (enabled == PIN_IRQ_DISABLE) + { + level = rt_hw_interrupt_disable(); + int_enable = gpio->INT_ENABLE.reg; + BITS_CLR(int_enable, bitpos); + gpio->INT_ENABLE.reg = int_enable; + /* disable GPIO_IRQn if no EXTIx enabled */ + if (int_enable == 0) + { + rt_hw_interrupt_mask(GPIO_IRQn); + } + rt_hw_interrupt_enable(level); + } + else + { + return -RT_EINVAL; + } + + return RT_EOK; +} + +static const struct rt_pin_ops pin_ops = +{ + .pin_mode = gpio_pin_mode, + .pin_write = gpio_pin_write, + .pin_read = gpio_pin_read, + .pin_attach_irq = gpio_pin_attach_irq, + .pin_detach_irq = gpio_pin_detach_irq, + .pin_irq_enable = gpio_pin_irq_enable, + .pin_get = gpio_pin_get, +}; + +static int rt_hw_pin_init(void) +{ + return rt_device_pin_register("pin", &pin_ops, RT_NULL); +} +INIT_BOARD_EXPORT(rt_hw_pin_init); + +void gpio_irq_handler(void) __attribute__((interrupt())); +void gpio_irq_handler(void) +{ + volatile struct gpio_registers *gpio; + + uint8_t iflags; + + int ibit, bitpos; + + isr_sp_enter(); + rt_interrupt_enter(); + + gpio = (struct gpio_registers *)GPIO_REG_BASE; + iflags = gpio->INT_FLAG.reg; + /* prioritized as pb15 -> pa2 (CH569), or pb10 -> pa3 */ + for (ibit = 7; ibit >= 0; ibit--) + { + bitpos = (1 << ibit); + if (iflags & bitpos) + { + if (pin_irq_hdr_table[ibit].hdr) + { + pin_irq_hdr_table[ibit].hdr(pin_irq_hdr_table[ibit].args); + } + /* clear interrupt */ + gpio->INT_FLAG.reg = bitpos; + } + } + + rt_interrupt_leave(); + isr_sp_leave(); +} diff --git a/bsp/wch/risc-v/Libraries/ch56x_drivers/ch56x_gpio.h b/bsp/wch/risc-v/Libraries/ch56x_drivers/ch56x_gpio.h new file mode 100644 index 0000000000..2b6af1a5d5 --- /dev/null +++ b/bsp/wch/risc-v/Libraries/ch56x_drivers/ch56x_gpio.h @@ -0,0 +1,216 @@ +/* + * Copyright (c) 2006-2022, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2022-07-15 Emuzit first version + */ +#ifndef __CH56X_GPIO_H__ +#define __CH56X_GPIO_H__ + +#include "soc.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* Fixed linear mapping : 32 pins per port, for ports A,B,C,D... +*/ +#define GET_PIN(port,pin) (GPIO_P##port##_PIN_START + pin) + +#ifdef SOC_SERIES_CH569 +#define GPIO_INT_PINS { \ + GET_PIN(A,2), GET_PIN(A,3), GET_PIN(A,4), GET_PIN(B,3), \ + GET_PIN(B,4), GET_PIN(B,11), GET_PIN(B,12), GET_PIN(B,15) \ + } +#else +#define GPIO_INT_PINS { \ + GET_PIN(A,3), GET_PIN(A,4), GET_PIN(A,6), GET_PIN(A,10), \ + GET_PIN(A,11), GET_PIN(A,12), GET_PIN(B,4), GET_PIN(B,10), \ + } +#endif + +/* + * R8_GPIO_INT_FLAG / R8_GPIO_INT_STATUS (CH567,CH568): + * write 1 to specific bit to clear int flag + * + * R8_GPIO_INT_ENABLE: + * To use EXTIx function, pin should be set as input. + * For wakeup function, also set RB_SLP_GPIO_WAKE. + * + * R8_GPIO_INT_MODE: + * R8_GPIO_INT_POLAR: + */ +#if defined(SOC_SERIES_CH569) +union _gpio_interrupt +{ + uint8_t reg; + struct + { + uint8_t pa2 : 1; + uint8_t pa3 : 1; + uint8_t pa4 : 1; + uint8_t pb3 : 1; + uint8_t pb4 : 1; + uint8_t pb11 : 1; + uint8_t pb12 : 1; + uint8_t pb15 : 1; + }; +}; + +#else + +union _gpio_interrupt +{ + uint8_t reg; + struct + { + uint8_t pa3 : 1; + uint8_t pa4 : 1; + uint8_t pa6 : 1; + uint8_t pa10 : 1; + uint8_t pa11 : 1; + uint8_t pa12 : 1; + uint8_t pb4 : 1; + uint8_t pb10 : 1; + }; +}; +#endif + +#define GPIO_IE_DISABLE 0 +#define GPIO_IE_ENABLE 1 + +#define GPIO_IM_LEVEL 0 +#define GPIO_IM_EDGE 1 + +#define GPIO_IP_LOW_FALLING 0 +#define GPIO_IP_HIGH_RISING 1 + +/* + * R8_PIN_ALTERNATE (CH569) : reset value is 0x01 + * R8_PORT_PIN (CH567/CH568) : reset value is 0x00 + */ +union _gpio_pin_alternate +{ + uint8_t reg; + struct + { + uint8_t pin_mii : 1; // RW, ETH uses RMII/RGMII (CH565W/CH569W) + uint8_t pin_tmr1 : 1; // RW, TMR1/PWM5/CAP1 remapping + uint8_t pin_tmr2 : 1; // RW, TMR2/PWM6/CAP2 remapping + uint8_t resv_3 : 1; + uint8_t pin_uart0 : 1; // RW, RXD0/TXD0 remapping + uint8_t pin_uart1 : 1; // RW, CH567 only + uint8_t resv_6 : 2; + }; +}; +#define RB_PIN_MII 0x01 +#define RB_PIN_TMR1 0x02 +#define RB_PIN_TMR2 0x04 +#define RB_PIN_UART0 0x10 +#define RB_PIN_UART1 0x20 + +#ifdef SOC_SERIES_CH569 +#define GPIO_ALT_RMII 0 +#define GPIO_ALT_RGMII 1 +#define GPIO_ALT_TMR1_PB15 0 +#define GPIO_ALT_TMR1_PB0 1 +#define GPIO_ALT_TMR2_PA4 0 +#define GPIO_ALT_TMR2_PB3 1 +#define GPIO_ALT_UART0_PB5_6 0 +#define GPIO_ALT_UART0_PA5_6 1 +#else +#define GPIO_ALT_TMR1_PA10 0 +#define GPIO_ALT_TMR1_PB2 1 +#define GPIO_ALT_TMR2_PA11 0 +#define GPIO_ALT_TMR2_PB11 1 +#define GPIO_ALT_UART0_PB4_7 0 +#define GPIO_ALT_UART0_PA15_14 1 +#define GPIO_ALT_UART1_PA8_9 0 +#define GPIO_ALT_UART1_PB8_9 1 +#endif + +struct gpio_px_regs +{ + uint32_t DIR; // reset value for pins is 0, input pins + uint32_t PIN; // RO + uint32_t OUT; // reset value is 0 + uint32_t CLR; // reset value is 0 + uint32_t PU; // reset value is 0 + uint32_t PD; // reset value is 0 + uint32_t DRV; // reset value for pins is 0, 8mA + uint32_t SMT; // reset value for pins is 1, enable schmitt trigger +} __packed; + +CHECK_STRUCT_SIZE(struct gpio_px_regs, 0x20); + +#define GPIO_PX_DIR_IN 0 +#define GPIO_PX_DIR_OUT 1 + +#define GPIO_PX_PU_DISABLE 0 +#define GPIO_PX_PU_ENABLE 1 +#define GPIO_PX_PD_DISABLE 0 // for DIR_IN +#define GPIO_PX_PD_ENABLE 1 // for DIR_IN +#define GPIO_PX_PD_PUSH_PULL 0 // for DIR_OUT +#define GPIO_PX_PD_OPEN_DRAIN 1 // for DIR_OUT + +#define GPIO_PX_DRV_8mA 0 +#define GPIO_PX_DRV_16mA 1 + +#define GPIO_PX_SMT_DISABLE 0 +#define GPIO_PX_SMT_SLOW 1 // for DIR_OUT +#define GPIO_PX_SMT_ENABLE 1 // for DIR_IN + +/* + * 0x12 R8_PIN_ALTERNATE: GPIO multi-use remapping register + * 0x1c R8_GPIO_INT_FLAG: GPIO interrupt flag register + * 0x1d R8_GPIO_INT_ENABLE: GPIO interrupt enable register + * 0x1e R8_GPIO_INT_MODE: GPIO interrupt mode register + * 0x1f R8_GPIO_INT_POLAR: GPIO interrupt polarity register + * + * 0x40 R32_PA_DIR: PA pin direction control + * 0x44 R32_PA_PIN: PA pin input status + * 0x48 R32_PA_OUT: PA pin output register + * 0x4c R32_PA_CLR: PA pin output clear + * 0x50 R32_PA_PU: PA pin pull-up resistor enable register + * 0x54 R32_PA_PD: PA pin open drain output / input pull-down control + * 0x58 R32_PA_DRV: PA pin output driving capability register + * 0x5c R32_PA_SMT: PA pin slow output / schmitt trigger input control + * + * 0x60 R32_PB_DIR: + * 0x64 R32_PB_PIN: + * 0x68 R32_PB_OUT: + * 0x6c R32_PB_CLR: + * 0x70 R32_PB_PU: + * 0x74 R32_PB_PD: + * 0x78 R32_PB_DRV: + * 0x7c R32_PB_SMT: + * + * CAVEAT: gcc (as of 8.2.0) tends to read 32-bit word for bit field test. + * Be careful for those with side effect for read. + */ +struct gpio_registers +{ + uint32_t resv_00[4]; + uint8_t resv_10[2]; + union _gpio_pin_alternate PIN_ALTERNATE; + uint8_t resv_13; + uint32_t resv_14[2]; + union _gpio_interrupt INT_FLAG; + union _gpio_interrupt INT_ENABLE; + union _gpio_interrupt INT_MODE; + union _gpio_interrupt INT_POLAR; + uint32_t resv_20[8]; + struct gpio_px_regs PA; + struct gpio_px_regs PB; +} __packed; + +CHECK_STRUCT_SIZE(struct gpio_registers, 0x80); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/bsp/wch/risc-v/Libraries/ch56x_drivers/ch56x_pfic.c b/bsp/wch/risc-v/Libraries/ch56x_drivers/ch56x_pfic.c new file mode 100644 index 0000000000..9f01264006 --- /dev/null +++ b/bsp/wch/risc-v/Libraries/ch56x_drivers/ch56x_pfic.c @@ -0,0 +1,119 @@ +/* + * Copyright (c) 2006-2022, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2022-07-15 Emuzit first version + */ +#include +#include +#include "ch56x_pfic.h" +#include "ch56x_sys.h" +#include "isr_sp.h" + +void rt_hw_interrupt_mask(int vector) +{ + pfic_interrupt_mask(vector); +} + +void rt_hw_interrupt_umask(int vector) +{ + pfic_interrupt_umask(vector); +} + +/** + * @brief Trigger software interrupt. + */ +void pfic_swi_pendset(void) +{ + volatile struct pfic_registers *pfic = (void *)PFIC_REG_BASE; + _pfic_ireg_bit_set(pfic, IPSR, SWI_IRQn); +} + +/** + * @brief Clear software interrupt. + */ +void pfic_swi_pendreset(void) +{ + volatile struct pfic_registers *pfic = (void *)PFIC_REG_BASE; + _pfic_ireg_bit_set(pfic, IPRR, SWI_IRQn); +} + +/** + * @brief Write PFIC interrupt configuration register. + * + * @param key_bit is (PFIC_CFGR_KEYx + bit_position), one of the following : + * PFIC_CFGR_NMISET / PFIC_CFGR_NMIRESET + * PFIC_CFGR_EXCSET / PFIC_CFGR_EXCRESET + * PFIC_CFGR_PFICRESET + * PFIC_CFGR_SYSRESET + * All others are treated as NEST/HWSTK (B.1/B.0) write. + */ +void pfic_cfgr_set(uint32_t key_bit) +{ + volatile struct pfic_registers *pfic = (void *)PFIC_REG_BASE; + uint32_t u32v; + + switch (key_bit) + { + case PFIC_CFGR_NMISET: + case PFIC_CFGR_NMIRESET: + case PFIC_CFGR_EXCSET: + case PFIC_CFGR_EXCRESET: + case PFIC_CFGR_PFICRESET: + case PFIC_CFGR_SYSRESET: + pfic->CFGR = key_bit; + default: + /* B.1/B.0 hold NEST/HWSTK, key ignored */ + u32v = key_bit & (CFGR_NESTCTRL_MASK | CFGR_HWSTKCTRL_MASK); + pfic->CFGR = cfgr_nest_hwstk(u32v); + } +} + +/** + * @brief Make SysTick ready, systick/swi irq are enabled. + * + * @param count is (HCLK/8) clocks count to generate systick irq. + * if 0 => calculate with current HCLK and RT_TICK_PER_SECOND + */ +void systick_init(uint32_t count) +{ + volatile struct systick_registers *systick = (void *)SysTick_REG_BASE; + volatile struct pfic_registers *pfic = (void *)PFIC_REG_BASE; + + if (count == 0) + count = sys_hclk_get() / 8 / RT_TICK_PER_SECOND; + + _pfic_irqn_disable(pfic, SysTick_IRQn); + pfic->IPRIOR[SysTick_IRQn] = 0xe0; + pfic->IPRIOR[SWI_IRQn] = 0xf0; + systick->CTLR.reg = 0; + systick->CNTL = 0; + systick->CNTH = 0; + systick->CMPLR = count - 1; + systick->CMPHR = 0; + systick->CNTFG.cntif = 0; + /* enable & reload SysTick, with HCLK/8 */ + systick->CTLR.reg = RB_STKCTL_STRELOAD | RB_STKCTL_STIE | RB_STKCTL_STE; + _pfic_irqn_enable(pfic, SysTick_IRQn); + _pfic_irqn_enable(pfic, SWI_IRQn); +} + +void systick_handler(void) __attribute__((interrupt())); +void systick_handler(void) +{ + volatile struct systick_registers *systick; + + isr_sp_enter(); + rt_interrupt_enter(); + + rt_tick_increase(); + systick = (struct systick_registers *)SysTick_REG_BASE; + /* clear count-to-zero flag */ + systick->CNTFG.cntif = 0; + + rt_interrupt_leave(); + isr_sp_leave(); +} diff --git a/bsp/wch/risc-v/Libraries/ch56x_drivers/ch56x_pfic.h b/bsp/wch/risc-v/Libraries/ch56x_drivers/ch56x_pfic.h new file mode 100644 index 0000000000..be3c652074 --- /dev/null +++ b/bsp/wch/risc-v/Libraries/ch56x_drivers/ch56x_pfic.h @@ -0,0 +1,258 @@ +/* + * Copyright (c) 2006-2022, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2022-07-15 Emuzit first version + */ +#ifndef __CH56X_PFIC_H__ +#define __CH56X_PFIC_H__ + +#include "soc.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* IREG: IENR/IRER/IRSR/IPRR, make sure irqn be 12~59 +*/ +#define PFIC_IREG1_MASK 0xfffff000 +#define PFIC_IREG2_MASK 0x0fffffff +#define PFIC_MAX_IREG_BITS 60 + +#define _pfic_ireg_bit_get(pfic, IREG, irqn) \ + ((pfic->IREG[(irqn) >> 5] >> ((irqn) & 0x1f)) & 1) + +#define _pfic_ireg_bit_set(pfic, IREG, irqn) \ + do pfic->IREG[(irqn) >> 5] = 1 << ((irqn) & 0x1f); while(0) + +#define _pfic_irqn_enable(pfic, irqn) _pfic_ireg_bit_set(pfic, IENR, irqn) +#define _pfic_irqn_disable(pfic, irqn) _pfic_ireg_bit_set(pfic, IRER, irqn) + +/* Note: `union _pfic_cfgr` is not used directly in the reg structure */ +union _pfic_cfgr +{ + uint32_t reg; + struct + { + uint32_t hwstkctrl : 1; // RW, hw stack push/pop control (0:enable) + uint32_t nestctrl : 1; // RW, nested intr enable control (0:enable) + uint32_t nmiset : 1; // WO, write 1 to set NMI pending + uint32_t nmireset : 1; // WO, write 1 to reset NMI pending + uint32_t excset : 1; // WO, write 1 to set exception pending + uint32_t excreset : 1; // WO, write 1 to reset exception pending + uint32_t pficreset : 1; // WO, write 1 to reset PFIC module, auto clear + uint32_t sysreset : 1; // WO, write 1 for a system reset, auto clear + uint32_t resv_8 : 8; + uint32_t keycode : 16; // WO, write protection keycode + }; +}; +#define PFIC_CFGR_KEY1 0xfa05 // for hwstkctrl & nestctrl +#define PFIC_CFGR_KEY2 0xbcaf // for nmi & exc set/reset +#define PFIC_CFGR_KEY3 0xbeef // for sysreset + +#define PFIC_CFGR_NMISET (PFIC_CFGR_KEY2 << 16 | 1 << 2) +#define PFIC_CFGR_NMIRESET (PFIC_CFGR_KEY2 << 16 | 1 << 3) +#define PFIC_CFGR_EXCSET (PFIC_CFGR_KEY2 << 16 | 1 << 4) +#define PFIC_CFGR_EXCRESET (PFIC_CFGR_KEY2 << 16 | 1 << 5) +#define PFIC_CFGR_PFICRESET (1 << 6) +#define PFIC_CFGR_SYSRESET (PFIC_CFGR_KEY3 << 16 | 1 << 7) + +#define CFGR_HWSTKCTRL_ENABLE (0 << 0) +#define CFGR_HWSTKCTRL_DISABLE (1 << 0) +#define CFGR_HWSTKCTRL_MASK (1 << 0) +#define CFGR_NESTCTRL_ENABLE (0 << 1) +#define CFGR_NESTCTRL_DISABLE (1 << 1) +#define CFGR_NESTCTRL_MASK (1 << 1) +#define cfgr_nest_hwstk(v) (PFIC_CFGR_KEY1 << 16 | (v)) + +union _pfic_gisr +{ + uint32_t reg; + struct + { + uint32_t neststa : 8; // RO, nested interrupt state (0/1/2) + uint32_t gactsta : 1; // RO, global interrupt active status + uint32_t gpendsta : 1; // RO, global interrupt pending status + uint32_t resv_10 : 22; + }; +}; +#define PFIC_NESTSTA_NONE 0 +#define PFIC_NESTSTA_L1 1 +#define PFIC_NESTSTA_L2 2 + +union _pfic_fiofaddrr +{ + uint32_t reg; + struct + { + uint32_t offaddr : 24; // RW + uint32_t irqid : 8; // RW + }; +}; + +union _pfic_sctlr +{ + uint32_t reg; + struct + { + uint32_t resv_0 : 1; + uint32_t sleeponexit : 1; // enter (deep) sleep mode on isr exiting + uint32_t sleepdeep : 1; // RW, 0/1 for sleep/deep-sleep mode + uint32_t wfitowfe : 1; // RW, treat WFI as WFE + uint32_t sevonpend : 1; // RW + uint32_t setevent : 1; // WO, set event for WFE + uint32_t resv_6 : 26; + }; +}; + +/* + * 0x000 R32_PFIC_ISR1: PFIC interrupt enable status (# 12-31) + * 0x004 R32_PFIC_ISR2: PFIC interrupt enable status (# 32-59) + * 0x020 R32_PFIC_IPR1: PFIC interrupt pending status (# 12-31) + * 0x024 R32_PFIC_IPR2: PFIC interrupt pending status (# 32-59) + * 0x040 R32_PFIC_ITHRESDR: PFIC interrupt priority threshold (B.7-4) + * 0x044 R32_PFIC_FIBADDRR: PFIC fast intr base address (B.31-28) + * 0x048 R32_PFIC_CFGR: PFIC interrupt configuration register + * 0x04c R32_PFIC_GISR: PFIC global interrupt status register + * 0x060 R32_PFIC_FIOFADDRR0: PFIC fast intr 0 offset address (B.23-0) + * 0x064 R32_PFIC_FIOFADDRR1: PFIC fast intr 1 offset address (B.23-0) + * 0x068 R32_PFIC_FIOFADDRR2: PFIC fast intr 2 offset address (B.23-0) + * 0x06c R32_PFIC_FIOFADDRR3: PFIC fast intr 3 offset address (B.23-0) + * 0x100 R32_PFIC_IENR1: PFIC interrupt enable register (# 12-31) + * 0x104 R32_PFIC_IENR2: PFIC interrupt enable register (# 32-59) + * 0x180 R32_PFIC_IRER1: PFIC interrupt reset register (# 12-31) + * 0x184 R32_PFIC_IRER2: PFIC interrupt reset register (# 32-59) + * 0x200 R32_PFIC_IPSR1: PFIC interrupt pending set register (# 12-31) + * 0x204 R32_PFIC_IPSR2: PFIC interrupt pending set register (# 32-59) + * 0x280 R32_PFIC_IPRR1: PFIC interrupt pending reset register (# 12-31) + * 0x284 R32_PFIC_IPRR2: PFIC interrupt pending reset register (# 32-59) + * 0x300 R32_PFIC_IACTR1: PFIC interrupt active status register (# 12-31) + * 0x304 R32_PFIC_IACTR2: PFIC interrupt active status register (# 32-59) + * 0x400 R32_PFIC_IPRIORx: PFIC interrupt priority registers + * 0xd10 R32_PFIC_SCTLR: PFIC system control register + * + * CAVEAT: gcc (as of 8.2.0) tends to read 32-bit word for bit field test. + * Be careful for those with side effect for read. + */ +struct pfic_registers +{ + uint32_t ISR[2]; + uint32_t resv_08[6]; + uint32_t IPR[2]; + uint32_t resv_28[6]; + uint32_t ITHRESDR; + uint32_t FIBADDRR; + uint32_t CFGR; + union _pfic_gisr GISR; + uint32_t resv_50[4]; + union _pfic_fiofaddrr FIOFADDRR[4]; + uint32_t resv_70[36]; + uint32_t IENR[2]; + uint32_t resv_108[30]; + uint32_t IRER[2]; + uint32_t resv_188[30]; + uint32_t IPSR[2]; + uint32_t resv_208[30]; + uint32_t IPRR[2]; + uint32_t resv_288[30]; + uint32_t IACTR[2]; + uint32_t resv_308[62]; + uint8_t IPRIOR[256]; + uint32_t resv_500[516]; + union _pfic_sctlr SCTLR; +} __packed; + +CHECK_STRUCT_SIZE(struct pfic_registers, 0xd14); + +union _systick_ctlr +{ + uint32_t reg; + struct + { + uint32_t ste : 1; // RW, systick enable + uint32_t stie : 1; // RW, systick interrupt enable + uint32_t stclk : 1; // RW, systick clock source select + uint32_t resv_3 : 5; + uint32_t streload : 1; // W1, write 1 to reload systick + uint32_t resv_9 : 23; + }; +}; +#define RB_STKCTL_STE 0x001 +#define RB_STKCTL_STIE 0x002 +#define RB_STKCTL_STCLK 0x004 +#define RB_STKCTL_STRELOAD 0x100 + +#define SYSTICK_SRC_HCLK_8 0 +#define SYSTICK_SRC_HCLK 1 + +union _systick_cntfg +{ + uint32_t reg; + struct + { + uint32_t swie : 1; // RW, software interrupt enable + uint32_t cntif : 1; // RW0, counter dec to 0 (write 0 to clear) + uint32_t resv_2 : 30; + }; +}; +#define RB_CNTFG_SWIE 0X01 +#define RB_CNTFG_CNTIF 0X02 + +/* + * 0x00 R32_STK_CTLR: SysTick control register + * 0x04 R32_STK_CNTL: SysTick counter, Lo.32 + * 0x08 R32_STK_CNTH: SysTick counter, Hi.32 + * 0x0c R32_STK_CMPLR: SysTick compare-reload register, Lo.32 + * 0x10 R32_STK_CMPHR: SysTick compare-reload register, Hi.32 + * 0x14 R32_STK_CNTFG: SysTick counter flags + * + * CAVEAT: gcc (as of 8.2.0) tends to read 32-bit word for bit field test. + * Be careful for those with side effect for read. + */ +struct systick_registers +{ + union _systick_ctlr CTLR; + uint32_t CNTL; + uint32_t CNTH; + uint32_t CMPLR; + uint32_t CMPHR; + union _systick_cntfg CNTFG; +} __packed; + +CHECK_STRUCT_SIZE(struct systick_registers, 0x18); + +rt_inline void pfic_interrupt_umask(uint8_t irqn) +{ + volatile struct pfic_registers *pfic; + + if (irqn < PFIC_MAX_IREG_BITS) + { + pfic = (void *)PFIC_REG_BASE; + _pfic_ireg_bit_set(pfic, IENR, irqn); + } +} + +rt_inline void pfic_interrupt_mask(uint8_t irqn) +{ + volatile struct pfic_registers *pfic; + + if (irqn < PFIC_MAX_IREG_BITS) + { + pfic = (void *)PFIC_REG_BASE; + _pfic_ireg_bit_set(pfic, IRER, irqn); + } +} + +void pfic_swi_pendset(void); +void pfic_swi_pendreset(void); +void pfic_cfgr_set(uint32_t key_bit); +void systick_init(uint32_t count); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/bsp/wch/risc-v/Libraries/ch56x_drivers/ch56x_sys.c b/bsp/wch/risc-v/Libraries/ch56x_drivers/ch56x_sys.c new file mode 100644 index 0000000000..51305e4a51 --- /dev/null +++ b/bsp/wch/risc-v/Libraries/ch56x_drivers/ch56x_sys.c @@ -0,0 +1,249 @@ +/* + * Copyright (c) 2006-2022, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2022-07-15 Emuzit first version + */ +#include +#include +#include "ch56x_sys.h" + +rt_inline uint8_t _slp_clk_off0_irqn_bit(uint8_t irqn) +{ + uint8_t bitpos; + + switch (irqn) + { + case TMR0_IRQn: bitpos = RB_SLP_CLK_TMR0; break; + case TMR1_IRQn: bitpos = RB_SLP_CLK_TMR1; break; + case TMR2_IRQn: bitpos = RB_SLP_CLK_TMR2; break; + /* special case to control PWMX clock in irqn way */ + case PWMX_OFFn: bitpos = RB_SLP_CLK_PWMX; break; + case UART0_IRQn: bitpos = RB_SLP_CLK_UART0; break; + case UART1_IRQn: bitpos = RB_SLP_CLK_UART1; break; + case UART2_IRQn: bitpos = RB_SLP_CLK_UART2; break; + case UART3_IRQn: bitpos = RB_SLP_CLK_UART3; break; + default: + bitpos = 0; + } + + return bitpos; +} + +rt_inline uint8_t _slp_clk_off1_irqn_bit(uint8_t irqn) +{ + uint8_t bitpos; + + switch (irqn) + { + case SPI0_IRQn: bitpos = RB_SLP_CLK_SPI0; break; + case SPI1_IRQn: bitpos = RB_SLP_CLK_SPI1; break; +#if defined(SOC_CH567) + case SDC_IRQn: bitpos = RB_SLP_CLK_SDC; break; + case LED_IRQn: bitpos = RB_SLP_CLK_LED; break; + case USB0_IRQn: bitpos = RB_SLP_CLK_USB0; break; + case USB1_IRQn: bitpos = RB_SLP_CLK_USB1; break; + case ECDC_IRQn: bitpos = RB_SLP_CLK_ECDC; break; +#elif defined(SOC_CH568) + case SDC_IRQn: bitpos = RB_SLP_CLK_SDC; break; + case LED_IRQn: bitpos = RB_SLP_CLK_LED; break; + case USB1_IRQn: bitpos = RB_SLP_CLK_USB1; break; + case USB0_IRQn: bitpos = RB_SLP_CLK_SATA; break; + case ECDC_IRQn: bitpos = RB_SLP_CLK_ECDC; break; +#else + case EMMC_IRQn: bitpos = RB_SLP_CLK_EMMC; break; + case HSPI_IRQn: bitpos = RB_SLP_CLK_HSPI; break; + case USBHS_IRQn: bitpos = RB_SLP_CLK_USBHS; break; + case USBSS_IRQn: bitpos = RB_SLP_CLK_USBSS; break; + case SerDes_IRQn: bitpos = RB_SLP_CLK_SERD; break; + case DVP_IRQn: bitpos = RB_SLP_CLK_DVP; break; +#endif + default: + bitpos = 0; + } + + return bitpos; +} + +#if defined(SOC_SERIES_CH569) +rt_inline uint8_t _wake_clk_off_irqn_bit(uint8_t irqn) +{ + uint8_t bitpos; + + switch (irqn) + { + case ETH_IRQn: bitpos = RB_SLP_CLK_ETH; break; + case ECDC_IRQn: bitpos = RB_SLP_CLK_ECDC; break; + default: + bitpos = 0; + } + + return bitpos; +} +#endif + +/** + * @brief Turn on/off device clock for group clk_off0. + * + * @param bits is a bit mask to select corresponding devices. + * + * @param off is to turn off the clock (1) or trun on (0). + */ +void sys_slp_clk_off0(uint8_t bits, int off) +{ + volatile struct sys_registers *sys = (void *)SYS_REG_BASE; + rt_base_t level; + uint8_t u8v; + + u8v = sys->SLP_CLK_OFF0.reg; + u8v = off ? (u8v | bits) : (u8v & ~bits); + level = rt_hw_interrupt_disable(); + sys_safe_access_enter(sys); + sys->SLP_CLK_OFF0.reg = u8v; + sys_safe_access_leave(sys); + rt_hw_interrupt_enable(level); +} + +/** + * @brief Turn on/off device clock for group clk_off1. + * + * @param bits is a bit mask to select corresponding devices. + * + * @param off is to turn off the clock (1) or trun on (0). + */ +void sys_slp_clk_off1(uint8_t bits, int off) +{ + volatile struct sys_registers *sys = (void *)SYS_REG_BASE; + rt_base_t level; + uint8_t u8v; + + u8v = sys->SLP_CLK_OFF1.reg; + u8v = off ? (u8v | bits) : (u8v & ~bits); + level = rt_hw_interrupt_disable(); + sys_safe_access_enter(sys); + sys->SLP_CLK_OFF1.reg = u8v; + sys_safe_access_leave(sys); + rt_hw_interrupt_enable(level); +} + +/** + * @brief Turn on/off device clock, specified by its irq number. + * + * @param irqn is the irq number of the target device. + * PWMX does not have irqn, use special PWMX_OFFn number. + * + * @param off is to turn off the clock (1) or trun on (0). + * + * @return Returns if irqn-device has corresponding clk off bit : + * 0 if device not found; otherwise bit position of off0/off1. + */ +int sys_clk_off_by_irqn(uint8_t irqn, int off) +{ + volatile struct sys_registers *sys = (void *)SYS_REG_BASE; + uint8_t u8v; + size_t offset; + + uint8_t bitpos = 0; + + if (irqn < END_OF_IRQn) + { + if ((bitpos = _slp_clk_off0_irqn_bit(irqn)) != 0) + { + offset = offsetof(struct sys_registers, SLP_CLK_OFF0); + } + else if ((bitpos = _slp_clk_off1_irqn_bit(irqn)) != 0) + { + offset = offsetof(struct sys_registers, SLP_CLK_OFF1); + } +#if defined(SOC_SERIES_CH569) + else if ((bitpos = _wake_clk_off_irqn_bit(irqn)) != 0) + { + offset = offsetof(struct sys_registers, SLP_WAKE_CTRL); + } +#endif + if (bitpos) + { + volatile uint8_t *cxreg = (void *)sys; + rt_base_t level; + u8v = cxreg[offset]; + u8v = off ? (u8v | bitpos) : (u8v & ~bitpos); + level = rt_hw_interrupt_disable(); + sys_safe_access_enter(sys); + cxreg[offset] = u8v; + sys_safe_access_leave(sys); + rt_hw_interrupt_enable(level); + } + } + + return bitpos; +} + +/** + * @brief Setup HCLK frequency. + * + * @param freq is the desired hclk frequency. + * supported : 120/96/80/60/48/40/32/30/15/10/6/3/2 MHz + * + * @return Returns 0 if hclk is successfully set. + */ +int sys_hclk_set(uint32_t freq) +{ + volatile struct sys_registers *sys = (void *)SYS_REG_BASE; + + uint8_t plldiv; + + int clksel = -1; + + if (freq >= 30000000) + { + if (freq <= 120000000) + { + /* supported : 120/96/80/60/48/40/32/30 MHz */ + plldiv = 480000000 / freq; // 30M => 16 & 0xf => 0 + clksel = RB_CLK_SEL_PLL; + } + } + else if (freq >= 2000000) + { + /* supported : 15/10/6/3/2 MHz */ + plldiv = 30000000 / freq; + clksel = 0; + } + + if (clksel >= 0) + { + rt_base_t level = rt_hw_interrupt_disable(); + sys_safe_access_enter(sys); + sys->CLK_PLL_DIV.reg = clk_pll_div_wdat(plldiv); + sys->CLK_CFG_CTRL.reg = clk_cfg_ctrl_wdat(clksel); + sys_safe_access_leave(sys); + rt_hw_interrupt_enable(level); + clksel = 0; + } + + return clksel; +} + +/** + * @brief Get current HCLK frequency. + * + * @return Returns current HCLK frequency (Hz). + */ +uint32_t sys_hclk_get(void) +{ + volatile struct sys_registers *sys = (void *)SYS_REG_BASE; + + uint8_t plldiv = sys->CLK_PLL_DIV.pll_div; + + if (sys->CLK_CFG_CTRL.sel_pll == CLK_SEL_PLL_USB_480M) + { + return plldiv ? 480000000 / plldiv : 30000000; + } + else + { + return plldiv ? 30000000 / plldiv : 2000000; + } +} diff --git a/bsp/wch/risc-v/Libraries/ch56x_drivers/ch56x_sys.h b/bsp/wch/risc-v/Libraries/ch56x_drivers/ch56x_sys.h new file mode 100644 index 0000000000..9b2b7b3e54 --- /dev/null +++ b/bsp/wch/risc-v/Libraries/ch56x_drivers/ch56x_sys.h @@ -0,0 +1,395 @@ +/* + * Copyright (c) 2006-2022, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2022-07-15 Emuzit first version + */ +#ifndef __CH56X_SYS_H__ +#define __CH56X_SYS_H__ + +#include "soc.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define sys_safe_access_enter(sys) \ + do { \ + sys->SAFE_ACCESS_SIG.reg = SAFE_ACCESS_SIG_1; \ + sys->SAFE_ACCESS_SIG.reg = SAFE_ACCESS_SIG_2; \ + } while(0) + +#define sys_safe_access_leave(sys) \ + do sys->SAFE_ACCESS_SIG.reg = 0; while(0) + +union _sys_safe_access_sig +{ + uint8_t reg; + struct + { + uint8_t safe_acc_mode : 2; // RO, current safe access, 11b => RWA ok + uint8_t resv_2 : 2; + uint8_t safe_acc_timer : 3; // RO, current safe access time count + uint8_t resv_7 : 1; + }; +}; +#define RB_SAFE_ACC_MODE 0x03 +#define RB_SAFE_ACC_TIMER 0x70 + +#define SAFE_ACCESS_SIG_1 0x57 +#define SAFE_ACCESS_SIG_2 0xa8 + +union _sys_glob_rom_cfg +{ + uint8_t reg; + struct + { + uint8_t rom_ext_re : 1; // RO, allow programmer to read FlashROM + uint8_t code_ram_we : 1; // RWA, code SRAM writaboe + uint8_t rom_data_we : 1; // RWA, FlashROM data erasable/writable + uint8_t rom_code_we : 1; // RWA, FlashROM code erasable/writable + uint8_t rom_code_ofs : 1; // RWA, FlashROM offset for user code + uint8_t resv_5 : 3; + }; +}; +#define RB_ROM_EXT_RE 0x01 +#define RB_CODE_RAM_WE 0x02 +#define RB_ROM_DATA_WE 0x04 +#define RB_ROM_CODE_WE 0x08 +#define RB_ROM_CODE_OFS 0x10 + +#define ROM_CODE_OFS_0x00000 0 +#define ROM_CODE_OFS_0x04000 1 + +union _sys_rst_boot_stat +{ + uint8_t reg; + struct + { + uint8_t reset_flag : 2; // RO, last reset cause + uint8_t cfg_reset_en : 1; // RO, external reset pin (#RST) status + uint8_t cfg_boot_en : 1; // RO, reset as 1 + uint8_t cfg_debug_en : 1; // RO + uint8_t boot_loader : 1; // RO + uint8_t resv_6 : 2; + }; +}; +#define RB_RESET_FLAG 0x03 +#define RB_CFG_RESET_EN 0x04 +#define RB_CFG_BOOT_EN 0x08 +#define RB_CFG_DEBUG_EN 0x10 +#define RB_BOOT_LOADER 0x20 + +#define RESET_FLAG_IS_SOFT 0 +#define RESET_FLAG_IS_POR 1 +#define RESET_FLAG_IS_WDOG 2 +#define RESET_FLAG_IS_RSTPIN 3 + +union _sys_rst_wdog_ctrl +{ + uint8_t reg; + struct + { + uint8_t software_reset : 1; // WA/WZ, system software reset, auto clear +#if defined(SOC_SERIES_CH569) + uint8_t wdog_rst_en : 1; // RWA, enable watchdog overflow to reset + uint8_t wdog_int_en : 1; // RWA, enable watchdog overflow interrupt + uint8_t wdog_int_flag : 1; // RW1, watchdog counter overflow +#else + uint8_t resv_2 : 3; +#endif + uint8_t resv_4 : 4; // RO, B.7-6 must write 01b + }; +}; +#define RB_SOFTWARE_RESET 0x01 +#ifdef SOC_SERIES_CH569 +#define RB_WDOG_RST_EN 0x02 +#define RB_WDOG_INT_EN 0x04 +#define RB_WDOG_INT_FLAG 0x08 +#endif +#define wdog_ctrl_wdat(v) (0x40 | (v)) + +union _sys_clk_pll_div +{ + uint8_t reg; + struct + { + uint8_t pll_div : 4; // RWA, min 2 + uint8_t resv_4 : 4; // RWA, B.7-6 must write 01b + }; +}; +#define clk_pll_div_wdat(div) (0x40 | (div)) + +union _sys_clk_cfg_ctrl +{ + uint8_t reg; + struct + { + uint8_t pll_sleep : 1; // RWA, PLL sleep control + uint8_t sel_pll : 1; // RWA, clock source select + uint8_t resv_6 : 6; // RWA, must write 10b + }; +}; +#define RB_CLK_PLL_SLEEP 0x01 +#define RB_CLK_SEL_PLL 0x02 + +#define CLK_PLL_SLEEP_DISABLE 0 +#define CLK_PLL_SLEEP_ENABLE 1 +#define CLK_SEL_PLL_HSE_30M 0 +#define CLK_SEL_PLL_USB_480M 1 +#define clk_cfg_ctrl_wdat(v) (0x80 | (v)) + +union _sys_clk_mod_aux +{ + uint8_t reg; + struct + { + uint8_t int_125m_en : 1; // RWA, USB PHY 125MHz to ETH + uint8_t ext_125m_en : 1; // RWA, external 125MHz to ETH + uint8_t mco_sel_msk : 2; // RWA, MCO output select + uint8_t mco_en : 1; // RWA, MCO output enable + uint8_t resv_5 : 3; + }; +}; +#define RB_INT_125M_EN 0x01 +#define RB_EXT_125M_EN 0x02 +#define RB_MCO_SEL_MSK 0x0c +#define RB_MCO_EN 0x10 + +#define MCO_SEL_MSK_125M 0 +#define MCO_SEL_MSK_25M 1 +#define MCO_SEL_MSK_2_5M 2 + +/* All bits are RWA (need safe_access_sig), 0/1 : clock on/off +*/ +union _sys_slp_clk_off0 +{ + uint8_t reg; + struct + { + uint8_t tmr0 : 1; + uint8_t tmr1 : 1; + uint8_t tmr2 : 1; + uint8_t pwmx : 1; + uint8_t uart0 : 1; + uint8_t uart1 : 1; + uint8_t uart2 : 1; + uint8_t uart3 : 1; + }; +}; +#define RB_SLP_CLK_TMR0 0x01 +#define RB_SLP_CLK_TMR1 0x02 +#define RB_SLP_CLK_TMR2 0x04 +#define RB_SLP_CLK_PWMX 0x08 +#define RB_SLP_CLK_UART0 0x10 +#define RB_SLP_CLK_UART1 0x20 +#define RB_SLP_CLK_UART2 0x40 +#define RB_SLP_CLK_UART3 0x80 + +#define SYS_SLP_CLK_ON 0 +#define SYS_SLP_CLK_OFF 1 + +/* All writable bits are RWA (need safe_access_sig), 0/1 : clock on/off +*/ +union _sys_slp_clk_off1 +{ + uint8_t reg; + struct + { + uint8_t spi0 : 1; + uint8_t spi1 : 1; +#if defined(SOC_CH567) + uint8_t sdc : 1; + uint8_t led : 1; + uint8_t usb0 : 1; + uint8_t usb1 : 1; + uint8_t resv_6 : 1; +#elif defined(SOC_CH568) + uint8_t sdc : 1; + uint8_t led : 1; + uint8_t resv_4 : 1; + uint8_t usb1 : 1; + uint8_t sata : 1; + uint8_t ecdc : 1; +#else + uint8_t emmc : 1; + uint8_t hspi : 1; + uint8_t usbhs : 1; + uint8_t usbss : 1; + uint8_t serd : 1; + uint8_t dvp : 1; +#endif + }; +}; +#define RB_SLP_CLK_SPI0 0x01 +#define RB_SLP_CLK_SPI1 0x02 +#if defined(SOC_WCH_CH567) +#define RB_SLP_CLK_SDC 0x04 +#define RB_SLP_CLK_LED 0x08 +#define RB_SLP_CLK_USB0 0x10 +#define RB_SLP_CLK_USB1 0x20 +#define RB_SLP_CLK_ECDC 0x80 +#elif defined(SOC_WCH_CH568) +#define RB_SLP_CLK_SDC 0x04 +#define RB_SLP_CLK_LED 0x08 +#define RB_SLP_CLK_USB1 0x20 +#define RB_SLP_CLK_SATA 0x40 +#define RB_SLP_CLK_ECDC 0x80 +#else +#define RB_SLP_CLK_EMMC 0x04 +#define RB_SLP_CLK_HSPI 0x08 +#define RB_SLP_CLK_USBHS 0x10 +#define RB_SLP_CLK_USBSS 0x20 +#define RB_SLP_CLK_SERD 0x40 +#define RB_SLP_CLK_DVP 0x80 +#endif + +/* All writable bits are RWA (need safe_access_sig) +*/ +union _sys_slp_wake_ctrl +{ + uint8_t reg; + struct + { +#if defined(SOC_WCH_CH567) + uint8_t usb0_wake : 1; + uint8_t usb1_wake : 1; + uint8_t resv_2 : 2; + uint8_t gpio_wake : 1; + uint8_t resv_5 : 3; +#elif defined(SOC_WCH_CH568) + uint8_t resv_0 : 1; + uint8_t usb1_wake : 1; + uint8_t sata_wake : 1; + uint8_t resv_3 : 1; + uint8_t gpio_wake : 1; + uint8_t resv_5 : 3; +#else + uint8_t usbhs_wake : 1; + uint8_t usbss_wake : 1; + uint8_t clk_eth : 1; + uint8_t clk_ecdc : 1; + uint8_t gpio_wake : 1; + uint8_t eth_wake : 1; + uint8_t resv_6 : 2; +#endif + }; +}; +#if defined(SOC_WCH_CH567) +#define RB_SLP_USB0_WAKE 0x01 +#define RB_SLP_USB1_WAKE 0x02 +#define RB_SLP_GPIO_WAKE 0x10 +#elif defined(SOC_WCH_CH568) +#define RB_SLP_USB1_WAKE 0x02 +#define RB_SLP_SATA_WAKE 0x04 +#define RB_SLP_GPIO_WAKE 0x10 +#else +#define RB_SLP_USBHS_WAKE 0x01 +#define RB_SLP_USBSS_WAKE 0x02 +#define RB_SLP_CLK_ETH 0x04 +#define RB_SLP_CLK_ECDC 0x08 +#define RB_SLP_GPIO_WAKE 0x10 +#define RB_SLP_ETH_WAKE 0x20 +#endif + +union _sys_slp_power_ctrl +{ + uint8_t reg; + struct + { + uint8_t usbhs_pwrdn : 1; // RWA, USBHS power down (0:PWRUP) + uint8_t resv_2 : 7; + }; +}; +#define RB_SLP_USBHS_PWRDN 0x01 + +union _sys_serd_ana_cfg1 +{ + uint16_t reg; + struct + { + uint8_t serd_pll_cfg; // RWA, reset as 0x5a + uint8_t serd_30m_sel : 1; // RWA + uint8_t serd_dn_tst : 1; // RWA + uint8_t resv_10 : 6; + }; +}; +#define RB_SERD_PLL_CFG 0x0ff +#define RB_SERD_30M_SEL 0x100 +#define RB_SERD_DN_TST 0x200 + +union _sys_serd_ana_cfg2 +{ + uint32_t reg; + struct + { + uint32_t serd_trx_cfg : 25; // RWA, reset as 423015h + uint32_t resv_25 : 7; + }; +}; +#define RB_SERD_TRX_CFG 0x1000000 + +/* + * 0x00 R8_SAFE_ACCESS_SIG: safe access signature register + * 0x01 R8_CHIP_ID: RF, chip ID register + * 0x02 R8_SAFE_ACCESS_ID: RF, read as 02h + * 0x03 R8_WDOG_COUNT RW, watchdog counter + * 0x04 R8_GLOB_ROM_CFG: ROM config register + * 0x05 R8_RST_BOOT_STAT: RO, boot state register + * 0x06 R8_RST_WDOG_CTRL: software reset & watchdog control register + * 0x07 R8_GLOB_RESET_KEEP: RW, only power-on-reset can clear this register + * 0x08 R8_CLK_PLL_DIV: RWA, PLL output divisor register + * 0x0a R8_CLK_CFG_CTRL: RWA, clock config register + * 0x0b R8_CLK_MOD_AUX: RWA, clock auxiliary register + * 0x0c R8_SLP_CLK_OFF0: RWA, sleep control register 0 + * 0x0d R8_SLP_CLK_OFF1: RWA, sleep control register 1 + * 0x0e R8_SLP_WAKE_CTRL: RWA, wakeup control register + * 0x0f R8_SLP_POWER_CTRL: RWA, low power management register + * 0x20 R16_SERD_ANA_CFG1: RWA, SerDes PHY analog param config register 1 + * 0x24 R32_SERD_ANA_CFG2: RWA, SerDes PHY analog param config register 2 + * + * CAVEAT: gcc (as of 8.2.0) tends to read 32-bit word for bit field test. + * Be careful for those with side effect for read. + */ +struct sys_registers +{ + union _sys_safe_access_sig SAFE_ACCESS_SIG; + uint8_t CHIP_ID; + uint8_t SAFE_ACCESS_ID; + uint8_t WDOG_COUNT; + union _sys_glob_rom_cfg GLOB_ROM_CFG; + union _sys_rst_boot_stat RST_BOOT_STAT; + union _sys_rst_wdog_ctrl RST_WDOG_CTRL; + uint8_t GLOB_RESET_KEEP; + union _sys_clk_pll_div CLK_PLL_DIV; + uint8_t resv_9; + union _sys_clk_cfg_ctrl CLK_CFG_CTRL; + union _sys_clk_mod_aux CLK_MOD_AUX; + union _sys_slp_clk_off0 SLP_CLK_OFF0; + union _sys_slp_clk_off1 SLP_CLK_OFF1; + union _sys_slp_wake_ctrl SLP_WAKE_CTRL; + union _sys_slp_power_ctrl SLP_POWER_CTRL; +#if defined(SOC_SERIES_CH569) + uint32_t resv_10[4]; + union _sys_serd_ana_cfg1 SERD_ANA_CFG1; + uint16_t resv_22; + union _sys_serd_ana_cfg2 SERD_ANA_CFG2; +#endif +} __packed; + +CHECK_STRUCT_SIZE(struct sys_registers, 0x28); + +uint32_t sys_hclk_get(void); +int sys_hclk_set(uint32_t freq); +int sys_clk_off_by_irqn(uint8_t irqn, int off); +void sys_slp_clk_off0(uint8_t bits, int off); +void sys_slp_clk_off1(uint8_t bits, int off); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/bsp/wch/risc-v/Libraries/ch56x_drivers/ch56x_timer.c b/bsp/wch/risc-v/Libraries/ch56x_drivers/ch56x_timer.c new file mode 100644 index 0000000000..be388386c6 --- /dev/null +++ b/bsp/wch/risc-v/Libraries/ch56x_drivers/ch56x_timer.c @@ -0,0 +1,262 @@ +/* + * Copyright (c) 2006-2022, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2022-07-15 Emuzit first version + */ +#include +#include +#include +#include "ch56x_sys.h" +#include "ch56x_timer.h" +#include "isr_sp.h" + +struct hwtimer_device +{ + struct rt_hwtimer_device parent; + struct rt_hwtimer_info hwtimer_info; + volatile struct timer_registers *reg_base; + rt_hwtimer_mode_t tmode; + irq_number_t irqn; + char *name; +}; + +#ifdef BSP_USING_TMR0 +static struct hwtimer_device hwtimer_device_0 = +{ + .hwtimer_info = + { + .maxfreq = 80000000, + .minfreq = 80000000, + .maxcnt = 0x3ffffff, + .cntmode = HWTIMER_CNTMODE_UP, + }, + .reg_base = (struct timer_registers *)TMR0_REG_BASE, + .tmode = HWTIMER_MODE_PERIOD, + .irqn = TMR0_IRQn, + .name = "timer0", +}; +#endif + +#ifdef BSP_USING_TMR1 +static struct hwtimer_device hwtimer_device_1 = +{ + .hwtimer_info = + { + .maxfreq = 80000000, + .minfreq = 80000000, + .maxcnt = 0x3ffffff, + .cntmode = HWTIMER_CNTMODE_UP, + }, + .reg_base = (struct timer_registers *)TMR1_REG_BASE, + .tmode = HWTIMER_MODE_PERIOD, + .irqn = TMR1_IRQn, + .name = "timer1", +}; +#endif + +#ifdef BSP_USING_TMR2 +static struct hwtimer_device hwtimer_device_2 = +{ + .hwtimer_info = + { + .maxfreq = 80000000, + .minfreq = 80000000, + .maxcnt = 0x3ffffff, + .cntmode = HWTIMER_CNTMODE_UP, + }, + .reg_base = (struct timer_registers *)TMR2_REG_BASE, + .tmode = HWTIMER_MODE_PERIOD, + .irqn = TMR2_IRQn, + .name = "timer2", +}; +#endif + +static void hwtimer_stop(struct rt_hwtimer_device *timer); + +static void hwtimer_init(struct rt_hwtimer_device *timer, uint32_t state) +{ + struct hwtimer_device *hwtimer_device = (void *)timer; + + RT_ASSERT(hwtimer_device != RT_NULL); + + /* no resource processing, `state` ignored */ + hwtimer_stop(timer); + + if (hwtimer_device->irqn != TMR0_IRQn) + { + hwtimer_device->reg_base->CTRL_DMA.reg = 0; + } +} + +static rt_err_t hwtimer_start(struct rt_hwtimer_device *timer, uint32_t cnt, rt_hwtimer_mode_t mode) +{ + struct hwtimer_device *hwtimer_device = (void *)timer; + volatile struct timer_registers *txreg; + + RT_ASSERT(hwtimer_device != RT_NULL); + + /* hwtimer_device->tmode may be different from timer->mode. + * For multi-cycle ONESHOT, tmode is set to PERIOD at hwtimer_start. + */ + hwtimer_device->tmode = mode; + + sys_clk_off_by_irqn(hwtimer_device->irqn, SYS_SLP_CLK_ON); + txreg = hwtimer_device->reg_base; + txreg->CNT_END = cnt; + txreg->CTRL_MOD.reg = RB_TMR_ALL_CLEAR; + txreg->CTRL_MOD.reg = RB_TMR_COUNT_EN; + txreg->INTER_EN.cyc_end = 1; + rt_hw_interrupt_umask(hwtimer_device->irqn); + + return RT_EOK; +} + +static void hwtimer_stop(struct rt_hwtimer_device *timer) +{ + struct hwtimer_device *hwtimer_device = (void *)timer; + volatile struct timer_registers *txreg; + + RT_ASSERT(hwtimer_device != RT_NULL); + + rt_hw_interrupt_mask(hwtimer_device->irqn); + /* note: RB_TMR_COUNT_EN cleared */ + txreg = hwtimer_device->reg_base; + txreg->CTRL_MOD.reg = RB_TMR_ALL_CLEAR; + txreg->INTER_EN.reg = 0; + sys_clk_off_by_irqn(hwtimer_device->irqn, SYS_SLP_CLK_OFF); +} + +static uint32_t hwtimer_count_get(struct rt_hwtimer_device *timer) +{ + struct hwtimer_device *hwtimer_device = (void *)timer; + + RT_ASSERT(hwtimer_device != RT_NULL); + + return hwtimer_device->reg_base->COUNT; +} + +static rt_err_t hwtimer_control( + struct rt_hwtimer_device *timer, uint32_t cmd, void *args) +{ + struct hwtimer_device *hwtimer_device = (void *)timer; + rt_err_t result = RT_EOK; + + RT_ASSERT(hwtimer_device != RT_NULL); + + switch (cmd) + { + case HWTIMER_CTRL_FREQ_SET: + /* clocking for ch56x timers are fixed to Fsys */ + if (args == RT_NULL || *(uint32_t *)args != timer->info->minfreq) + { + result = -RT_EINVAL; + } + break; + + case HWTIMER_CTRL_STOP: + case HWTIMER_CTRL_INFO_GET: + case HWTIMER_CTRL_MODE_SET: + default: + result = -RT_ENOSYS; + } + + return result; +} + +static const struct rt_hwtimer_ops hwtimer_ops = +{ + .init = hwtimer_init, + .start = hwtimer_start, + .stop = hwtimer_stop, + .count_get = hwtimer_count_get, + .control = hwtimer_control, +}; + +static int rt_hw_hwtimer_init(void) +{ + struct hwtimer_device *devices[3]; + + uint32_t Fsys = sys_hclk_get(); + + int n = 0; + +#ifdef BSP_USING_TMR2 + devices[n++] = &hwtimer_device_2; +#endif +#ifdef BSP_USING_TMR1 + devices[n++] = &hwtimer_device_1; +#endif +#ifdef BSP_USING_TMR0 + devices[n++] = &hwtimer_device_0; +#endif + + while (--n >= 0) + { + struct hwtimer_device *hwtimer_device = devices[n]; + /* counting frequency is fixed to Fsys */ + hwtimer_device->hwtimer_info.maxfreq = Fsys; + hwtimer_device->hwtimer_info.minfreq = Fsys; + hwtimer_device->parent.info = &hwtimer_device->hwtimer_info; + hwtimer_device->parent.ops = &hwtimer_ops; + rt_device_hwtimer_register( + &hwtimer_device->parent, hwtimer_device->name, RT_NULL); + } + return RT_EOK; +} +INIT_DEVICE_EXPORT(rt_hw_hwtimer_init); + +static void _hwtimer_isr_common(struct hwtimer_device *hwtimer_device) +{ + volatile struct timer_registers *txreg = hwtimer_device->reg_base; + + if (txreg->INT_FLAG.cyc_end) + { + if (hwtimer_device->tmode == HWTIMER_MODE_ONESHOT) + { + /* disable timer to emulate oneshot */ + txreg->CTRL_MOD.reg = 0; + } + rt_device_hwtimer_isr(&hwtimer_device->parent); + txreg->INT_FLAG.cyc_end = 1; + } +} + +#ifdef BSP_USING_TMR0 +void tmr0_irq_handler(void) __attribute__((interrupt())); +void tmr0_irq_handler(void) +{ + isr_sp_enter(); + rt_interrupt_enter(); + _hwtimer_isr_common(&hwtimer_device_0); + rt_interrupt_leave(); + isr_sp_leave(); +} +#endif + +#ifdef BSP_USING_TMR1 +void tmr1_irq_handler(void) __attribute__((interrupt())); +void tmr1_irq_handler(void) +{ + isr_sp_enter(); + rt_interrupt_enter(); + _hwtimer_isr_common(&hwtimer_device_1); + rt_interrupt_leave(); + isr_sp_leave(); +} +#endif + +#ifdef BSP_USING_TMR2 +void tmr2_irq_handler(void) __attribute__((interrupt())); +void tmr2_irq_handler(void) +{ + isr_sp_enter(); + rt_interrupt_enter(); + _hwtimer_isr_common(&hwtimer_device_2); + rt_interrupt_leave(); + isr_sp_leave(); +} +#endif diff --git a/bsp/wch/risc-v/Libraries/ch56x_drivers/ch56x_timer.h b/bsp/wch/risc-v/Libraries/ch56x_drivers/ch56x_timer.h new file mode 100644 index 0000000000..63a5ebef13 --- /dev/null +++ b/bsp/wch/risc-v/Libraries/ch56x_drivers/ch56x_timer.h @@ -0,0 +1,138 @@ +/* + * Copyright (c) 2006-2022, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2022-07-15 Emuzit first version + */ +#ifndef __CH56X_TIMER_H__ +#define __CH56X_TIMER_H__ + +#include "soc.h" + +#ifdef __cplusplus +extern "C" { +#endif + +union _timer_ctrl_mod +{ + uint8_t reg; + struct + { + uint8_t mode_in : 1; // B.0 : RW, timer mode setting + uint8_t all_clear : 1; // B.1 : RW, clear FIFO/count/int-flag + uint8_t count_en : 1; // B.2 : RW, enable timer module + uint8_t out_en : 1; // B.3 : RW, timer output enable + uint8_t out_polar : 1; // B.4 : RW, output polarity for PWM mode + uint8_t resv_5 : 1; + uint8_t pwm_repeat : 2; // B.7-6 : RW, PWM repeat count, 1/4/8/16 + }; + struct + { + uint8_t stuff_0 : 6; + uint8_t cap_edge : 2; // B.7-6 : RW, capture edge mode + }; +}; +#define RB_TMR_MODE_IN 0x01 +#define RB_TMR_ALL_CLEAR 0x02 +#define RB_TMR_COUNT_EN 0x04 +#define RB_TMR_OUT_EN 0x08 +#define RB_TMR_OUT_POLAR 0x10 +#define RB_TMR_CAP_COUNT 0x10 +#define RB_TMR_PWM_REPEAT 0xc0 +#define RB_TMR_CAP_EDGE 0xc0 + +#define TMR_MODE_TIMER_PWM 0 +#define TMR_MODE_CAP_COUNT 1 +#define TMR_PWM_REPEAT_1 0 +#define TMR_PWM_REPEAT_4 1 +#define TMR_PWM_REPEAT_8 2 +#define TMR_PWM_REPEAT_16 3 +#define TMR_CAP_EDGE_NONE 0 +#define TMR_CAP_EDGE_BOTH 1 +#define TMR_CAP_EDGE_F2F 2 +#define TMR_CAP_EDGE_R2R 3 + +union _timer_ctrl_dma +{ + uint8_t reg; + struct + { + uint8_t dma_enable : 1; // B.0 : RW, enable DMA + uint8_t resv_1 : 1; + uint8_t dma_loop : 1; // B.2 : RW, enable DMA address looping + uint8_t resv_3 : 5; + }; +}; +#define RB_TMR_DMA_ENABLE 0x01 +#define RB_TMR_DMA_LOOP 0x04 + +union _timer_interrupt +{ + uint8_t reg; + struct + { + uint8_t cyc_end : 1; // B.0 + uint8_t data_act : 1; // B.1 + uint8_t fifo_hf : 1; // B.2 + uint8_t dma_end : 1; // B.3 + uint8_t fifo_ov : 1; // B.4 + uint8_t resv_5 : 3; + }; +}; +#define RB_TMR_IX_MASK 0x1f +#define RB_TMR_IE_CYC_END 0x01 // RW, enable interrupt for timer capture count timeout or PWM cycle end +#define RB_TMR_IE_DATA_ACT 0x02 // RW, enable interrupt for timer capture input action or PWM trigger +#define RB_TMR_IE_FIFO_HF 0x04 // RW, enable interrupt for timer FIFO half (capture fifo >=4 or PWM fifo <=3) +#define RB_TMR_IE_DMA_END 0x08 // RW, enable interrupt for timer1/2 DMA completion +#define RB_TMR_IE_FIFO_OV 0x10 // RW, enable interrupt for timer FIFO overflow + +#define RB_TMR_IF_CYC_END 0x01 // RW1, interrupt flag for timer capture count timeout or PWM cycle end +#define RB_TMR_IF_DATA_ACT 0x02 // RW1, interrupt flag for timer capture input action or PWM trigger +#define RB_TMR_IF_FIFO_HF 0x04 // RW1, interrupt flag for timer FIFO half (capture fifo >=4 or PWM fifo <=3) +#define RB_TMR_IF_DMA_END 0x08 // RW1, interrupt flag for timer1/2 DMA completion +#define RB_TMR_IF_FIFO_OV 0x10 // RW1, interrupt flag for timer FIFO overflow + +/* + * 0x00 R8_TMRx_CTRL_MOD: mode setting register + * 0x01 R8_TMRx_CTRL_DMA: DMA control register + * 0x02 R8_TMRx_INTER_EN: interrupt enable register + * 0x06 R8_TMRx_INT_FLAG: interrupt flag register + * 0x07 R8_TMRx_FIFO_COUNT: RO, FIFO count register + * 0x08 R32_TMRx_COUNT: RO, timer current count register + * 0x0c R32_TMRx_CNT_END: RW, timer count end register + * 0x10 R32_TMRx_FIFO: RO/WO, FIFO data register, LSB 26 bits + * 0x14 R32_TMRx_DMA_NOW: RW, DMA buffer current address, LSB 18 bits + * 0x18 R32_TMRx_DMA_BEG: RW, DMA buffer begin address, LSB 18 bits + * 0x1c R32_TMRx_DMA_END: RW, DMA buffer end address (exclusive), LSB 18 bits + * + * Note: DMA related registers (0x10,0x14,0x18,0x1c) are TMR1/2 only + * + * CAVEAT: gcc (as of 8.2.0) tends to read 32-bit word for bit field test. + * Be careful for those with side effect for read. + */ +struct timer_registers +{ + union _timer_ctrl_mod CTRL_MOD; + union _timer_ctrl_dma CTRL_DMA; + union _timer_interrupt INTER_EN; + uint8_t resv_3[3]; + union _timer_interrupt INT_FLAG; + uint8_t FIFO_COUNT; + uint32_t COUNT; + uint32_t CNT_END; + uint32_t FIFO; + uint32_t DMA_NOW; + uint32_t DMA_BEG; + uint32_t DMA_END; +} __packed; + +CHECK_STRUCT_SIZE(struct timer_registers, 0x20); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/bsp/wch/risc-v/Libraries/ch56x_drivers/ch56x_uart.c b/bsp/wch/risc-v/Libraries/ch56x_drivers/ch56x_uart.c new file mode 100644 index 0000000000..d0230d21fb --- /dev/null +++ b/bsp/wch/risc-v/Libraries/ch56x_drivers/ch56x_uart.c @@ -0,0 +1,343 @@ +/* + * Copyright (c) 2006-2022, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2022-07-15 Emuzit first version + */ +#include +#include +#include +#include +#ifdef RT_USING_SERIAL_V2 + #include +#else + #include +#endif +#include "ch56x_sys.h" +#include "ch56x_uart.h" +#include "isr_sp.h" + +#if !defined(BSP_USING_UART0) && !defined(BSP_USING_UART1) && \ + !defined(BSP_USING_UART2) && !defined(BSP_USING_UART3) + #error "Please define at least one UARTx" +#endif + +struct serial_device +{ + struct rt_serial_device parent; + volatile struct uart_registers *reg_base; + irq_number_t irqn; + char *name; +}; + +#ifdef BSP_USING_UART0 +static struct serial_device serial_device_0 = +{ + .reg_base = (struct uart_registers *)UART0_REG_BASE, + .irqn = UART0_IRQn, + .name = "uart0", +}; +#endif + +#ifdef BSP_USING_UART1 +static struct serial_device serial_device_1 = +{ + .reg_base = (struct uart_registers *)UART1_REG_BASE, + .irqn = UART1_IRQn, + .name = "uart1", +}; +#endif + +#ifdef BSP_USING_UART2 +static struct serial_device serial_device_2 = +{ + .reg_base = (struct uart_registers *)UART2_REG_BASE, + .irqn = UART2_IRQn, + .name = "uart2", +}; +#endif + +#ifdef BSP_USING_UART3 +static struct serial_device serial_device_3 = +{ + .reg_base = (struct uart_registers *)UART3_REG_BASE, + .irqn = UART3_IRQn, + .name = "uart3", +}; +#endif + +static rt_err_t uart_configure(struct rt_serial_device *serial, struct serial_configure *cfg) +{ + struct serial_device *serial_device = (struct serial_device *)serial; + volatile struct uart_registers *uxreg = serial_device->reg_base; + + union _uart_fcr fcr; + union _uart_lcr lcr; + uint32_t x; + + x = 10 * sys_hclk_get() / 8 / cfg->baud_rate; + x = (x + 5) / 10; + uxreg->DL = x; + uxreg->DIV = 1; + + lcr.reg = 0; + switch (cfg->data_bits) + { + case DATA_BITS_5: + lcr.word_sz = LCR_DATA_BITS_5; + break; + case DATA_BITS_6: + lcr.word_sz = LCR_DATA_BITS_6; + break; + case DATA_BITS_7: + lcr.word_sz = LCR_DATA_BITS_7; + break; + case DATA_BITS_8: + default: + lcr.word_sz = LCR_DATA_BITS_8; + break; + } + + switch (cfg->stop_bits) + { + case STOP_BITS_2: + lcr.stop_bit = LCR_STOP_BITS_2; + break; + case STOP_BITS_1: + default: + lcr.stop_bit = LCR_STOP_BITS_1; + break; + } + + switch (cfg->parity) + { + case PARITY_ODD: + lcr.par_mod = LCR_PARITY_ODD; + lcr.par_en = 1; + break; + case PARITY_EVEN: + lcr.par_mod = LCR_PARITY_EVEN; + lcr.par_en = 1; + break; + case PARITY_NONE: + default: + lcr.par_en = 0; + break; + } + uxreg->LCR.reg = lcr.reg; + + fcr.reg = RB_FCR_FIFO_EN | RB_FCR_RX_FIFO_CLR | RB_FCR_TX_FIFO_CLR; + fcr.fifo_trig = UART_1BYTE_TRIG; + uxreg->FCR.reg = fcr.reg; + + /* TXD pin output enable */ + uxreg->IER.txd_en = 1; + + return RT_EOK; +} + +static rt_err_t uart_control(struct rt_serial_device *serial, int cmd, void *args) +{ + struct serial_device *serial_device = (struct serial_device *)serial; + volatile struct uart_registers *uxreg = serial_device->reg_base; + + switch (cmd) + { + case RT_DEVICE_CTRL_CLR_INT: + uxreg->IER.recv_rdy = 0; + uxreg->IER.line_stat = 0; + uxreg->IER.thr_empty = 0; + rt_hw_interrupt_mask(serial_device->irqn); + break; + case RT_DEVICE_CTRL_SET_INT: + uxreg->FCR.fifo_trig = UART_1BYTE_TRIG; + uxreg->MCR.int_oe = 1; + uxreg->IER.recv_rdy = 1; + uxreg->IER.line_stat = 1; + if (serial->parent.open_flag & RT_DEVICE_FLAG_INT_TX) + { + uxreg->IER.thr_empty = 1; + } + rt_hw_interrupt_umask(serial_device->irqn); + break; + default: + break; + } + + return RT_EOK; +} + +static int uart_putc(struct rt_serial_device *serial, char ch) +{ + struct serial_device *serial_device = (struct serial_device *)serial; + volatile struct uart_registers *uxreg = serial_device->reg_base; + + if (serial->parent.open_flag & RT_DEVICE_FLAG_INT_TX) + { + if (uxreg->TFC >= UART_FIFO_SIZE) + return -1; + } + else + { + while (uxreg->TFC >= UART_FIFO_SIZE) + { + if (rt_thread_self() && rt_interrupt_get_nest() == 0) + rt_thread_yield(); + } + } + uxreg->THR = ch; + + return 1; +} + +static int uart_getc(struct rt_serial_device *serial) +{ + struct serial_device *serial_device = (struct serial_device *)serial; + volatile struct uart_registers *uxreg = serial_device->reg_base; + + /* UART_II_RECV_RDY is cleared by reading RBR */ + return (uxreg->RFC > 0) ? uxreg->RBR : -1; +} + +static const struct rt_uart_ops uart_ops = +{ + .configure = uart_configure, + .control = uart_control, + .putc = uart_putc, + .getc = uart_getc, + .dma_transmit = RT_NULL, +}; + +int rt_hw_uart_init(void) +{ + struct serial_device *devices[4]; + + /* Note: HCLK should be at least 8MHz for default 115200 baud to work */ + struct serial_configure config = RT_SERIAL_CONFIG_DEFAULT; + + int n = 0; + +#ifdef BSP_USING_UART3 + devices[n++] = &serial_device_3; +#endif +#ifdef BSP_USING_UART2 + devices[n++] = &serial_device_2; +#endif +#ifdef BSP_USING_UART1 + devices[n++] = &serial_device_1; +#endif +#ifdef BSP_USING_UART0 + devices[n++] = &serial_device_0; +#endif + + /* IMPORTANT: pin mode should be set properly @ board init */ + + while (--n >= 0) + { + uint32_t flag; + struct serial_device *serial = devices[n]; + serial->parent.ops = &uart_ops; + serial->parent.config = config; + + sys_clk_off_by_irqn(serial->irqn, SYS_SLP_CLK_ON); + + flag = RT_DEVICE_FLAG_RDWR | + RT_DEVICE_FLAG_STREAM | // for converting '\n' + RT_DEVICE_FLAG_INT_TX | + RT_DEVICE_FLAG_INT_RX ; + rt_hw_serial_register(&serial->parent, serial->name, flag, RT_NULL); + + /* rt_serial_open => uart_control with RT_DEVICE_CTRL_SET_INT */ + } + + return 0; +} + +static void _uart_isr_common(struct serial_device *serial_device) +{ + struct rt_serial_device *serial = &serial_device->parent; + volatile struct uart_registers *uxreg = serial_device->reg_base; + + switch (uxreg->IIR.int_mask) + { + case UART_II_RECV_TOUT: + /* FIXME: It's a bad idea to read RBR to clear UART_II_RECV_TOUT. + * Race condition may happen that actual rx data is dropped. + */ + if (uxreg->RFC == 0) + { + uxreg->RBR; + //rt_hw_serial_isr(serial, RT_SERIAL_EVENT_RX_TIMEOUT); + break; + } + /* pass through as if UART_II_RECV_RDY */ + case UART_II_RECV_RDY: + rt_hw_serial_isr(serial, RT_SERIAL_EVENT_RX_IND); + break; + case UART_II_THR_EMPTY: + rt_hw_serial_isr(serial, RT_SERIAL_EVENT_TX_DONE); + break; + case UART_II_LINE_STAT: + uxreg->LSR; + break; + case UART_II_MODEM_CHG: + uxreg->MSR; + break; + case UART_II_SLV_ADDR: + uxreg->IIR; + break; + default: + break; + } +} + +#ifdef BSP_USING_UART0 +void uart0_irq_handler(void) __attribute__((interrupt())); +void uart0_irq_handler(void) +{ + isr_sp_enter(); + rt_interrupt_enter(); + _uart_isr_common(&serial_device_0); + rt_interrupt_leave(); + isr_sp_leave(); +} +#endif + +#ifdef BSP_USING_UART1 +void uart1_irq_handler(void) __attribute__((interrupt())); +void uart1_irq_handler(void) +{ + isr_sp_enter(); + rt_interrupt_enter(); + _uart_isr_common(&serial_device_1); + rt_interrupt_leave(); + isr_sp_leave(); +} +#endif + +#ifdef BSP_USING_UART2 +void uart2_irq_handler(void) __attribute__((interrupt())); +void uart2_irq_handler(void) +{ + isr_sp_enter(); + rt_interrupt_enter(); + _uart_isr_common(&serial_device_2); + rt_interrupt_leave(); + isr_sp_leave(); +} +#endif + +#ifdef BSP_USING_UART3 +void uart3_irq_handler(void) __attribute__((interrupt())); +void uart3_irq_handler(void) +{ + isr_sp_enter(); + rt_interrupt_enter(); + _uart_isr_common(&serial_device_3); + rt_interrupt_leave(); + isr_sp_leave(); +} +#endif diff --git a/bsp/wch/risc-v/Libraries/ch56x_drivers/ch56x_uart.h b/bsp/wch/risc-v/Libraries/ch56x_drivers/ch56x_uart.h new file mode 100644 index 0000000000..c34c6b5c21 --- /dev/null +++ b/bsp/wch/risc-v/Libraries/ch56x_drivers/ch56x_uart.h @@ -0,0 +1,254 @@ +/* + * Copyright (c) 2006-2022, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2022-07-15 Emuzit first version + */ +#ifndef __CH56X_UART_H__ +#define __CH56X_UART_H__ + +#include "soc.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef UART_FIFO_SIZE +#define UART_FIFO_SIZE 8 +#endif + +#ifndef UART_RECV_RDY_SZ +#define UART_RECV_RDY_SZ 7 // FIFO trigger level for rx data available +#endif + +union _uart_mcr +{ + uint8_t reg; + struct + { + uint8_t dtr : 1; // B.0 : RW, DTR output (UART0 only) + uint8_t rts : 1; // B.1 : RW, RTS output (UART0 only) + uint8_t out1 : 1; // B.2 : RW, user defined modem control (UART0 only) + uint8_t int_oe : 1; // B.3 : RW, interrupt output enable / OUT2 + uint8_t loop : 1; // B.4 : RW, enable internal loop test (UART0 only) + uint8_t au_flow_en : 1; // B.5 : RW, enable CTS/RTS autoflow control + uint8_t tnow : 1; // B.6 : RW, enable DTR TNOW output (UART0 only) + uint8_t half : 1; // B.7 : RW, enable half-duplex mode (UART0 only) + }; +}; +#define RB_MCR_DTR 0x01 +#define RB_MCR_RTS 0x02 +#define RB_MCR_OUT1 0x04 +#define RB_MCR_OUT2 0x08 +#define RB_MCR_INT_OE 0x08 +#define RB_MCR_LOOP 0x10 +#define RB_MCR_AU_FLOW_EN 0x20 +#define RB_MCR_TNOW 0x40 +#define RB_MCR_HALF 0x80 + +union _uart_ier +{ + uint8_t reg; + struct + { + uint8_t recv_rdy : 1; // B.0 : RW, enable rx data ready intr + uint8_t thr_empty : 1; // B.1 : RW, enable THR empty intr + uint8_t line_stat : 1; // B.2 : RW, enable rx line status intr + uint8_t modem_chg : 1; // B.3 : RW, enable modem status change intr (UART0 only) + uint8_t dtr_en : 1; // B.4 : RW, DTR/TNOW output pin enable (UART0 only) + uint8_t rts_en : 1; // B.5 : RW, RTS output pin enable (UART0 only) + uint8_t txd_en : 1; // B.6 : RW, TXD pin enable + uint8_t reset : 1; // B.7 : WZ, software reset control, active high, auto clear + }; +}; +#define RB_IER_RECV_RDY 0x01 +#define RB_IER_THR_EMPTY 0x02 +#define RB_IER_LINE_STAT 0x04 +#define RB_IER_MODEM_CHG 0x08 +#define RB_IER_DTR_EN 0x10 +#define RB_IER_RTS_EN 0x20 +#define RB_IER_TXD_EN 0x40 +#define RB_IER_RESET 0x80 + +union _uart_fcr +{ + uint8_t reg; + struct + { + uint8_t fifo_en : 1; // B.0 : RW, FIFO enable + uint8_t rx_fifo_clr : 1; // B.1 : WZ, write 1 to clear rx FIFO, auto clear + uint8_t tx_fifo_clr : 1; // B.2 : WZ, write 1 to clear tx FIFO, auto clear + uint8_t resv_3 : 3; + uint8_t fifo_trig : 2; // B.7-6 : RW, rx FIFO trigger level, 1/2/4/7 bytes + }; +}; +#define RB_FCR_FIFO_EN 0x01 +#define RB_FCR_RX_FIFO_CLR 0x02 +#define RB_FCR_TX_FIFO_CLR 0x04 +#define RB_FCR_FIFO_TRIG 0xc0 + +#define UART_1BYTE_TRIG 0 +#define UART_2BYTE_TRIG 1 +#define UART_4BYTE_TRIG 2 +#define UART_7BYTE_TRIG 3 + +union _uart_lcr +{ + uint8_t reg; + struct + { + uint8_t word_sz : 2; // B.1-0 : RW, word bit length, 5/6/7/8 bits + uint8_t stop_bit : 1; // B.2 : RW, stop bit length, 1/2 bits + uint8_t par_en : 1; // B.3 : RW, parity enable + uint8_t par_mod : 2; // B.5-4 : RW, parity mode, odd/even/mark/space + uint8_t break_en : 1; // B.6 : RW, force BREAK line condition + uint8_t dlab : 1; // B.7 : RW, user defined general purpose bit + }; +}; +#define RB_LCR_WORD_SZ 0x03 +#define RB_LCR_STOP_BIT 0x04 +#define RB_LCR_PAR_EN 0x08 +#define RB_LCR_PAR_MOD 0x30 +#define RB_LCR_BREAK_EN 0x40 +#define RB_LCR_DLAB 0x80 +#define RB_LCR_GP_BIT 0x80 + +#define LCR_DATA_BITS_5 0 +#define LCR_DATA_BITS_6 1 +#define LCR_DATA_BITS_7 2 +#define LCR_DATA_BITS_8 3 + +#define LCR_STOP_BITS_1 0 +#define LCR_STOP_BITS_2 1 + +#define LCR_PARITY_ODD 0 +#define LCR_PARITY_EVEN 1 +#define LCR_PARITY_MARK 2 +#define LCR_PARITY_SPACE 3 + +union _uart_iir +{ + uint8_t reg; + struct + { + uint8_t int_mask : 4; // B.3-0 : RO, interrupt mask (intr if B.0 is 0) + uint8_t resv_4 : 2; + uint8_t fifo_id : 2; // B.7-6 : RO, FIFO enabled flag + }; +}; +#define RB_IIR_NO_INT 0x01 +#define RB_IIR_INT_MASK 0x0f +#define RB_IIR_FIFO_ID 0xc0 + +/* RB_IIR_INT_MASK (IIR bits 3:0) definition +*/ +#define UART_II_SLV_ADDR 0x0e // UART0 slave address match interrupt +#define UART_II_LINE_STAT 0x06 // rx line status interrupt +#define UART_II_RECV_RDY 0x04 // rx data available interrupt +#define UART_II_RECV_TOUT 0x0c // rx fifo timeout interrupt +#define UART_II_THR_EMPTY 0x02 // THR empty interrupt +#define UART_II_MODEM_CHG 0x00 // UART0 modem status change interrupt +#define UART_II_NO_INTER 0x01 // no interrupt pending + +union _uart_lsr +{ + uint8_t reg; + struct + { + uint8_t data_rdy : 1; // B.0 : RO, rx FIFO data ready + uint8_t over_err : 1; // B.1 : RZ, rx FIFO data overrun + uint8_t par_err : 1; // B.2 : RZ, rx parity error + uint8_t frame_err : 1; // B.3 : RZ, rx frame error + uint8_t break_err : 1; // B.4 : RZ, rx BREAK detected + uint8_t tx_fifo_emp : 1; // B.5 : RO, tx FIFO empty + uint8_t tx_all_emp : 1; // B.6 : RO, THR/TSR all empty + uint8_t err_rx_fifo : 1; // B.7 : RO, PAR/FRAME/BREAK ERR in rx FIFO + }; +}; +#define RB_LSR_DATA_RDY 0x01 +#define RB_LSR_OVER_ERR 0x02 +#define RB_LSR_PAR_ERR 0x04 +#define RB_LSR_FRAME_ERR 0x08 +#define RB_LSR_BREAK_ERR 0x10 +#define RB_LSR_TX_FIFO_EMP 0x20 +#define RB_LSR_TX_ALL_EMP 0x40 +#define RB_LSR_ERR_RX_FIFO 0x80 + +union _uart_msr +{ + uint8_t reg; + struct + { + uint8_t cts_chg : 1; // B.0 : RZ, CTS input changed + uint8_t dsr_chg : 1; // B.1 : RZ, DSR input changed + uint8_t ri_chg : 1; // B.2 : RZ, RI input changed + uint8_t dcd_chg : 1; // B.3 : RZ, DCD input changed + uint8_t cts : 1; // B.4 : RO, CTS action status + uint8_t dsr : 1; // B.5 : RO, DSR action status + uint8_t ri : 1; // B.6 : RO, RI action status + uint8_t dcd : 1; // B.7 : RO, DCD action status + }; +}; +#define RB_MSR_CTS_CHG 0x01 +#define RB_MSR_DSR_CHG 0x02 +#define RB_MSR_RI_CHG 0x04 +#define RB_MSR_DCD_CHG 0x08 +#define RB_MSR_CTS 0x10 +#define RB_MSR_DSR 0x20 +#define RB_MSR_RI 0x40 +#define RB_MSR_DCD 0x80 + +/* + * 0x00 R8_UARTx_MCR: Modem Control Register + * 0x01 R8_UARTx_IER: Interrupt Enable Register + * 0x02 R8_UARTx_FCR: FIFO Control Register + * 0x03 R8_UARTx_LCR: Line Control Register + * 0x04 R8_UARTx_IIR: Interrupt Identification Register + * 0x05 R8_UARTx_LSR: Line Status Register + * 0x06 R8_UARTx_MSR: Modem Status Register (UART0 only) + * 0x08 R8_UARTx_RBR: Rx Buffer Register + * 0x08 R8_UARTx_THR: Tx Hold Register + * 0x0a R8_UARTx_RFC: Rx FIFO count register + * 0x0b R8_UARTx_TFC: Tx FIFO count register + * 0x0c R16_UARTx_DL: Divisor Latch + * 0x0e R8_UARTx_DIV: frequency pre divider + * 0x0f R8_UARTx_ADR: Address Register (UART0 only) + * + * CAVEAT: gcc (as of 8.2.0) tends to read 32-bit word for bit field test. + * Be careful for those with side effect for read (e.g. RBR, IIR). + */ +struct uart_registers +{ + union _uart_mcr MCR; + union _uart_ier IER; + union _uart_fcr FCR; + union _uart_lcr LCR; + union _uart_iir IIR; + union _uart_lsr LSR; + union _uart_lsr MSR; + uint8_t resv_7; + union + { + uint8_t RBR; + uint8_t THR; + }; + uint8_t resv_9; + uint8_t RFC; + uint8_t TFC; + uint16_t DL; + uint8_t DIV; + uint8_t ADR; +} __packed; + +CHECK_STRUCT_SIZE(struct uart_registers, 0x10); + +int rt_hw_uart_init(void); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/bsp/wch/risc-v/Libraries/ch56x_drivers/ch56x_wdt.c b/bsp/wch/risc-v/Libraries/ch56x_drivers/ch56x_wdt.c new file mode 100644 index 0000000000..200f71b061 --- /dev/null +++ b/bsp/wch/risc-v/Libraries/ch56x_drivers/ch56x_wdt.c @@ -0,0 +1,241 @@ +/* + * Copyright (c) 2006-2022, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2022-07-20 Emuzit first version + */ +#include +#include +#include +#include "ch56x_sys.h" + +#define WDOG_HICOUNT_MAX 0xfff // enough to hold (4095 * 120M/524288) >> 8 + +struct watchdog_device +{ + rt_watchdog_t parent; + volatile uint32_t hicount; + uint32_t timeout; + uint32_t reload; + uint8_t is_start; +}; + +static struct watchdog_device watchdog_device; + +static void wdt_reload_counter(rt_watchdog_t *wdt, int cmd) +{ + struct watchdog_device *wdt_dev = (void *)wdt; + + volatile struct sys_registers *sys = (void *)SYS_REG_BASE; + + rt_base_t level; + + level = rt_hw_interrupt_disable(); + /* reload WDOG_COUNT also clears RB_WDOG_INT_FLAG*/ + sys->WDOG_COUNT = (uint8_t)wdt_dev->reload; + wdt_dev->hicount = wdt_dev->reload >> 8; + if (cmd != RT_DEVICE_CTRL_WDT_KEEPALIVE && wdt_dev->is_start) + { + sys_safe_access_enter(sys); + if ((wdt_dev->reload >> 8) == WDOG_HICOUNT_MAX) + { + /* WDOG_COUNT can work on its own, no wdog_irq needed */ + sys->RST_WDOG_CTRL.reg = wdog_ctrl_wdat(RB_WDOG_RST_EN); + rt_hw_interrupt_mask(WDOG_IRQn); + } + else + { + /* Extend wdt with wdt_dev->hicount through wdog_irq. + * CAVEAT: wdt not effective if global interrupt disabled !! + */ + sys->RST_WDOG_CTRL.reg = wdog_ctrl_wdat(RB_WDOG_INT_EN); + rt_hw_interrupt_umask(WDOG_IRQn); + } + sys_safe_access_leave(sys); + } + rt_hw_interrupt_enable(level); +} + +static uint32_t wdt_get_timeleft(rt_watchdog_t *wdt) +{ + struct watchdog_device *wdt_dev = (void *)wdt; + + volatile struct sys_registers *sys = (void *)SYS_REG_BASE; + + uint32_t countleft; + uint64_t count64; + + if ((wdt_dev->reload >> 8) == WDOG_HICOUNT_MAX) + { + /* WDOG_COUNT can work on its own, without hicount */ + countleft = 0xff - sys->WDOG_COUNT; + } + else + { + uint32_t c1 = sys->WDOG_COUNT; + uint32_t hc = wdt_dev->hicount; + uint32_t c2 = sys->WDOG_COUNT; + /* check if WDOG_COUNT overflows between c1/c2 reads */ + if (c2 < c1) + { + rt_base_t level = rt_hw_interrupt_disable(); + hc = wdt_dev->hicount; + if (sys->RST_WDOG_CTRL.wdog_int_flag && sys->RST_WDOG_CTRL.wdog_int_en) + { + hc++; + } + rt_hw_interrupt_enable(level); + } + countleft = ((WDOG_HICOUNT_MAX << 8) + 0xff) - ((hc << 8) + c2); + } + + /* convert wdt count to seconds : count / (Fsys/524288) */ + count64 = countleft; + return (uint32_t)((count64 << 19) / sys_hclk_get()); +} + +static uint32_t _convert_timeout_to_reload(uint32_t seconds) +{ + uint32_t N, R, Fsys, reload = -1; + + /* timeout is limited to 4095, not to overflow 32-bit (T * 2^19) */ + if (seconds < 4096) + { + /* watchdog timer is clocked at Fsys/524288, arround 3~228Hz */ + Fsys = sys_hclk_get(); + /* T * (Fsys/2^19) => (T * N) + T * (R/2^19) */ + N = Fsys >> 19; + R = Fsys & 0x7ffff; + reload = (WDOG_HICOUNT_MAX << 8) + 0xff; + reload -= seconds * N + ((seconds * R) >> 19) + 1; + } + + return reload; +} + +static void _stop_wdog_operation() +{ + volatile struct sys_registers *sys = (void *)SYS_REG_BASE; + + rt_base_t level = rt_hw_interrupt_disable(); + sys_safe_access_enter(sys); + sys->RST_WDOG_CTRL.reg = wdog_ctrl_wdat(RB_WDOG_INT_FLAG); + sys_safe_access_leave(sys); + rt_hw_interrupt_enable(level); + + rt_hw_interrupt_mask(WDOG_IRQn); +} + +static rt_err_t wdt_init(rt_watchdog_t *wdt) +{ + struct watchdog_device *wdt_dev = (void *)wdt; + + _stop_wdog_operation(); + wdt_dev->is_start = 0; + wdt_dev->timeout = -1; + wdt_dev->reload = -1; + + return RT_EOK; +} + +static rt_err_t wdt_control(rt_watchdog_t *wdt, int cmd, void *arg) +{ + struct watchdog_device *wdt_dev = (void *)wdt; + + uint32_t reload, timeout; + + switch (cmd) + { + case RT_DEVICE_CTRL_WDT_KEEPALIVE: + wdt_reload_counter(wdt, cmd); + break; + case RT_DEVICE_CTRL_WDT_GET_TIMELEFT: + *((uint32_t *)arg) = wdt_get_timeleft(wdt); + break; + case RT_DEVICE_CTRL_WDT_GET_TIMEOUT: + *((uint32_t *)arg) = wdt_dev->timeout; + break; + case RT_DEVICE_CTRL_WDT_SET_TIMEOUT: + /* CAVEAT: Setting timeout larger than an 8-bit WDOG_COUNT can + * hold turns the wdog into interrupt mode, which makes wdog + * usless if cause of death is lost global interrupt enable. + */ + timeout = *((uint32_t *)arg); + reload = _convert_timeout_to_reload(timeout); + if ((reload >> 8) > WDOG_HICOUNT_MAX) + { + return -RT_EINVAL; + } + wdt_dev->timeout = timeout; + wdt_dev->reload = reload; + /* FIXME: code example implies wdt started by SET_TIMEOUT ? */ + case RT_DEVICE_CTRL_WDT_START: + if ((wdt_dev->reload >> 8) > WDOG_HICOUNT_MAX) + { + return -RT_EINVAL; + } + wdt_dev->is_start = 1; + wdt_reload_counter(wdt, cmd); + break; + case RT_DEVICE_CTRL_WDT_STOP: + _stop_wdog_operation(); + wdt_dev->is_start = 0; + break; + default: + return -RT_ERROR; + } + + return RT_EOK; +} + +static struct rt_watchdog_ops watchdog_ops = +{ + .init = wdt_init, + .control = wdt_control, +}; + +int rt_hw_wdt_init(void) +{ + rt_uint32_t flag; + + watchdog_device.parent.ops = &watchdog_ops; + + flag = RT_DEVICE_FLAG_DEACTIVATE; + return rt_hw_watchdog_register(&watchdog_device.parent, "wdt", flag, RT_NULL); +} +INIT_BOARD_EXPORT(rt_hw_wdt_init); + +void wdog_irq_handler(void) __attribute__((interrupt())); +void wdog_irq_handler(void) +{ + volatile struct pfic_registers *pfic; + volatile struct sys_registers *sys; + + rt_interrupt_enter(); + + sys = (struct sys_registers *)SYS_REG_BASE; + /* FIXME: RB_WDOG_INT_FLAG seems completely not functioning at all !! + * It's not set at WDOG_COUNT overflow, writing 1 to it does not clear + * wdt interrupt. Bit 16 of pfic->IPR[0] is not effective thereof. + */ + if (watchdog_device.hicount < WDOG_HICOUNT_MAX) + { + watchdog_device.hicount++; + /* clear interrupt flag */ + //sys->RST_WDOG_CTRL.reg |= RB_WDOG_INT_FLAG; + sys->WDOG_COUNT = sys->WDOG_COUNT; + } + else + { + /* reset system if watchdog timeout */ + uint8_t u8v = RB_SOFTWARE_RESET | RB_WDOG_INT_FLAG; + sys_safe_access_enter(sys); + sys->RST_WDOG_CTRL.reg = wdog_ctrl_wdat(u8v); + sys_safe_access_leave(sys); + } + + rt_interrupt_leave(); +} diff --git a/bsp/wch/risc-v/Libraries/ch56x_drivers/isr_sp.h b/bsp/wch/risc-v/Libraries/ch56x_drivers/isr_sp.h new file mode 100644 index 0000000000..54c33ed942 --- /dev/null +++ b/bsp/wch/risc-v/Libraries/ch56x_drivers/isr_sp.h @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2006-2022, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2022-07-27 Emuzit first version + */ +#ifndef __ISR_SP_H__ +#define __ISR_SP_H__ + +/* usrstack is no more in use right after rt_system_scheduler_start(). + * It is also the time global interrupt is enabled. +*/ +#define isr_sp_enter() \ + asm("la t0, rt_interrupt_nest"); \ + asm("bnez t0, 1f"); \ + asm("la t0, _eusrstack"); \ + asm("sw sp, -4(t0)"); \ + asm("addi sp, t0, -4"); \ + asm("1:") + +#define isr_sp_leave() \ + asm("la t0, rt_interrupt_nest"); \ + asm("bnez t0, 1f"); \ + asm("la t0, _eusrstack"); \ + asm("lw sp, -4(t0)"); \ + asm("1:") + +#endif diff --git a/bsp/wch/risc-v/Libraries/ch56x_drivers/soc.h b/bsp/wch/risc-v/Libraries/ch56x_drivers/soc.h new file mode 100644 index 0000000000..579996bcdd --- /dev/null +++ b/bsp/wch/risc-v/Libraries/ch56x_drivers/soc.h @@ -0,0 +1,152 @@ +/* + * Copyright (c) 2006-2022, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2022-07-15 Emuzit first version + */ +#ifndef __SOC_H__ +#define __SOC_H__ + +#include +#include +#include + +#if !defined(SOC_CH567) && \ + !defined(SOC_CH568) && \ + !defined(SOC_SERIES_CH569) +#define SOC_SERIES_CH569 +#endif + +#define CHECK_STRUCT_SIZE(s, size) \ + static_assert(sizeof(s) == size, #s " has wrong size") + +#define BITS_SET(x, bits) do x |= bits; while(0) +#define BITS_CLR(x, bits) do x &= ~bits; while(0) + +#define FLASH_BASE_ADDRESS 0x00000000 +#define RAMS_BASE_ADDRESS 0x20000000 +#define RAMX_BASE_ADDRESS 0x20020000 +#define BUS8_BASE_ADDRESS 0x80000000 + +#ifdef SOC_SERIES_CH569 +#define RAMS_SIZE 16 +#else +#define RAMS_SIZE 32 +#endif +#define RAMS_END (RAMS_BASE_ADDRESS + RAMS_SIZE * 1024) + +#define SYS_REG_BASE 0x40001000 +#define GPIO_REG_BASE 0x40001000 +#define GPIO_REG_BASE_PA 0x40001040 +#define GPIO_REG_BASE_PB 0x40001060 + +#define GPIO_PORTS 2 // 2 ports : PA & PB +#define GPIO_PA_PIN_START 0 // PA : pin number 0~31 +#define GPIO_PB_PIN_START 32 // PB : pin number 32~63 + +#ifdef SOC_SERIES_CH569 +#define GPIO_PA_PIN_MARK 0x00ffffff // PA : bits 0~23 +#define GPIO_PB_PIN_MARK 0x01ffffff // PB : bits 0~24 +#else +#define GPIO_PA_PIN_MARK 0x0000ffff // PA : bits 0~15 +#define GPIO_PB_PIN_MARK 0x00003fff // PB : bits 0~13 +#endif + +#define TMR0_REG_BASE 0x40002000 +#define TMR1_REG_BASE 0x40002400 +#define TMR2_REG_BASE 0x40002800 + +#define UART0_REG_BASE 0x40003000 +#define UART1_REG_BASE 0x40003400 +#define UART2_REG_BASE 0x40003800 +#define UART3_REG_BASE 0x40003c00 + +#define SPI0_REG_BASE 0x40004000 +#define SPI1_REG_BASE 0x40004400 + +#define PWMX_REG_BASE 0x40005000 + +#define PFIC_REG_BASE 0xe000e000 +#define SysTick_REG_BASE 0xe000f000 + +#ifdef SOC_SERIES_CH569 +#define HSPI_REG_BASE 0x40006000 // CH569W +#define ECDC_REG_BASE 0x40007000 +#define USBSS_REG_BASE 0x40008000 +#define USBHS_REG_BASE 0x40009000 +#define EMMC_REG_BASE 0x4000a000 +#define SERDES_REG_BASE 0x4000b000 +#define ETH_REG_BASE 0x4000c000 // CH565W/CH569W +#define DVP_REG_BASE 0x4000e000 // CH565W/CH565M +#else +#define LED_REG_BASE 0x40006000 +#define USB0_REG_BASE 0x40008000 // CH567 +#define USB1_REG_BASE 0x40009000 // CH567 +#define USB_REG_BASE 0x40009000 // CH568 +#define SDC_REG_BASE 0x4000a000 +#define SATA_REG_BASE 0x4000b000 // CH568 +#define ECDC_REG_BASE 0x4000c400 +#endif + +#if defined(SOC_SERIES_CH569) +typedef enum +{ + PWMX_OFFn = 0, + NMI_IRQn = 2, + EXC_IRQn = 3, + SysTick_IRQn = 12, + SWI_IRQn = 14, + WDOG_IRQn = 16, + TMR0_IRQn = 17, + GPIO_IRQn = 18, + SPI0_IRQn = 19, + USBSS_IRQn = 20, + LINK_IRQn = 21, + TMR1_IRQn = 22, + TMR2_IRQn = 23, + UART0_IRQn = 24, + USBHS_IRQn = 25, + EMMC_IRQn = 26, + DVP_IRQn = 27, + HSPI_IRQn = 28, + SPI1_IRQn = 29, + UART1_IRQn = 30, + UART2_IRQn = 31, + UART3_IRQn = 32, + SerDes_IRQn = 33, + ETH_IRQn = 34, + PMT_IRQn = 35, + ECDC_IRQn = 36, + END_OF_IRQn +} irq_number_t; + +#else + +typedef enum +{ + PWMX_OFFn = 0, + SOFT_IRQn = 0, + TMR0_IRQn = 1, + GPIO_IRQn = 2, + SPI0_IRQn = 3, + USB0_IRQn = 4, // CH567 + SATA_IRQn = 4, // CH568 + TMR1_IRQn = 5, + TMR2_IRQn = 6, + UART0_IRQn = 7, + USB1_IRQn = 8, + SDC_IRQn = 9, + ECDC_IRQn = 10, + LED_IRQn = 11, + SPI1_IRQn = 12, + UART1_IRQn = 13, + UART2_IRQn = 14, + UART3_IRQn = 15, + END_OF_IRQn +} irq_number_t; +#endif + +#endif diff --git a/bsp/wch/risc-v/Libraries/ch56x_drivers/swi_gcc.S b/bsp/wch/risc-v/Libraries/ch56x_drivers/swi_gcc.S new file mode 100644 index 0000000000..3e064e7b59 --- /dev/null +++ b/bsp/wch/risc-v/Libraries/ch56x_drivers/swi_gcc.S @@ -0,0 +1,190 @@ +/* + * Copyright (c) 2006-2018, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2021-09-09 WCH the first version + * 2022-07-15 Emuzit adapt to ch569w-evt + */ + +#include "cpuport.h" + + .global swi_handler + .align 2 +swi_handler: + /* save all from thread context */ +#ifdef ARCH_RISCV_FPU + addi sp, sp, -32 * FREGBYTES + FSTORE f0, 0 * FREGBYTES(sp) + FSTORE f1, 1 * FREGBYTES(sp) + FSTORE f2, 2 * FREGBYTES(sp) + FSTORE f3, 3 * FREGBYTES(sp) + FSTORE f4, 4 * FREGBYTES(sp) + FSTORE f5, 5 * FREGBYTES(sp) + FSTORE f6, 6 * FREGBYTES(sp) + FSTORE f7, 7 * FREGBYTES(sp) + FSTORE f8, 8 * FREGBYTES(sp) + FSTORE f9, 9 * FREGBYTES(sp) + FSTORE f10, 10 * FREGBYTES(sp) + FSTORE f11, 11 * FREGBYTES(sp) + FSTORE f12, 12 * FREGBYTES(sp) + FSTORE f13, 13 * FREGBYTES(sp) + FSTORE f14, 14 * FREGBYTES(sp) + FSTORE f15, 15 * FREGBYTES(sp) + FSTORE f16, 16 * FREGBYTES(sp) + FSTORE f17, 17 * FREGBYTES(sp) + FSTORE f18, 18 * FREGBYTES(sp) + FSTORE f19, 19 * FREGBYTES(sp) + FSTORE f20, 20 * FREGBYTES(sp) + FSTORE f21, 21 * FREGBYTES(sp) + FSTORE f22, 22 * FREGBYTES(sp) + FSTORE f23, 23 * FREGBYTES(sp) + FSTORE f24, 24 * FREGBYTES(sp) + FSTORE f25, 25 * FREGBYTES(sp) + FSTORE f26, 26 * FREGBYTES(sp) + FSTORE f27, 27 * FREGBYTES(sp) + FSTORE f28, 28 * FREGBYTES(sp) + FSTORE f29, 29 * FREGBYTES(sp) + FSTORE f30, 30 * FREGBYTES(sp) + FSTORE f31, 31 * FREGBYTES(sp) +#endif + + addi sp, sp, -32 * REGBYTES + + STORE x1, 1 * REGBYTES(sp) + + /* saved MPIE */ + li x1, 0x80 + STORE x1, 2 * REGBYTES(sp) + + STORE x4, 4 * REGBYTES(sp) + STORE x5, 5 * REGBYTES(sp) + STORE x6, 6 * REGBYTES(sp) + STORE x7, 7 * REGBYTES(sp) + STORE x8, 8 * REGBYTES(sp) + STORE x9, 9 * REGBYTES(sp) + STORE x10, 10 * REGBYTES(sp) + STORE x11, 11 * REGBYTES(sp) + STORE x12, 12 * REGBYTES(sp) + STORE x13, 13 * REGBYTES(sp) + STORE x14, 14 * REGBYTES(sp) + STORE x15, 15 * REGBYTES(sp) + STORE x16, 16 * REGBYTES(sp) + STORE x17, 17 * REGBYTES(sp) + STORE x18, 18 * REGBYTES(sp) + STORE x19, 19 * REGBYTES(sp) + STORE x20, 20 * REGBYTES(sp) + STORE x21, 21 * REGBYTES(sp) + STORE x22, 22 * REGBYTES(sp) + STORE x23, 23 * REGBYTES(sp) + STORE x24, 24 * REGBYTES(sp) + STORE x25, 25 * REGBYTES(sp) + STORE x26, 26 * REGBYTES(sp) + STORE x27, 27 * REGBYTES(sp) + STORE x28, 28 * REGBYTES(sp) + STORE x29, 29 * REGBYTES(sp) + STORE x30, 30 * REGBYTES(sp) + STORE x31, 31 * REGBYTES(sp) + + /* rt_thread_switch_interrupt_flag not 0 => clear & switch thread */ + la t0, rt_thread_switch_interrupt_flag + lw t1, 0(t0) + beqz t1, .L_restore + sw zero, 0(t0) + + /* prepare to switch to rt_interrupt_to_thread */ + csrr t1, mepc + STORE t1, 0 * REGBYTES(sp) + + la t0, rt_interrupt_from_thread + LOAD t1, 0(t0) + STORE sp, 0(t1) + + la t0, rt_interrupt_to_thread + LOAD t1, 0(t0) + LOAD sp, 0(t1) + + LOAD t1, 0 * REGBYTES(sp) + csrw mepc, t1 + +.L_restore: + + /* clear software interrupt */ + jal pfic_swi_pendreset + + LOAD x1, 1 * REGBYTES(sp) + + li t0, 0x1800 + csrs mstatus, t0 + LOAD t0, 2 * REGBYTES(sp) + csrs mstatus, t0 + + LOAD x4, 4 * REGBYTES(sp) + LOAD x5, 5 * REGBYTES(sp) + LOAD x6, 6 * REGBYTES(sp) + LOAD x7, 7 * REGBYTES(sp) + LOAD x8, 8 * REGBYTES(sp) + LOAD x9, 9 * REGBYTES(sp) + LOAD x10, 10 * REGBYTES(sp) + LOAD x11, 11 * REGBYTES(sp) + LOAD x12, 12 * REGBYTES(sp) + LOAD x13, 13 * REGBYTES(sp) + LOAD x14, 14 * REGBYTES(sp) + LOAD x15, 15 * REGBYTES(sp) + LOAD x16, 16 * REGBYTES(sp) + LOAD x17, 17 * REGBYTES(sp) + LOAD x18, 18 * REGBYTES(sp) + LOAD x19, 19 * REGBYTES(sp) + LOAD x20, 20 * REGBYTES(sp) + LOAD x21, 21 * REGBYTES(sp) + LOAD x22, 22 * REGBYTES(sp) + LOAD x23, 23 * REGBYTES(sp) + LOAD x24, 24 * REGBYTES(sp) + LOAD x25, 25 * REGBYTES(sp) + LOAD x26, 26 * REGBYTES(sp) + LOAD x27, 27 * REGBYTES(sp) + LOAD x28, 28 * REGBYTES(sp) + LOAD x29, 29 * REGBYTES(sp) + LOAD x30, 30 * REGBYTES(sp) + LOAD x31, 31 * REGBYTES(sp) + addi sp, sp, 32 * REGBYTES + + /* load float reg */ +#ifdef ARCH_RISCV_FPU + FLOAD f0, 0 * FREGBYTES(sp) + FLOAD f1, 1 * FREGBYTES(sp) + FLOAD f2, 2 * FREGBYTES(sp) + FLOAD f3, 3 * FREGBYTES(sp) + FLOAD f4, 4 * FREGBYTES(sp) + FLOAD f5, 5 * FREGBYTES(sp) + FLOAD f6, 6 * FREGBYTES(sp) + FLOAD f7, 7 * FREGBYTES(sp) + FLOAD f8, 8 * FREGBYTES(sp) + FLOAD f9, 9 * FREGBYTES(sp) + FLOAD f10, 10 * FREGBYTES(sp) + FLOAD f11, 11 * FREGBYTES(sp) + FLOAD f12, 12 * FREGBYTES(sp) + FLOAD f13, 13 * FREGBYTES(sp) + FLOAD f14, 14 * FREGBYTES(sp) + FLOAD f15, 15 * FREGBYTES(sp) + FLOAD f16, 16 * FREGBYTES(sp) + FLOAD f17, 17 * FREGBYTES(sp) + FLOAD f18, 18 * FREGBYTES(sp) + FLOAD f19, 19 * FREGBYTES(sp) + FLOAD f20, 20 * FREGBYTES(sp) + FLOAD f21, 21 * FREGBYTES(sp) + FLOAD f22, 22 * FREGBYTES(sp) + FLOAD f23, 23 * FREGBYTES(sp) + FLOAD f24, 24 * FREGBYTES(sp) + FLOAD f25, 25 * FREGBYTES(sp) + FLOAD f26, 26 * FREGBYTES(sp) + FLOAD f27, 27 * FREGBYTES(sp) + FLOAD f28, 28 * FREGBYTES(sp) + FLOAD f29, 29 * FREGBYTES(sp) + FLOAD f30, 30 * FREGBYTES(sp) + FLOAD f31, 31 * FREGBYTES(sp) + addi sp, sp, 32 * FREGBYTES +#endif + mret diff --git a/bsp/wch/risc-v/ch569w-evt/.config b/bsp/wch/risc-v/ch569w-evt/.config new file mode 100644 index 0000000000..cb3bcbf285 --- /dev/null +++ b/bsp/wch/risc-v/ch569w-evt/.config @@ -0,0 +1,664 @@ +# +# Automatically generated file; DO NOT EDIT. +# RT-Thread Configuration +# + +# +# RT-Thread Kernel +# +CONFIG_RT_NAME_MAX=8 +# CONFIG_RT_USING_ARCH_DATA_TYPE is not set +# CONFIG_RT_USING_SMP is not set +CONFIG_RT_ALIGN_SIZE=4 +# CONFIG_RT_THREAD_PRIORITY_8 is not set +CONFIG_RT_THREAD_PRIORITY_32=y +# CONFIG_RT_THREAD_PRIORITY_256 is not set +CONFIG_RT_THREAD_PRIORITY_MAX=32 +CONFIG_RT_TICK_PER_SECOND=200 +# CONFIG_RT_USING_OVERFLOW_CHECK is not set +CONFIG_RT_USING_HOOK=y +CONFIG_RT_HOOK_USING_FUNC_PTR=y +CONFIG_RT_USING_IDLE_HOOK=y +CONFIG_RT_IDLE_HOOK_LIST_SIZE=4 +CONFIG_IDLE_THREAD_STACK_SIZE=384 +# CONFIG_RT_USING_TIMER_SOFT is not set + +# +# kservice optimization +# +CONFIG_RT_KSERVICE_USING_STDLIB=y +# CONFIG_RT_KSERVICE_USING_STDLIB_MEMORY is not set +# CONFIG_RT_KSERVICE_USING_TINY_SIZE is not set +# CONFIG_RT_USING_TINY_FFS is not set +# CONFIG_RT_KPRINTF_USING_LONGLONG is not set +# CONFIG_RT_DEBUG is not set + +# +# Inter-Thread communication +# +CONFIG_RT_USING_SEMAPHORE=y +CONFIG_RT_USING_MUTEX=y +# CONFIG_RT_USING_EVENT is not set +CONFIG_RT_USING_MAILBOX=y +# CONFIG_RT_USING_MESSAGEQUEUE is not set +# CONFIG_RT_USING_SIGNALS is not set + +# +# Memory Management +# +CONFIG_RT_USING_MEMPOOL=y +CONFIG_RT_USING_SMALL_MEM=y +# CONFIG_RT_USING_SLAB is not set +# CONFIG_RT_USING_MEMHEAP is not set +CONFIG_RT_USING_SMALL_MEM_AS_HEAP=y +# CONFIG_RT_USING_MEMHEAP_AS_HEAP is not set +# CONFIG_RT_USING_SLAB_AS_HEAP is not set +# CONFIG_RT_USING_USERHEAP is not set +# CONFIG_RT_USING_NOHEAP is not set +# CONFIG_RT_USING_MEMTRACE is not set +# CONFIG_RT_USING_HEAP_ISR is not set +CONFIG_RT_USING_HEAP=y + +# +# Kernel Device Object +# +CONFIG_RT_USING_DEVICE=y +# CONFIG_RT_USING_DEVICE_OPS is not set +CONFIG_RT_USING_INTERRUPT_INFO=y +CONFIG_RT_USING_CONSOLE=y +CONFIG_RT_CONSOLEBUF_SIZE=128 +CONFIG_RT_CONSOLE_DEVICE_NAME="uart1" +CONFIG_RT_VER_NUM=0x40101 +# CONFIG_RT_USING_CPU_FFS is not set +CONFIG_ARCH_RISCV=y +# CONFIG_ARCH_CPU_STACK_GROWS_UPWARD is not set + +# +# RT-Thread Components +# +CONFIG_RT_USING_COMPONENTS_INIT=y +CONFIG_RT_USING_USER_MAIN=y +CONFIG_RT_MAIN_THREAD_STACK_SIZE=2048 +CONFIG_RT_MAIN_THREAD_PRIORITY=10 +# CONFIG_RT_USING_LEGACY is not set +CONFIG_RT_USING_MSH=y +CONFIG_RT_USING_FINSH=y +CONFIG_FINSH_USING_MSH=y +CONFIG_FINSH_THREAD_NAME="tshell" +CONFIG_FINSH_THREAD_PRIORITY=20 +CONFIG_FINSH_THREAD_STACK_SIZE=4096 +CONFIG_FINSH_USING_HISTORY=y +CONFIG_FINSH_HISTORY_LINES=5 +CONFIG_FINSH_USING_SYMTAB=y +CONFIG_FINSH_CMD_SIZE=80 +CONFIG_MSH_USING_BUILT_IN_COMMANDS=y +CONFIG_FINSH_USING_DESCRIPTION=y +# CONFIG_FINSH_ECHO_DISABLE_DEFAULT is not set +# CONFIG_FINSH_USING_AUTH is not set +CONFIG_FINSH_ARG_MAX=10 +# CONFIG_RT_USING_DFS is not set +# CONFIG_RT_USING_FAL is not set + +# +# Device Drivers +# +CONFIG_RT_USING_DEVICE_IPC=y +# CONFIG_RT_USING_SYSTEM_WORKQUEUE is not set +CONFIG_RT_USING_SERIAL=y +CONFIG_RT_USING_SERIAL_V1=y +# CONFIG_RT_USING_SERIAL_V2 is not set +# CONFIG_RT_SERIAL_USING_DMA is not set +CONFIG_RT_SERIAL_RB_BUFSZ=64 +# CONFIG_RT_USING_CAN is not set +CONFIG_RT_USING_HWTIMER=y +# CONFIG_RT_USING_CPUTIME is not set +# CONFIG_RT_USING_I2C is not set +# CONFIG_RT_USING_PHY is not set +CONFIG_RT_USING_PIN=y +# CONFIG_RT_USING_ADC is not set +# CONFIG_RT_USING_DAC is not set +# CONFIG_RT_USING_PWM is not set +# CONFIG_RT_USING_MTD_NOR is not set +# CONFIG_RT_USING_MTD_NAND is not set +# CONFIG_RT_USING_PM is not set +# CONFIG_RT_USING_RTC is not set +# CONFIG_RT_USING_SDIO is not set +# CONFIG_RT_USING_SPI is not set +CONFIG_RT_USING_WDT=y +# CONFIG_RT_USING_AUDIO is not set +# CONFIG_RT_USING_SENSOR is not set +# CONFIG_RT_USING_TOUCH is not set +# CONFIG_RT_USING_HWCRYPTO is not set +# CONFIG_RT_USING_PULSE_ENCODER is not set +# CONFIG_RT_USING_INPUT_CAPTURE is not set +# CONFIG_RT_USING_WIFI is not set + +# +# Using USB +# +# CONFIG_RT_USING_USB is not set +# CONFIG_RT_USING_USB_HOST is not set +# CONFIG_RT_USING_USB_DEVICE is not set + +# +# C/C++ and POSIX layer +# +CONFIG_RT_LIBC_DEFAULT_TIMEZONE=8 + +# +# POSIX (Portable Operating System Interface) layer +# +# CONFIG_RT_USING_POSIX_FS is not set +# CONFIG_RT_USING_POSIX_DELAY is not set +# CONFIG_RT_USING_POSIX_CLOCK is not set +# CONFIG_RT_USING_POSIX_TIMER is not set +# CONFIG_RT_USING_PTHREADS is not set +# CONFIG_RT_USING_MODULE is not set + +# +# Interprocess Communication (IPC) +# +# CONFIG_RT_USING_POSIX_PIPE is not set +# CONFIG_RT_USING_POSIX_MESSAGE_QUEUE is not set +# CONFIG_RT_USING_POSIX_MESSAGE_SEMAPHORE is not set + +# +# Socket is in the 'Network' category +# +# CONFIG_RT_USING_CPLUSPLUS is not set + +# +# Network +# +# CONFIG_RT_USING_SAL is not set +# CONFIG_RT_USING_NETDEV is not set +# CONFIG_RT_USING_LWIP is not set +# CONFIG_RT_USING_AT is not set + +# +# Utilities +# +# CONFIG_RT_USING_RYM is not set +# CONFIG_RT_USING_ULOG is not set +# CONFIG_RT_USING_UTEST is not set +# CONFIG_RT_USING_VAR_EXPORT is not set +# CONFIG_RT_USING_RT_LINK is not set +# CONFIG_RT_USING_VBUS is not set + +# +# RT-Thread Utestcases +# +# CONFIG_RT_USING_UTESTCASES is not set + +# +# RT-Thread online packages +# + +# +# IoT - internet of things +# +# CONFIG_PKG_USING_LWIP is not set +# CONFIG_PKG_USING_LORAWAN_DRIVER is not set +# CONFIG_PKG_USING_PAHOMQTT is not set +# CONFIG_PKG_USING_UMQTT is not set +# CONFIG_PKG_USING_WEBCLIENT is not set +# CONFIG_PKG_USING_WEBNET is not set +# CONFIG_PKG_USING_MONGOOSE is not set +# CONFIG_PKG_USING_MYMQTT is not set +# CONFIG_PKG_USING_KAWAII_MQTT is not set +# CONFIG_PKG_USING_BC28_MQTT is not set +# CONFIG_PKG_USING_WEBTERMINAL is not set +# CONFIG_PKG_USING_LIBMODBUS is not set +# CONFIG_PKG_USING_FREEMODBUS is not set +# CONFIG_PKG_USING_NANOPB is not set + +# +# Wi-Fi +# + +# +# Marvell WiFi +# +# CONFIG_PKG_USING_WLANMARVELL is not set + +# +# Wiced WiFi +# +# CONFIG_PKG_USING_WLAN_WICED is not set +# CONFIG_PKG_USING_RW007 is not set +# CONFIG_PKG_USING_COAP is not set +# CONFIG_PKG_USING_NOPOLL is not set +# CONFIG_PKG_USING_NETUTILS is not set +# CONFIG_PKG_USING_CMUX is not set +# CONFIG_PKG_USING_PPP_DEVICE is not set +# CONFIG_PKG_USING_AT_DEVICE is not set +# CONFIG_PKG_USING_ATSRV_SOCKET is not set +# CONFIG_PKG_USING_WIZNET is not set +# CONFIG_PKG_USING_ZB_COORDINATOR is not set + +# +# IoT Cloud +# +# CONFIG_PKG_USING_ONENET is not set +# CONFIG_PKG_USING_GAGENT_CLOUD is not set +# CONFIG_PKG_USING_ALI_IOTKIT is not set +# CONFIG_PKG_USING_AZURE is not set +# CONFIG_PKG_USING_TENCENT_IOT_EXPLORER is not set +# CONFIG_PKG_USING_JIOT-C-SDK is not set +# CONFIG_PKG_USING_UCLOUD_IOT_SDK is not set +# CONFIG_PKG_USING_JOYLINK is not set +# CONFIG_PKG_USING_EZ_IOT_OS is not set +# CONFIG_PKG_USING_IOTSHARP_SDK is not set +# CONFIG_PKG_USING_NIMBLE is not set +# CONFIG_PKG_USING_LLSYNC_SDK_ADAPTER is not set +# CONFIG_PKG_USING_OTA_DOWNLOADER is not set +# CONFIG_PKG_USING_IPMSG is not set +# CONFIG_PKG_USING_LSSDP is not set +# CONFIG_PKG_USING_AIRKISS_OPEN is not set +# CONFIG_PKG_USING_LIBRWS is not set +# CONFIG_PKG_USING_TCPSERVER is not set +# CONFIG_PKG_USING_PROTOBUF_C is not set +# CONFIG_PKG_USING_DLT645 is not set +# CONFIG_PKG_USING_QXWZ is not set +# CONFIG_PKG_USING_SMTP_CLIENT is not set +# CONFIG_PKG_USING_ABUP_FOTA is not set +# CONFIG_PKG_USING_LIBCURL2RTT is not set +# CONFIG_PKG_USING_CAPNP is not set +# CONFIG_PKG_USING_AGILE_TELNET is not set +# CONFIG_PKG_USING_NMEALIB is not set +# CONFIG_PKG_USING_PDULIB is not set +# CONFIG_PKG_USING_BTSTACK is not set +# CONFIG_PKG_USING_LORAWAN_ED_STACK is not set +# CONFIG_PKG_USING_WAYZ_IOTKIT is not set +# CONFIG_PKG_USING_MAVLINK is not set +# CONFIG_PKG_USING_BSAL is not set +# CONFIG_PKG_USING_AGILE_MODBUS is not set +# CONFIG_PKG_USING_AGILE_FTP is not set +# CONFIG_PKG_USING_EMBEDDEDPROTO is not set +# CONFIG_PKG_USING_RT_LINK_HW is not set +# CONFIG_PKG_USING_LORA_PKT_FWD is not set +# CONFIG_PKG_USING_LORA_GW_DRIVER_LIB is not set +# CONFIG_PKG_USING_LORA_PKT_SNIFFER is not set +# CONFIG_PKG_USING_HM is not set +# CONFIG_PKG_USING_SMALL_MODBUS is not set +# CONFIG_PKG_USING_NET_SERVER is not set +# CONFIG_PKG_USING_ZFTP is not set + +# +# security packages +# +# CONFIG_PKG_USING_MBEDTLS is not set +# CONFIG_PKG_USING_LIBSODIUM is not set +# CONFIG_PKG_USING_LIBHYDROGEN is not set +# CONFIG_PKG_USING_TINYCRYPT is not set +# CONFIG_PKG_USING_TFM is not set +# CONFIG_PKG_USING_YD_CRYPTO is not set + +# +# language packages +# + +# +# JSON: JavaScript Object Notation, a lightweight data-interchange format +# +# CONFIG_PKG_USING_CJSON is not set +# CONFIG_PKG_USING_LJSON is not set +# CONFIG_PKG_USING_RT_CJSON_TOOLS is not set +# CONFIG_PKG_USING_RAPIDJSON is not set +# CONFIG_PKG_USING_JSMN is not set +# CONFIG_PKG_USING_AGILE_JSMN is not set + +# +# XML: Extensible Markup Language +# +# CONFIG_PKG_USING_SIMPLE_XML is not set +# CONFIG_PKG_USING_EZXML is not set +# CONFIG_PKG_USING_LUATOS_SOC is not set +# CONFIG_PKG_USING_LUA is not set +# CONFIG_PKG_USING_JERRYSCRIPT is not set +# CONFIG_PKG_USING_MICROPYTHON is not set +# CONFIG_PKG_USING_PIKASCRIPT is not set +# CONFIG_PKG_USING_RTT_RUST is not set + +# +# multimedia packages +# + +# +# LVGL: powerful and easy-to-use embedded GUI library +# +# CONFIG_PKG_USING_LVGL is not set +# CONFIG_PKG_USING_LITTLEVGL2RTT is not set +# CONFIG_PKG_USING_LV_MUSIC_DEMO is not set +# CONFIG_PKG_USING_GUI_GUIDER_DEMO is not set + +# +# u8g2: a monochrome graphic library +# +# CONFIG_PKG_USING_U8G2_OFFICIAL is not set +# CONFIG_PKG_USING_U8G2 is not set +# CONFIG_PKG_USING_OPENMV is not set +# CONFIG_PKG_USING_MUPDF is not set +# CONFIG_PKG_USING_STEMWIN is not set +# CONFIG_PKG_USING_WAVPLAYER is not set +# CONFIG_PKG_USING_TJPGD is not set +# CONFIG_PKG_USING_PDFGEN is not set +# CONFIG_PKG_USING_HELIX is not set +# CONFIG_PKG_USING_AZUREGUIX is not set +# CONFIG_PKG_USING_TOUCHGFX2RTT is not set +# CONFIG_PKG_USING_NUEMWIN is not set +# CONFIG_PKG_USING_MP3PLAYER is not set +# CONFIG_PKG_USING_TINYJPEG is not set +# CONFIG_PKG_USING_UGUI is not set + +# +# PainterEngine: A cross-platform graphics application framework written in C language +# +# CONFIG_PKG_USING_PAINTERENGINE is not set +# CONFIG_PKG_USING_PAINTERENGINE_AUX is not set +# CONFIG_PKG_USING_MCURSES is not set +# CONFIG_PKG_USING_TERMBOX is not set +# CONFIG_PKG_USING_VT100 is not set +# CONFIG_PKG_USING_QRCODE is not set +# CONFIG_PKG_USING_GUIENGINE is not set + +# +# tools packages +# +# CONFIG_PKG_USING_CMBACKTRACE is not set +# CONFIG_PKG_USING_EASYFLASH is not set +# CONFIG_PKG_USING_EASYLOGGER is not set +# CONFIG_PKG_USING_SYSTEMVIEW is not set +# CONFIG_PKG_USING_SEGGER_RTT is not set +# CONFIG_PKG_USING_RDB is not set +# CONFIG_PKG_USING_ULOG_EASYFLASH is not set +# CONFIG_PKG_USING_ULOG_FILE is not set +# CONFIG_PKG_USING_LOGMGR is not set +# CONFIG_PKG_USING_ADBD is not set +# CONFIG_PKG_USING_COREMARK is not set +# CONFIG_PKG_USING_DHRYSTONE is not set +# CONFIG_PKG_USING_MEMORYPERF is not set +# CONFIG_PKG_USING_NR_MICRO_SHELL is not set +# CONFIG_PKG_USING_CHINESE_FONT_LIBRARY is not set +# CONFIG_PKG_USING_LUNAR_CALENDAR is not set +# CONFIG_PKG_USING_BS8116A is not set +# CONFIG_PKG_USING_GPS_RMC is not set +# CONFIG_PKG_USING_URLENCODE is not set +# CONFIG_PKG_USING_UMCN is not set +# CONFIG_PKG_USING_LWRB2RTT is not set +# CONFIG_PKG_USING_CPU_USAGE is not set +# CONFIG_PKG_USING_GBK2UTF8 is not set +# CONFIG_PKG_USING_VCONSOLE is not set +# CONFIG_PKG_USING_KDB is not set +# CONFIG_PKG_USING_WAMR is not set +# CONFIG_PKG_USING_MICRO_XRCE_DDS_CLIENT is not set +# CONFIG_PKG_USING_LWLOG is not set +# CONFIG_PKG_USING_ANV_TRACE is not set +# CONFIG_PKG_USING_ANV_MEMLEAK is not set +# CONFIG_PKG_USING_ANV_TESTSUIT is not set +# CONFIG_PKG_USING_ANV_BENCH is not set +# CONFIG_PKG_USING_DEVMEM is not set +# CONFIG_PKG_USING_REGEX is not set +# CONFIG_PKG_USING_MEM_SANDBOX is not set +# CONFIG_PKG_USING_SOLAR_TERMS is not set +# CONFIG_PKG_USING_GAN_ZHI is not set +# CONFIG_PKG_USING_FDT is not set +# CONFIG_PKG_USING_CBOX is not set +# CONFIG_PKG_USING_SNOWFLAKE is not set +# CONFIG_PKG_USING_HASH_MATCH is not set +# CONFIG_PKG_USING_FIRE_PID_CURVE is not set +# CONFIG_PKG_USING_ARMV7M_DWT_TOOL is not set + +# +# system packages +# + +# +# enhanced kernel services +# +# CONFIG_PKG_USING_RT_MEMCPY_CM is not set +# CONFIG_PKG_USING_RT_KPRINTF_THREADSAFE is not set +# CONFIG_PKG_USING_RT_VSNPRINTF_FULL is not set + +# +# acceleration: Assembly language or algorithmic acceleration packages +# +# CONFIG_PKG_USING_QFPLIB_M0_FULL is not set +# CONFIG_PKG_USING_QFPLIB_M0_TINY is not set +# CONFIG_PKG_USING_QFPLIB_M3 is not set + +# +# CMSIS: ARM Cortex-M Microcontroller Software Interface Standard +# +# CONFIG_PKG_USING_CMSIS_5 is not set +# CONFIG_PKG_USING_CMSIS_RTOS1 is not set +# CONFIG_PKG_USING_CMSIS_RTOS2 is not set + +# +# Micrium: Micrium software products porting for RT-Thread +# +# CONFIG_PKG_USING_UCOSIII_WRAPPER is not set +# CONFIG_PKG_USING_UCOSII_WRAPPER is not set +# CONFIG_PKG_USING_UC_CRC is not set +# CONFIG_PKG_USING_UC_CLK is not set +# CONFIG_PKG_USING_UC_COMMON is not set +# CONFIG_PKG_USING_UC_MODBUS is not set +# CONFIG_PKG_USING_RTDUINO is not set +# CONFIG_PKG_USING_CAIRO is not set +# CONFIG_PKG_USING_PIXMAN is not set +# CONFIG_PKG_USING_PARTITION is not set +# CONFIG_PKG_USING_PERF_COUNTER is not set +# CONFIG_PKG_USING_FLASHDB is not set +# CONFIG_PKG_USING_SQLITE is not set +# CONFIG_PKG_USING_RTI is not set +# CONFIG_PKG_USING_DFS_YAFFS is not set +# CONFIG_PKG_USING_LITTLEFS is not set +# CONFIG_PKG_USING_DFS_JFFS2 is not set +# CONFIG_PKG_USING_DFS_UFFS is not set +# CONFIG_PKG_USING_LWEXT4 is not set +# CONFIG_PKG_USING_THREAD_POOL is not set +# CONFIG_PKG_USING_ROBOTS is not set +# CONFIG_PKG_USING_EV is not set +# CONFIG_PKG_USING_SYSWATCH is not set +# CONFIG_PKG_USING_SYS_LOAD_MONITOR is not set +# CONFIG_PKG_USING_PLCCORE is not set +# CONFIG_PKG_USING_RAMDISK is not set +# CONFIG_PKG_USING_MININI is not set +# CONFIG_PKG_USING_QBOOT is not set +# CONFIG_PKG_USING_PPOOL is not set +# CONFIG_PKG_USING_OPENAMP is not set +# CONFIG_PKG_USING_LPM is not set +# CONFIG_PKG_USING_TLSF is not set +# CONFIG_PKG_USING_EVENT_RECORDER is not set +# CONFIG_PKG_USING_ARM_2D is not set +# CONFIG_PKG_USING_MCUBOOT is not set +# CONFIG_PKG_USING_TINYUSB is not set +# CONFIG_PKG_USING_CHERRYUSB is not set +# CONFIG_PKG_USING_KMULTI_RTIMER is not set +# CONFIG_PKG_USING_TFDB is not set +# CONFIG_PKG_USING_QPC is not set + +# +# peripheral libraries and drivers +# +# CONFIG_PKG_USING_SENSORS_DRIVERS is not set +# CONFIG_PKG_USING_REALTEK_AMEBA is not set +# CONFIG_PKG_USING_SHT2X is not set +# CONFIG_PKG_USING_SHT3X is not set +# CONFIG_PKG_USING_AS7341 is not set +# CONFIG_PKG_USING_STM32_SDIO is not set +# CONFIG_PKG_USING_ICM20608 is not set +# CONFIG_PKG_USING_BUTTON is not set +# CONFIG_PKG_USING_PCF8574 is not set +# CONFIG_PKG_USING_SX12XX is not set +# CONFIG_PKG_USING_SIGNAL_LED is not set +# CONFIG_PKG_USING_LEDBLINK is not set +# CONFIG_PKG_USING_LITTLED is not set +# CONFIG_PKG_USING_LKDGUI is not set +# CONFIG_PKG_USING_NRF5X_SDK is not set +# CONFIG_PKG_USING_NRFX is not set +# CONFIG_PKG_USING_WM_LIBRARIES is not set +# CONFIG_PKG_USING_KENDRYTE_SDK is not set +# CONFIG_PKG_USING_INFRARED is not set +# CONFIG_PKG_USING_MULTI_INFRARED is not set +# CONFIG_PKG_USING_AGILE_BUTTON is not set +# CONFIG_PKG_USING_AGILE_LED is not set +# CONFIG_PKG_USING_AT24CXX is not set +# CONFIG_PKG_USING_MOTIONDRIVER2RTT is not set +# CONFIG_PKG_USING_AD7746 is not set +# CONFIG_PKG_USING_PCA9685 is not set +# CONFIG_PKG_USING_I2C_TOOLS is not set +# CONFIG_PKG_USING_NRF24L01 is not set +# CONFIG_PKG_USING_TOUCH_DRIVERS is not set +# CONFIG_PKG_USING_MAX17048 is not set +# CONFIG_PKG_USING_RPLIDAR is not set +# CONFIG_PKG_USING_AS608 is not set +# CONFIG_PKG_USING_RC522 is not set +# CONFIG_PKG_USING_WS2812B is not set +# CONFIG_PKG_USING_EMBARC_BSP is not set +# CONFIG_PKG_USING_EXTERN_RTC_DRIVERS is not set +# CONFIG_PKG_USING_MULTI_RTIMER is not set +# CONFIG_PKG_USING_MAX7219 is not set +# CONFIG_PKG_USING_BEEP is not set +# CONFIG_PKG_USING_EASYBLINK is not set +# CONFIG_PKG_USING_PMS_SERIES is not set +# CONFIG_PKG_USING_NUCLEI_SDK is not set +# CONFIG_PKG_USING_CAN_YMODEM is not set +# CONFIG_PKG_USING_LORA_RADIO_DRIVER is not set +# CONFIG_PKG_USING_QLED is not set +# CONFIG_PKG_USING_PAJ7620 is not set +# CONFIG_PKG_USING_AGILE_CONSOLE is not set +# CONFIG_PKG_USING_LD3320 is not set +# CONFIG_PKG_USING_WK2124 is not set +# CONFIG_PKG_USING_LY68L6400 is not set +# CONFIG_PKG_USING_DM9051 is not set +# CONFIG_PKG_USING_SSD1306 is not set +# CONFIG_PKG_USING_QKEY is not set +# CONFIG_PKG_USING_RS485 is not set +# CONFIG_PKG_USING_RS232 is not set +# CONFIG_PKG_USING_NES is not set +# CONFIG_PKG_USING_VIRTUAL_SENSOR is not set +# CONFIG_PKG_USING_VDEVICE is not set +# CONFIG_PKG_USING_SGM706 is not set +# CONFIG_PKG_USING_STM32WB55_SDK is not set +# CONFIG_PKG_USING_RDA58XX is not set +# CONFIG_PKG_USING_LIBNFC is not set +# CONFIG_PKG_USING_MFOC is not set +# CONFIG_PKG_USING_TMC51XX is not set +# CONFIG_PKG_USING_TCA9534 is not set +# CONFIG_PKG_USING_KOBUKI is not set +# CONFIG_PKG_USING_ROSSERIAL is not set +# CONFIG_PKG_USING_MICRO_ROS is not set +# CONFIG_PKG_USING_MCP23008 is not set +# CONFIG_PKG_USING_BLUETRUM_SDK is not set +# CONFIG_PKG_USING_MISAKA_AT24CXX is not set +# CONFIG_PKG_USING_MISAKA_RGB_BLING is not set +# CONFIG_PKG_USING_LORA_MODEM_DRIVER is not set +# CONFIG_PKG_USING_BL_MCU_SDK is not set +# CONFIG_PKG_USING_SOFT_SERIAL is not set +# CONFIG_PKG_USING_MB85RS16 is not set +# CONFIG_PKG_USING_CW2015 is not set +# CONFIG_PKG_USING_RFM300 is not set + +# +# AI packages +# +# CONFIG_PKG_USING_LIBANN is not set +# CONFIG_PKG_USING_NNOM is not set +# CONFIG_PKG_USING_ONNX_BACKEND is not set +# CONFIG_PKG_USING_ONNX_PARSER is not set +# CONFIG_PKG_USING_TENSORFLOWLITEMICRO is not set +# CONFIG_PKG_USING_ELAPACK is not set +# CONFIG_PKG_USING_ULAPACK is not set +# CONFIG_PKG_USING_QUEST is not set +# CONFIG_PKG_USING_NAXOS is not set + +# +# miscellaneous packages +# + +# +# project laboratory +# + +# +# samples: kernel and components samples +# +# CONFIG_PKG_USING_KERNEL_SAMPLES is not set +# CONFIG_PKG_USING_FILESYSTEM_SAMPLES is not set +# CONFIG_PKG_USING_NETWORK_SAMPLES is not set +# CONFIG_PKG_USING_PERIPHERAL_SAMPLES is not set + +# +# entertainment: terminal games and other interesting software packages +# +# CONFIG_PKG_USING_CMATRIX is not set +# CONFIG_PKG_USING_SL is not set +# CONFIG_PKG_USING_CAL is not set +# CONFIG_PKG_USING_ACLOCK is not set +# CONFIG_PKG_USING_THREES is not set +# CONFIG_PKG_USING_2048 is not set +# CONFIG_PKG_USING_SNAKE is not set +# CONFIG_PKG_USING_TETRIS is not set +# CONFIG_PKG_USING_DONUT is not set +# CONFIG_PKG_USING_COWSAY is not set +# CONFIG_PKG_USING_LIBCSV is not set +# CONFIG_PKG_USING_OPTPARSE is not set +# CONFIG_PKG_USING_FASTLZ is not set +# CONFIG_PKG_USING_MINILZO is not set +# CONFIG_PKG_USING_QUICKLZ is not set +# CONFIG_PKG_USING_LZMA is not set +# CONFIG_PKG_USING_MULTIBUTTON is not set +# CONFIG_PKG_USING_FLEXIBLE_BUTTON is not set +# CONFIG_PKG_USING_CANFESTIVAL is not set +# CONFIG_PKG_USING_ZLIB is not set +# CONFIG_PKG_USING_MINIZIP is not set +# CONFIG_PKG_USING_HEATSHRINK is not set +# CONFIG_PKG_USING_DSTR is not set +# CONFIG_PKG_USING_TINYFRAME is not set +# CONFIG_PKG_USING_KENDRYTE_DEMO is not set +# CONFIG_PKG_USING_DIGITALCTRL is not set +# CONFIG_PKG_USING_UPACKER is not set +# CONFIG_PKG_USING_UPARAM is not set +# CONFIG_PKG_USING_HELLO is not set +# CONFIG_PKG_USING_VI is not set +# CONFIG_PKG_USING_KI is not set +# CONFIG_PKG_USING_ARMv7M_DWT is not set +# CONFIG_PKG_USING_UKAL is not set +# CONFIG_PKG_USING_CRCLIB is not set +# CONFIG_PKG_USING_LWGPS is not set +# CONFIG_PKG_USING_STATE_MACHINE is not set +# CONFIG_PKG_USING_DESIGN_PATTERN is not set +# CONFIG_PKG_USING_CONTROLLER is not set +# CONFIG_PKG_USING_PHASE_LOCKED_LOOP is not set +# CONFIG_PKG_USING_MFBD is not set +# CONFIG_PKG_USING_SLCAN2RTT is not set +# CONFIG_PKG_USING_SOEM is not set +CONFIG_SOC_FAMILY_CH56X=y +CONFIG_SOC_SERIES_CH569=y + +# +# Hardware Drivers Config +# +CONFIG_SOC_CH569W=y + +# +# On-chip Peripheral Drivers +# +CONFIG_BSP_USING_UART=y +# CONFIG_BSP_USING_UART0 is not set +CONFIG_BSP_USING_UART1=y +# CONFIG_BSP_USING_UART2 is not set +# CONFIG_BSP_USING_UART3 is not set +CONFIG_BSP_USING_TIMER=y +CONFIG_BSP_USING_TMR0=y +CONFIG_BSP_USING_TMR1=y +# CONFIG_BSP_USING_TMR2 is not set + +# +# Onboard Peripheral Drivers +# + +# +# Board extended module Drivers +# diff --git a/bsp/wch/risc-v/ch569w-evt/Kconfig b/bsp/wch/risc-v/ch569w-evt/Kconfig new file mode 100644 index 0000000000..c80a8958fd --- /dev/null +++ b/bsp/wch/risc-v/ch569w-evt/Kconfig @@ -0,0 +1,21 @@ +mainmenu "RT-Thread Configuration" + +config BSP_DIR + string + option env="BSP_ROOT" + default "." + +config RTT_DIR + string + option env="RTT_ROOT" + default "../../../.." + +config PKGS_DIR + string + option env="PKGS_ROOT" + default "packages" + +source "$RTT_DIR/Kconfig" +source "$PKGS_DIR/Kconfig" +source "../Libraries/Kconfig" +source "board/Kconfig" diff --git a/bsp/wch/risc-v/ch569w-evt/SConscript b/bsp/wch/risc-v/ch569w-evt/SConscript new file mode 100644 index 0000000000..fab6e36f21 --- /dev/null +++ b/bsp/wch/risc-v/ch569w-evt/SConscript @@ -0,0 +1,13 @@ +import os +from building import * + +objs = [] + +cwd = GetCurrentDir() +list = os.listdir(cwd) + +for d in list: + if os.path.isfile(os.path.join(cwd, d, 'SConscript')): + objs = objs + SConscript(os.path.join(d, 'SConscript')) + +Return('objs') diff --git a/bsp/wch/risc-v/ch569w-evt/SConstruct b/bsp/wch/risc-v/ch569w-evt/SConstruct new file mode 100644 index 0000000000..0264ce6202 --- /dev/null +++ b/bsp/wch/risc-v/ch569w-evt/SConstruct @@ -0,0 +1,38 @@ +import os +import sys +import rtconfig + +if os.getenv('RTT_ROOT'): + RTT_ROOT = os.getenv('RTT_ROOT') +else: + RTT_ROOT = os.path.normpath(os.getcwd() + '/../../../..') + +sys.path = sys.path + [os.path.join(RTT_ROOT, 'tools')] +try: + from building import * +except: + print('Cannot find RT-Thread root directory, please check RTT_ROOT') + print('RTT_ROOT: ' + RTT_ROOT) + exit(-1) + +TARGET = 'rtthread.' + rtconfig.TARGET_EXT + +DefaultEnvironment(tools=[]) +env = Environment(tools = ['mingw'], + AS = rtconfig.AS, ASFLAGS = rtconfig.AFLAGS, + CC = rtconfig.CC, CFLAGS = rtconfig.CFLAGS, + AR = rtconfig.AR, ARFLAGS = '-rc', + LINK = rtconfig.LINK, LINKFLAGS = rtconfig.LFLAGS) +env.PrependENVPath('PATH', rtconfig.EXEC_PATH) + +Export('RTT_ROOT') +Export('rtconfig') + +# prepare building environment +objs = PrepareBuilding(env, RTT_ROOT, has_libcpu=False) + +objs.extend(SConscript('../Libraries/ch56x_drivers/SConscript', + variant_dir='build/libraries/ch56x_drivers', duplicate=0)) + +# make a building +DoBuilding(TARGET, objs) diff --git a/bsp/wch/risc-v/ch569w-evt/applications/SConscript b/bsp/wch/risc-v/ch569w-evt/applications/SConscript new file mode 100644 index 0000000000..72c14ab8db --- /dev/null +++ b/bsp/wch/risc-v/ch569w-evt/applications/SConscript @@ -0,0 +1,13 @@ +from building import * + +cwd = GetCurrentDir() + +src = Split(""" +main.c +""") + +path = [cwd, str(Dir('#'))] + +group = DefineGroup('Applications', src, depend=[''], CPPPATH=path) + +Return('group') diff --git a/bsp/wch/risc-v/ch569w-evt/applications/main.c b/bsp/wch/risc-v/ch569w-evt/applications/main.c new file mode 100644 index 0000000000..3c126ce04f --- /dev/null +++ b/bsp/wch/risc-v/ch569w-evt/applications/main.c @@ -0,0 +1,231 @@ +/* + * Copyright (c) 2006-2022, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2022-07-15 Emuzit first version + * 2022-07-20 Emuzit add watchdog test + * 2022-07-26 Emuzit add hwtimer test + */ +#include +#include +#include +#include +#include +#include "board.h" + +static const rt_base_t gpio_int_pins[8] = GPIO_INT_PINS; + +/* note : PIN_IRQ_MODE_RISING_FALLING not supported */ +static const uint32_t gpint_mode[] = +{ + PIN_IRQ_MODE_RISING, + PIN_IRQ_MODE_RISING, + PIN_IRQ_MODE_RISING, + PIN_IRQ_MODE_RISING, + PIN_IRQ_MODE_FALLING, + PIN_IRQ_MODE_FALLING, + PIN_IRQ_MODE_FALLING, + PIN_IRQ_MODE_FALLING, +}; + +static struct rt_mailbox *gpint_mb = RT_NULL; +static struct rt_thread *gpint_thread = RT_NULL; + +static rt_base_t led0, led1; + +static rt_device_t wdg_dev; + +static struct rt_device *tmr_dev_0; +static struct rt_device *tmr_dev_1; + +static void gpio_int_callback(void *pin) +{ + led1 = (led1 == PIN_LOW) ? PIN_HIGH : PIN_LOW; + rt_pin_write(LED1_PIN, led1); + + if (gpint_mb != RT_NULL) + { + /* non-block, silently ignore RT_EFULL */ + rt_mb_send(gpint_mb, (uint32_t)pin); + } +} + +static void gpio_int_thread(void *param) +{ + while (1) + { + rt_err_t res; + uint32_t pin; + + res = rt_mb_recv(gpint_mb, &pin, RT_WAITING_FOREVER); + if (res == RT_EOK) + { + rt_kprintf("gpio_int #%d (%d)\n", pin, rt_pin_read(pin)); + } + rt_thread_mdelay(100); + +#ifdef RT_USING_WDT + rt_device_control(wdg_dev, RT_DEVICE_CTRL_WDT_KEEPALIVE, RT_NULL); +#endif + } +} + +#ifdef RT_USING_HWTIMER +static rt_err_t tmr_timeout_cb(rt_device_t dev, rt_size_t size) +{ + rt_tick_t tick = rt_tick_get(); + + int tmr = (dev == tmr_dev_1) ? 1 : 0; + + rt_kprintf("hwtimer %d timeout callback fucntion @tick %d\n", tmr, tick); + + return RT_EOK; +} +#endif + +void main(void) +{ + rt_hwtimerval_t timerval; + rt_hwtimer_mode_t mode; + rt_size_t tsize; + + uint32_t seconds; + rt_err_t res; + + int i; + + rt_kprintf("\nCH569W-R0-1v0, HCLK: %dMHz\n\n", sys_hclk_get() / 1000000); + + /* Enable all gpio interrupt with various modes. + * LED0 or GND touching can be used to trigger pin interrupt. + */ + gpint_mb = rt_mb_create("pximb", 8, RT_IPC_FLAG_FIFO); + if (gpint_mb == RT_NULL) + { + rt_kprintf("gpint mailbox create failed !\n"); + } + else + { + gpint_thread = rt_thread_create("pxith", gpio_int_thread, RT_NULL, + 512, RT_MAIN_THREAD_PRIORITY, 50); + if (gpint_thread == RT_NULL) + { + rt_kprintf("gpint thread create failed !\n"); + } + else + { + rt_thread_startup(gpint_thread); + + for (i = 0; i < 8; i++) + { + rt_base_t pin = gpio_int_pins[i]; + rt_pin_mode(pin, PIN_MODE_INPUT_PULLUP); + res = rt_pin_attach_irq( + pin, gpint_mode[i], gpio_int_callback, (void *)pin); + if (res != RT_EOK) + { + rt_kprintf("rt_pin_attach_irq failed (%d:%d)\n", i, res); + } + else + { + rt_pin_irq_enable(pin, PIN_IRQ_ENABLE); + } + } + } + } + +#ifdef RT_USING_WDT + /* Test watchdog with 30s timeout, keepalive with gpio interrupt. + * + * CAVEAT: With only 8-bit WDOG_COUNT and fixed clocking at Fsys/524288, + * watchdog of ch56x may be quite limited with very short timeout. + */ + seconds = 30; + wdg_dev = rt_device_find("wdt"); + if (!wdg_dev) + { + rt_kprintf("watchdog device not found !\n"); + } + else if (rt_device_init(wdg_dev) != RT_EOK || + rt_device_control(wdg_dev, RT_DEVICE_CTRL_WDT_SET_TIMEOUT, &seconds) != RT_EOK) + { + rt_kprintf("watchdog setup failed !\n"); + } + else + { + rt_kprintf("WDT_TIMEOUT in %d seconds, trigger gpio interrupt to keep alive.\n\n", seconds); + } +#endif + +#ifdef RT_USING_HWTIMER + /* setup two timers, ONESHOT & PERIOD each + */ + tmr_dev_0 = rt_device_find("timer0"); + tmr_dev_1 = rt_device_find("timer1"); + if (tmr_dev_0 == RT_NULL || tmr_dev_1 == RT_NULL) + { + rt_kprintf("hwtimer device(s) not found !\n"); + } + else if (rt_device_open(tmr_dev_0, RT_DEVICE_OFLAG_RDWR) != RT_EOK || + rt_device_open(tmr_dev_1, RT_DEVICE_OFLAG_RDWR) != RT_EOK) + { + rt_kprintf("hwtimer device(s) open failed !\n"); + } + else + { + rt_device_set_rx_indicate(tmr_dev_0, tmr_timeout_cb); + rt_device_set_rx_indicate(tmr_dev_1, tmr_timeout_cb); + + timerval.sec = 3; + timerval.usec = 500000; + tsize = sizeof(timerval); + mode = HWTIMER_MODE_ONESHOT; + if (rt_device_control(tmr_dev_0, HWTIMER_CTRL_MODE_SET, &mode) != RT_EOK) + { + rt_kprintf("timer0 set mode failed !\n"); + } + else if (rt_device_write(tmr_dev_0, 0, &timerval, tsize) != tsize) + { + rt_kprintf("timer0 start failed !\n"); + } + else + { + rt_kprintf("timer0 started !\n"); + } + + timerval.sec = 5; + timerval.usec = 0; + tsize = sizeof(timerval); + mode = HWTIMER_MODE_PERIOD; + if (rt_device_control(tmr_dev_1, HWTIMER_CTRL_MODE_SET, &mode) != RT_EOK) + { + rt_kprintf("timer1 set mode failed !\n"); + } + else if (rt_device_write(tmr_dev_1, 0, &timerval, tsize) != tsize) + { + rt_kprintf("timer1 start failed !\n"); + } + else + { + rt_kprintf("timer1 started !\n"); + } + } +#endif + + rt_pin_mode(LED1_PIN, PIN_MODE_OUTPUT); + rt_pin_write(LED1_PIN, led1 = PIN_HIGH); + + rt_pin_mode(LED0_PIN, PIN_MODE_OUTPUT); + rt_pin_write(LED0_PIN, led0 = PIN_LOW); + + while (1) + { + /* flashing LED0 every 1 second */ + rt_thread_mdelay(500); + led0 = (led0 == PIN_LOW) ? PIN_HIGH : PIN_LOW; + rt_pin_write(LED0_PIN, led0); + } +} diff --git a/bsp/wch/risc-v/ch569w-evt/board/Kconfig b/bsp/wch/risc-v/ch569w-evt/board/Kconfig new file mode 100644 index 0000000000..80a2613d03 --- /dev/null +++ b/bsp/wch/risc-v/ch569w-evt/board/Kconfig @@ -0,0 +1,64 @@ +menu "Hardware Drivers Config" + +config SOC_CH569W + bool + select SOC_FAMILY_CH56X + select SOC_SERIES_CH569 + select RT_USING_COMPONENTS_INIT + select RT_USING_USER_MAIN + default y + +menu "On-chip Peripheral Drivers" + +config BSP_USING_UART + bool "using on-chip uart" + select RT_USING_SERIAL + default y + + if BSP_USING_UART + config BSP_USING_UART0 + bool "using UART0" + default n + + config BSP_USING_UART1 + bool "using UART1" + default y + + config BSP_USING_UART2 + bool "using UART2" + default n + + config BSP_USING_UART3 + bool "using UART3" + default n + endif + +config BSP_USING_TIMER + bool "using on-chip timer" + select RT_USING_HWTIMER + default n + + if BSP_USING_TIMER + config BSP_USING_TMR0 + bool "using TMR0" + default y + + config BSP_USING_TMR1 + bool "using TMR1" + default n + + config BSP_USING_TMR2 + bool "using TMR2" + default n + endif +endmenu + +menu "Onboard Peripheral Drivers" + +endmenu + +menu "Board extended module Drivers" + +endmenu + +endmenu diff --git a/bsp/wch/risc-v/ch569w-evt/board/SConscript b/bsp/wch/risc-v/ch569w-evt/board/SConscript new file mode 100644 index 0000000000..fee2061360 --- /dev/null +++ b/bsp/wch/risc-v/ch569w-evt/board/SConscript @@ -0,0 +1,13 @@ +from building import * + +cwd = GetCurrentDir() + +src = Split(''' +board.c +startup_gcc.S +''') + +path = [cwd] + +group = DefineGroup('Drivers', src, depend=[''], CPPPATH=path) +Return('group') diff --git a/bsp/wch/risc-v/ch569w-evt/board/board.c b/bsp/wch/risc-v/ch569w-evt/board/board.c new file mode 100644 index 0000000000..6b4b6dbc81 --- /dev/null +++ b/bsp/wch/risc-v/ch569w-evt/board/board.c @@ -0,0 +1,80 @@ +/* + * Copyright (c) 2006-2022, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2022-07-15 Emuzit first version + */ +#include +#include +#include "board.h" +#include "ch56x_pfic.h" +#include "ch56x_uart.h" + +extern rt_uint32_t rt_thread_switch_interrupt_flag; + +/* FIXME: Use rt_interrupt_leave_hook to trigger SWI for context switch. + * Hopefully there's a standard riscv way instead of this clumsy patch. +*/ +static void irq_leave_hook(void) +{ + if (rt_thread_switch_interrupt_flag) + { + pfic_swi_pendset(); + } +} + +/* + * _start -> handle_reset + * src/components.c/entry() -> rtthread_startup() + * libcpu/risc-v/common/context_gcc.S/rt_hw_interrupt_disable + */ +void rt_hw_board_init() +{ + volatile struct pfic_registers *pfic = (void *)PFIC_REG_BASE; + + /* disable all pfic interrupts */ + pfic->IRER[0] = PFIC_IREG1_MASK; + pfic->IRER[1] = PFIC_IREG2_MASK; + + /* disable hwstack push/pop & nested interrupt */ + pfic->CFGR = cfgr_nest_hwstk(CFGR_NESTCTRL_DISABLE | CFGR_HWSTKCTRL_DISABLE); + + /* disable clock input for all peripheral devices */ + sys_slp_clk_off0(0xff, SYS_SLP_CLK_OFF); + sys_slp_clk_off1(0xff, SYS_SLP_CLK_OFF); + sys_clk_off_by_irqn(ETH_IRQn, SYS_SLP_CLK_OFF); + sys_clk_off_by_irqn(ECDC_IRQn, SYS_SLP_CLK_OFF); + + /* setup HCLK for systick & peripheral devices */ + sys_hclk_set(SYS_HCLK_FREQ); + + /* set SysTick to RT_TICK_PER_SECOND with current HCLK */ + systick_init(0); + + /* Note: keep MSTATUS_MIE disabled to prevent SysTick from processing + * thread scheduling, which may not be ready upon 1st systick irq. + * MSTATUS_MIE will be set when rt_system_scheduler_start() starts + * the first thread and copies mstatus from stack_frame. + */ + +#ifdef RT_USING_HEAP + rt_system_heap_init((void *)HEAP_BEGIN, (void *)HEAP_END); +#endif + +#ifdef RT_USING_COMPONENTS_INIT + rt_components_board_init(); +#endif + +#ifdef RT_USING_CONSOLE + /* console is uart1, TXD1/RXD1 : PA8/PA7 */ + rt_pin_mode(GET_PIN(A, 8), PIN_MODE_OUTPUT); + rt_pin_mode(GET_PIN(A, 7), PIN_MODE_INPUT_PULLUP); + rt_hw_uart_init(); + rt_console_set_device(RT_CONSOLE_DEVICE_NAME); +#endif + + rt_interrupt_leave_sethook(irq_leave_hook); +} diff --git a/bsp/wch/risc-v/ch569w-evt/board/board.h b/bsp/wch/risc-v/ch569w-evt/board/board.h new file mode 100644 index 0000000000..631352203c --- /dev/null +++ b/bsp/wch/risc-v/ch569w-evt/board/board.h @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2006-2022, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2022-07-15 Emuzit first version + */ +#ifndef __BOARD_H__ +#define __BOARD_H__ + +#include +#include "ch56x_sys.h" +#include "ch56x_gpio.h" + +#define LED0_PIN GET_PIN(B, 24) +#define LED1_PIN GET_PIN(B, 22) +#define LED2_PIN GET_PIN(B, 23) + +#define SYS_HCLK_FREQ 80000000 // 80 MHz + +#define RAMX_SIZE 32 // USER_MEM 00/01/1x : 32/64/96 KB +#define RAMX_END (RAMX_BASE_ADDRESS + RAMX_SIZE * 1024) + +extern uint32_t _ebss, _heap_end; +extern uint32_t _susrstack, _eusrstack; + +#define HEAP_BEGIN ((void *)&_ebss) +#define HEAP_END ((void *)&_heap_end) +#define SUSRSTACK ((void *)&_susrstack) +#define EUSRSTACK ((void *)&_eusrstack) + +void rt_hw_board_init(void); + +#endif /* __BOARD_H__ */ diff --git a/bsp/wch/risc-v/ch569w-evt/board/linker_scripts/link.lds b/bsp/wch/risc-v/ch569w-evt/board/linker_scripts/link.lds new file mode 100644 index 0000000000..41dce1bbea --- /dev/null +++ b/bsp/wch/risc-v/ch569w-evt/board/linker_scripts/link.lds @@ -0,0 +1,199 @@ +ENTRY( _start ) + +__stack_size = 2048; + +PROVIDE( _stack_size = __stack_size ); + +MEMORY +{ + FLASH (rx) : ORIGIN = 0x00000000, LENGTH = 96K + RAM (xrw) : ORIGIN = 0x20000000, LENGTH = 16K + RAMX (xrw) : ORIGIN = 0x20020000, LENGTH = 32K +} + +SECTIONS +{ + + .init : + { + _sinit = .; + . = ALIGN(4); + KEEP(*(SORT_NONE(.init))) + . = ALIGN(4); + _einit = .; + } >FLASH AT>FLASH + + .vector : + { + *(.vector); + . = ALIGN(64); + } >FLASH AT>FLASH + + .text : + { + . = ALIGN(4); + *(.text) + *(.text.*) + *(.rodata) + *(.rodata*) + *(.glue_7) + *(.glue_7t) + *(.gnu.linkonce.t.*) + + /* section information for finsh shell */ + . = ALIGN(4); + __fsymtab_start = .; + KEEP(*(FSymTab)) + __fsymtab_end = .; + . = ALIGN(4); + __vsymtab_start = .; + KEEP(*(VSymTab)) + __vsymtab_end = .; + . = ALIGN(4); + + /* section information for initial. */ + . = ALIGN(4); + __rt_init_start = .; + KEEP(*(SORT(.rti_fn*))) + __rt_init_end = .; + . = ALIGN(4); + + /* section information for modules */ + . = ALIGN(4); + __rtmsymtab_start = .; + KEEP(*(RTMSymTab)) + __rtmsymtab_end = .; + . = ALIGN(4); + + } >FLASH AT>FLASH + + .fini : + { + KEEP(*(SORT_NONE(.fini))) + . = ALIGN(4); + } >FLASH AT>FLASH + + PROVIDE( _etext = . ); + PROVIDE( _eitcm = . ); + + .preinit_array : + { + PROVIDE_HIDDEN (__preinit_array_start = .); + KEEP (*(.preinit_array)) + PROVIDE_HIDDEN (__preinit_array_end = .); + } >FLASH AT>FLASH + + .init_array : + { + PROVIDE_HIDDEN (__init_array_start = .); + KEEP (*(SORT_BY_INIT_PRIORITY(.init_array.*) SORT_BY_INIT_PRIORITY(.ctors.*))) + KEEP (*(.init_array EXCLUDE_FILE (*crtbegin.o *crtbegin?.o *crtend.o *crtend?.o ) .ctors)) + PROVIDE_HIDDEN (__init_array_end = .); + } >FLASH AT>FLASH + + .fini_array : + { + PROVIDE_HIDDEN (__fini_array_start = .); + KEEP (*(SORT_BY_INIT_PRIORITY(.fini_array.*) SORT_BY_INIT_PRIORITY(.dtors.*))) + KEEP (*(.fini_array EXCLUDE_FILE (*crtbegin.o *crtbegin?.o *crtend.o *crtend?.o ) .dtors)) + PROVIDE_HIDDEN (__fini_array_end = .); + } >FLASH AT>FLASH + + .ctors : + { + /* gcc uses crtbegin.o to find the start of + the constructors, so we make sure it is + first. Because this is a wildcard, it + doesn't matter if the user does not + actually link against crtbegin.o; the + linker won't look for a file to match a + wildcard. The wildcard also means that it + doesn't matter which directory crtbegin.o + is in. */ + KEEP (*crtbegin.o(.ctors)) + KEEP (*crtbegin?.o(.ctors)) + /* We don't want to include the .ctor section from + the crtend.o file until after the sorted ctors. + The .ctor section from the crtend file contains the + end of ctors marker and it must be last */ + KEEP (*(EXCLUDE_FILE (*crtend.o *crtend?.o ) .ctors)) + KEEP (*(SORT(.ctors.*))) + KEEP (*(.ctors)) + } >FLASH AT>FLASH + + .dtors : + { + KEEP (*crtbegin.o(.dtors)) + KEEP (*crtbegin?.o(.dtors)) + KEEP (*(EXCLUDE_FILE (*crtend.o *crtend?.o ) .dtors)) + KEEP (*(SORT(.dtors.*))) + KEEP (*(.dtors)) + } >FLASH AT>FLASH + + .dalign : + { + . = ALIGN(4); + PROVIDE(_data_vma = .); + } >RAM AT>FLASH + + .dlalign : + { + . = ALIGN(4); + PROVIDE(_data_lma = .); + } >FLASH AT>FLASH + + .data : + { + *(.gnu.linkonce.r.*) + *(.data .data.*) + *(.gnu.linkonce.d.*) + . = ALIGN(8); + PROVIDE( __global_pointer$ = . + 0x800 ); + *(.sdata .sdata.*) + *(.sdata2.*) + *(.gnu.linkonce.s.*) + . = ALIGN(8); + *(.srodata.cst16) + *(.srodata.cst8) + *(.srodata.cst4) + *(.srodata.cst2) + *(.srodata .srodata.*) + . = ALIGN(4); + PROVIDE( _edata = .); + } >RAM AT>FLASH + + .bss : + { + . = ALIGN(4); + PROVIDE( _sbss = .); + *(.sbss*) + *(.gnu.linkonce.sb.*) + *(.bss*) + *(.gnu.linkonce.b.*) + *(COMMON*) + . = ALIGN(4); + PROVIDE( _ebss = .); + } >RAM AT>FLASH + + PROVIDE( _end = _ebss); + PROVIDE( end = . ); + + .dmadata : + { + . = ALIGN(16); + PROVIDE( _dmadata_start = .); + *(.dmadata*) + *(.dmadata.*) + . = ALIGN(16); + PROVIDE( _dmadata_end = .); + } >RAMX AT>FLASH /**/ + + .stack ORIGIN(RAM) + LENGTH(RAM) - __stack_size : + { + PROVIDE( _heap_end = . ); + . = ALIGN(4); + PROVIDE(_susrstack = . ); + . = . + __stack_size; + PROVIDE( _eusrstack = .); + } >RAM +} diff --git a/bsp/wch/risc-v/ch569w-evt/board/startup_gcc.S b/bsp/wch/risc-v/ch569w-evt/board/startup_gcc.S new file mode 100644 index 0000000000..71649eff1e --- /dev/null +++ b/bsp/wch/risc-v/ch569w-evt/board/startup_gcc.S @@ -0,0 +1,174 @@ +/********************************** (C) COPYRIGHT ******************************* +* File Name : startup_gcc.s +* Author : WCH +* Version : V1.0 +* Date : 2020/07/31 +* Description : CH56x vector table for eclipse toolchain. +*******************************************************************************/ + + .section .init,"ax",@progbits + .global _start + .align 1 +_start: + j handle_reset + + .section .vector,"ax",@progbits + .align 1 +_vector_base: + .option norvc; + .word 0 + .word 0 + j nmi_handler + j hardfault_handler + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + j systick_handler + .word 0 + j swi_handler + .word 0 + /* External Interrupts */ + j wdog_irq_handler + j tmr0_irq_handler + j gpio_irq_handler + j spi0_irq_handler + j usbss_irq_handler + j link_irq_handler + j tmr1_irq_handler + j tmr2_irq_handler + j uart0_irq_handler + j usbhs_irq_handler + j emmc_irq_handler + j dvp_irq_handler + j hspi_irq_handler + j spi1_irq_handler + j uart1_irq_handler + j uart2_irq_handler + j uart3_irq_handler + j serdes_irq_handler + j eth_irq_handler + j pmt_irq_handler + j ecdc_irq_handler + + .option rvc; + + .section .text.vector_handler,"ax", @prsocogbits + + .weak nmi_handler + .weak hardfault_handler + .weak systick_handler + .weak swi_handler + .weak wdog_irq_handler + .weak tmr0_irq_handler + .weak gpio_irq_handler + .weak spi0_irq_handler + .weak usbss_irq_handler + .weak link_irq_handler + .weak tmr1_irq_handler + .weak tmr2_irq_handler + .weak uart0_irq_handler + .weak usbhs_irq_handler + .weak emmc_irq_handler + .weak dvp_irq_handler + .weak hspi_irq_handler + .weak spi1_irq_handler + .weak uart1_irq_handler + .weak uart2_irq_handler + .weak uart3_irq_handler + .weak serdes_irq_handler + .weak eth_irq_handler + .weak pmt_irq_handler + .weak ecdc_irq_handler + +nmi_handler: j .L_rip +hardfault_handler: j .L_rip +systick_handler: j .L_rip +swi_handler: j .L_rip +wdog_irq_handler: j .L_rip +tmr0_irq_handler: j .L_rip +gpio_irq_handler: j .L_rip +spi0_irq_handler: j .L_rip +usbss_irq_handler: j .L_rip +link_irq_handler: j .L_rip +tmr1_irq_handler: j .L_rip +tmr2_irq_handler: j .L_rip +uart0_irq_handler: j .L_rip +usbhs_irq_handler: j .L_rip +emmc_irq_handler: j .L_rip +dvp_irq_handler: j .L_rip +hspi_irq_handler: j .L_rip +spi1_irq_handler: j .L_rip +uart1_irq_handler: j .L_rip +uart2_irq_handler: j .L_rip +uart3_irq_handler: j .L_rip +serdes_irq_handler: j .L_rip +eth_irq_handler: j .L_rip +pmt_irq_handler: j .L_rip +ecdc_irq_handler: j .L_rip + +.L_rip: + csrr t0, mepc + csrr t1, mstatus + csrr t2, mcause + csrr t3, mtval + csrr t4, mscratch +1: j 1b + + .section .text.handle_reset,"ax",@progbits + .weak handle_reset + .align 1 +handle_reset: + .option push + .option norelax + la gp, __global_pointer$ + .option pop +1: + la sp, _eusrstack + +/* Load data section from flash to RAM */ +2: + la a0, _data_lma + la a1, _data_vma + la a2, _edata + bgeu a1, a2, 2f +1: + lw t0, (a0) + sw t0, (a1) + addi a0, a0, 4 + addi a1, a1, 4 + bltu a1, a2, 1b +/* clear bss section */ +2: + la a0, _sbss + la a1, _ebss + bgeu a0, a1, 2f +1: + sw zero, (a0) + addi a0, a0, 4 + bltu a0, a1, 1b + +/* clear dmadata section */ +2: + la a0, _dmadata_start + la a1, _dmadata_end + bgeu a0, a1, 2f +1: + sw zero, (a0) + addi a0, a0, 4 + bltu a0, a1, 1b + +2: + /* leave all interrupt disabled */ + li t0, 0x1800 + csrs mstatus, t0 + la t0, _vector_base + ori t0, t0, 1 + csrw mtvec, t0 + la t0, entry + csrw mepc, t0 + mret diff --git a/bsp/wch/risc-v/ch569w-evt/figures/ch569w-evt.jpeg b/bsp/wch/risc-v/ch569w-evt/figures/ch569w-evt.jpeg new file mode 100644 index 0000000000000000000000000000000000000000..74e645b708aa25bb55d8f0232bf37c24284c24f6 GIT binary patch literal 114930 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≫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 literal 0 HcmV?d00001 diff --git a/bsp/wch/risc-v/ch569w-evt/rtconfig.h b/bsp/wch/risc-v/ch569w-evt/rtconfig.h new file mode 100644 index 0000000000..99bad3ce80 --- /dev/null +++ b/bsp/wch/risc-v/ch569w-evt/rtconfig.h @@ -0,0 +1,192 @@ +#ifndef RT_CONFIG_H__ +#define RT_CONFIG_H__ + +/* Automatically generated file; DO NOT EDIT. */ +/* RT-Thread Configuration */ + +/* RT-Thread Kernel */ + +#define RT_NAME_MAX 8 +#define RT_ALIGN_SIZE 4 +#define RT_THREAD_PRIORITY_32 +#define RT_THREAD_PRIORITY_MAX 32 +#define RT_TICK_PER_SECOND 200 +#define RT_USING_HOOK +#define RT_HOOK_USING_FUNC_PTR +#define RT_USING_IDLE_HOOK +#define RT_IDLE_HOOK_LIST_SIZE 4 +#define IDLE_THREAD_STACK_SIZE 384 + +/* kservice optimization */ + +#define RT_KSERVICE_USING_STDLIB + +/* Inter-Thread communication */ + +#define RT_USING_SEMAPHORE +#define RT_USING_MUTEX +#define RT_USING_MAILBOX + +/* Memory Management */ + +#define RT_USING_MEMPOOL +#define RT_USING_SMALL_MEM +#define RT_USING_SMALL_MEM_AS_HEAP +#define RT_USING_HEAP + +/* Kernel Device Object */ + +#define RT_USING_DEVICE +#define RT_USING_INTERRUPT_INFO +#define RT_USING_CONSOLE +#define RT_CONSOLEBUF_SIZE 128 +#define RT_CONSOLE_DEVICE_NAME "uart1" +#define RT_VER_NUM 0x40101 +#define ARCH_RISCV + +/* RT-Thread Components */ + +#define RT_USING_COMPONENTS_INIT +#define RT_USING_USER_MAIN +#define RT_MAIN_THREAD_STACK_SIZE 2048 +#define RT_MAIN_THREAD_PRIORITY 10 +#define RT_USING_MSH +#define RT_USING_FINSH +#define FINSH_USING_MSH +#define FINSH_THREAD_NAME "tshell" +#define FINSH_THREAD_PRIORITY 20 +#define FINSH_THREAD_STACK_SIZE 4096 +#define FINSH_USING_HISTORY +#define FINSH_HISTORY_LINES 5 +#define FINSH_USING_SYMTAB +#define FINSH_CMD_SIZE 80 +#define MSH_USING_BUILT_IN_COMMANDS +#define FINSH_USING_DESCRIPTION +#define FINSH_ARG_MAX 10 + +/* Device Drivers */ + +#define RT_USING_DEVICE_IPC +#define RT_USING_SERIAL +#define RT_USING_SERIAL_V1 +#define RT_SERIAL_RB_BUFSZ 64 +#define RT_USING_HWTIMER +#define RT_USING_PIN +#define RT_USING_WDT + +/* Using USB */ + + +/* C/C++ and POSIX layer */ + +#define RT_LIBC_DEFAULT_TIMEZONE 8 + +/* POSIX (Portable Operating System Interface) layer */ + + +/* Interprocess Communication (IPC) */ + + +/* Socket is in the 'Network' category */ + + +/* Network */ + + +/* Utilities */ + + +/* RT-Thread Utestcases */ + + +/* RT-Thread online packages */ + +/* IoT - internet of things */ + + +/* Wi-Fi */ + +/* Marvell WiFi */ + + +/* Wiced WiFi */ + + +/* IoT Cloud */ + + +/* security packages */ + + +/* language packages */ + +/* JSON: JavaScript Object Notation, a lightweight data-interchange format */ + + +/* XML: Extensible Markup Language */ + + +/* multimedia packages */ + +/* LVGL: powerful and easy-to-use embedded GUI library */ + + +/* u8g2: a monochrome graphic library */ + + +/* PainterEngine: A cross-platform graphics application framework written in C language */ + + +/* tools packages */ + + +/* system packages */ + +/* enhanced kernel services */ + + +/* acceleration: Assembly language or algorithmic acceleration packages */ + + +/* CMSIS: ARM Cortex-M Microcontroller Software Interface Standard */ + + +/* Micrium: Micrium software products porting for RT-Thread */ + + +/* peripheral libraries and drivers */ + + +/* AI packages */ + + +/* miscellaneous packages */ + +/* project laboratory */ + +/* samples: kernel and components samples */ + + +/* entertainment: terminal games and other interesting software packages */ + +#define SOC_FAMILY_CH56X +#define SOC_SERIES_CH569 + +/* Hardware Drivers Config */ + +#define SOC_CH569W + +/* On-chip Peripheral Drivers */ + +#define BSP_USING_UART +#define BSP_USING_UART1 +#define BSP_USING_TIMER +#define BSP_USING_TMR0 +#define BSP_USING_TMR1 + +/* Onboard Peripheral Drivers */ + +/* Board extended module Drivers */ + + +#endif diff --git a/bsp/wch/risc-v/ch569w-evt/rtconfig.py b/bsp/wch/risc-v/ch569w-evt/rtconfig.py new file mode 100644 index 0000000000..d6e291657a --- /dev/null +++ b/bsp/wch/risc-v/ch569w-evt/rtconfig.py @@ -0,0 +1,62 @@ +import os +ARCH = 'risc-v' +CPU = 'ch56x' +# toolchains options +CROSS_TOOL = 'gcc' + +#------- toolchains path ------------------------------------------------------- +if os.getenv('RTT_CC'): + CROSS_TOOL = os.getenv('RTT_CC') + +if CROSS_TOOL == 'gcc': + PLATFORM = 'gcc' + EXEC_PATH = r'/opt/mrs-riscv-none-embed/bin' +else: + print('Please make sure your toolchains is GNU GCC!') + exit(0) + +if os.getenv('RTT_EXEC_PATH'): + EXEC_PATH = os.getenv('RTT_EXEC_PATH') + +#BUILD = 'debug' +BUILD = 'release' + +CORE = 'risc-v' +MAP_FILE = 'rtthread.map' +LINK_FILE = './board/linker_scripts/link.lds' +TARGET_NAME = 'rtthread.bin' + +#------- GCC settings ---------------------------------------------------------- +if PLATFORM == 'gcc': + # toolchains + PREFIX = 'riscv-none-embed-' + CC = PREFIX + 'gcc' + CXX= PREFIX + 'g++' + AS = PREFIX + 'gcc' + AR = PREFIX + 'ar' + LINK = PREFIX + 'gcc' + TARGET_EXT = 'elf' + SIZE = PREFIX + 'size' + OBJDUMP = PREFIX + 'objdump' + OBJCPY = PREFIX + 'objcopy' + + DEVICE = ' -march=rv32imac -mabi=ilp32 -DUSE_PLIC -DUSE_M_TIME -DNO_INIT -mcmodel=medany -msmall-data-limit=8 -L. -nostartfiles -lc ' + CFLAGS = DEVICE + CFLAGS += ' -save-temps=obj' + AFLAGS = '-c'+ DEVICE + ' -x assembler-with-cpp' + LFLAGS = DEVICE + LFLAGS += ' -Wl,--gc-sections,-cref,-Map=' + MAP_FILE + LFLAGS += ' -T ' + LINK_FILE + LFLAGS += ' -Wl,-wrap=memset' + + CPATH = '' + LPATH = '' + + if BUILD == 'debug': + CFLAGS += ' -O0 -g3' + AFLAGS += ' -g3' + else: + CFLAGS += ' -O2' + + POST_ACTION = OBJCPY + ' -O binary $TARGET ' + TARGET_NAME + '\n' + POST_ACTION += SIZE + ' $TARGET\n' diff --git a/libcpu/risc-v/SConscript b/libcpu/risc-v/SConscript index ffb2e7ae17..7e6b9947a3 100644 --- a/libcpu/risc-v/SConscript +++ b/libcpu/risc-v/SConscript @@ -24,7 +24,7 @@ else : # cpu porting code files if rtconfig.CPU == "e9xx" : group = group + SConscript(os.path.join(rtconfig.VENDOR, rtconfig.CPU, 'SConscript')) -else : +elif rtconfig.CPU in list: group = group + SConscript(os.path.join(rtconfig.CPU, 'SConscript')) Return('group') diff --git a/libcpu/risc-v/common/cpuport.c b/libcpu/risc-v/common/cpuport.c index 97f34d8c4c..9e9957e66c 100644 --- a/libcpu/risc-v/common/cpuport.c +++ b/libcpu/risc-v/common/cpuport.c @@ -138,7 +138,7 @@ rt_uint8_t *rt_hw_stack_init(void *tentry, * #endif */ #ifndef RT_USING_SMP -void rt_hw_context_switch_interrupt(rt_ubase_t from, rt_ubase_t to) +RT_WEAK void rt_hw_context_switch_interrupt(rt_ubase_t from, rt_ubase_t to) { if (rt_thread_switch_interrupt_flag == 0) rt_interrupt_from_thread = from;