diff --git a/COPYING b/COPYING index a4f46800..1c228d7f 100644 --- a/COPYING +++ b/COPYING @@ -269,30 +269,23 @@ arch/cortex-m23/m2351/src/CMSIS/Include/ * limitations under the License. */ -Please keep in mind that after downloading source code of std driver -for M2351 Nuvoton chip the two copyright notices given below become -valid. - -./arch/cortex-m23/m2351/src/NuBL2/main.c -./arch/cortex-m23/m2351/src/NuBL2/VerifyNuBL3x.c -./arch/cortex-m23/m2351/src/NuBL2/NuBL2.h -./arch/cortex-m23/m2351/src/NuBL2/FwInfo.c -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -/** - * Copyright (C) 2017 Nuvoton Technology Corp. All rights reserved. - */ - +arch/cortex-m23/m2351/src/NuBL2/main.c +arch/cortex-m23/m2351/src/NuBL2/VerifyNuBL3x.c +arch/cortex-m23/m2351/src/NuBL2/NuBL2.h +arch/cortex-m23/m2351/src/NuBL2/FwInfo.c arch/cortex-m23/m2351/src/Device/ arch/cortex-m23/m2351/src/StdDriver/ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +/* * SPDX-License-Identifier: Apache-2.0 * @copyright (C) 2016-2020 Nuvoton Technology Corp. All rights reserved. + */ freertos/ ^^^^^^^^ - /* - * FreeRTOS Kernel V10.1.1 - * Copyright (C) 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved. +/* + * FreeRTOS Kernel V10.2.1 + * Copyright (C) 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Permission is hereby granted, free of charge, to any person obtaining a copy of * this software and associated documentation files (the "Software"), to deal in diff --git a/Makefile b/Makefile index 671b0214..c89507db 100644 --- a/Makefile +++ b/Makefile @@ -120,7 +120,7 @@ $(TOPDIR)/toolchain/$(GCC_SOURCE): gcc-unpacked: $(TOPDIR)/toolchain/$(GCC_SOURCE) $(Q) if [ ! -d $(TOOLCHAINPATH) ]; then \ - tar xjf $(TOPDIR)/toolchain/$(GCC_SOURCE) -C $(TOPDIR)/toolchain ; \ + tar xvf $(TOPDIR)/toolchain/$(GCC_SOURCE) -C $(TOPDIR)/toolchain ; \ fi toolchain: check_context Make.defs .config $(TOPDIR)/toolchain/$(GCC_SOURCE) gcc-unpacked diff --git a/README.md b/README.md index bd3ee4fd..d1f703d9 100644 --- a/README.md +++ b/README.md @@ -34,19 +34,22 @@ the beginnings of corresponding files; also, all licenses are listed in --- ## 3. Platforms supported -| **NuMaker-PFM-M2351** | **M2351-Badge** | **V2M-MPS2-Qemu** | **How to add a platform** | +| **NuMaker-PFM-M2351** | **M2351-Badge** | **V2M-MPS2-Qemu** | **RISCV SparkFun RedBoard** | |-----------------------|---------------------------|-------------|-----------| -|[![](docs/images/platforms/numaker_pfm_m2351/numaker_pfm_m2351.png)](docs/numaker_pfm_m2351.md)|[![](docs/images/platforms/m2351_badge/m2351_badge.png)](docs/m2351_badge.md) |[![V2M-MPS2-Qemu](docs/images/platforms/v2m-mps2/v2m-mps2.png)](docs/v2m-mps2-qemu.md)|[![](docs/images/platforms/add_new_board.jpg)](docs/port-new-platform.md)| +|[![](docs/images/platforms/numaker_pfm_m2351/numaker_pfm_m2351.png)](docs/numaker_pfm_m2351.md)|[![](docs/images/platforms/m2351_badge/m2351_badge.png)](docs/m2351_badge.md) |[![V2M-MPS2-Qemu](docs/images/platforms/v2m-mps2/v2m-mps2.png)](docs/v2m-mps2-qemu.md)|[![](docs/images/platforms/sparkfun_redboard/sparkfun_redboard.png)](docs/sparkfun_redboard.md)| +| **How to add a platform** |||| +|[![](docs/images/platforms/add_new_board.jpg)](docs/port-new-platform.md)|||| Several platforms are supported. In order to manage slight differences between platforms, a `PLATFORM` flag has been introduced. -| Platform | Composite PLATFORM flag | Maintained | -|-----------------------|------------------------------------|------------| -| [NuMaker-PFM-M2351] |`PLATFORM=numaker_pfm_m2351` | v0.4 | -| [M2351-Badge] |`PLATFORM=m2351_badge` | v0.4 | -| [V2M-MPS2-Qemu] |`PLATFORM=mps2_an505_qemu` | v0.4 | +| Platform | Composite PLATFORM flag | Maintained | +|---------------------------|------------------------------------|------------| +| [NuMaker-PFM-M2351] |`PLATFORM=numaker_pfm_m2351` | v0.4 | +| [M2351-Badge] |`PLATFORM=m2351_badge` | v0.4 | +| [V2M-MPS2-Qemu] |`PLATFORM=mps2_an505_qemu` | v0.4 | +| [SparkFun RED-V RedBoard] |`PLATFORM=sparkfun_redboard` | v0.5 | For information on adding a new platform see the [how to add a platform]. @@ -98,3 +101,4 @@ you can find here](.github/CONTRIBUTING.md). [NuMaker-PFM-M2351]: http://www.nuvoton.com.cn/hq/products/iot-solution/iot-platform/numaker-maker-platform/numaker-pfm-m2351?__locale=en [M2351-Badge]: docs/schemes/m2351_badge [V2M-MPS2-Qemu]: https://developer.arm.com/documentation/100964/1114/Microcontroller-Prototyping-System-2?lang=en +[SparkFun RED-V RedBoard]: https://www.sparkfun.com/products/15594 diff --git a/arch/Kconfig b/arch/Kconfig index 79a3a628..13d31004 100644 --- a/arch/Kconfig +++ b/arch/Kconfig @@ -17,12 +17,18 @@ config ARCH_CORTEX_M33 ---help--- The ARM architecture +config ARCH_RISCV_32 + bool "riscv32" + ---help--- + The ARM architecture + endchoice config ARCH string default "cortex-m23" if ARCH_CORTEX_M23 default "cortex-m33" if ARCH_CORTEX_M33 + default "riscv32" if ARCH_RISCV_32 if ARCH_CORTEX_M23 source arch/cortex-m23/Kconfig @@ -32,6 +38,10 @@ if ARCH_CORTEX_M33 source arch/cortex-m33/Kconfig endif +if ARCH_RISCV_32 +source arch/riscv32/Kconfig +endif + comment "Toolchain Configuration Options" choice prompt "Toolchain Selection" @@ -53,6 +63,12 @@ config GCC_VERSION_8_2018Q4 # depends on ARCH_FAMILY_MPS2 # bool "GCC compiler Version: 8 2019 q3" +config GCC_VERSION_10_2_0_2020_12 + bool "GCC compiler Version: SiFive GCC-Metal 10.2.0-2020.12.8" + +config GCC_VERSION_T_HEAD_10_2_0 + bool "GCC compiler Version: T-Head GCC 10.2.0" + endchoice config GCC_VERSION @@ -61,6 +77,7 @@ config GCC_VERSION default arm-none-eabi-6-2017-q2-update-linux if GCC_VERSION_6_1_2017Q2 default arm-none-eabi-8-2018-q4-major-linux if GCC_VERSION_8_2018Q4 # default gcc-arm-none-eabi-8-2019-q3-update-linux if GCC_VERSION_8_2019Q3 + default riscv64-unknown-elf-toolchain-10.2.0-2020.12.8-x86_64-linux-ubuntu14 if GCC_VERSION_10_2_0_2020_12 config GCC_SITE string @@ -70,6 +87,8 @@ config GCC_SITE # default "https://developer.arm.com/-/media/Files/downloads/gnu-rm/8-2019q3" if GCC_VERSION_8_2019Q3 # default "https://developer.arm.com/-/media/Files/downloads/gnu-rm/8-2019q3/RC1.1" if GCC_VERSION_8_2019Q3 # https://developer.arm.com/-/media/Files/downloads/gnu-rm/8-2019q3/RC1.1/gcc-arm-none-eabi-8-2019-q3-update-linux.tar.bz2?revision=c34d758a-be0c-476e-a2de-af8c6e16a8a2?product=GNU%20Arm%20Embedded%20Toolchain%20Downloads,64-bit,,Linux,8-2019-q3-update + default "https://static.dev.sifive.com/dev-tools/freedom-tools/v2020.12" if GCC_VERSION_10_2_0_2020_12 + default "https://github.com/bouffalolab/toolchain_gcc_t-head_linux" if GCC_VERSION_T_HEAD_10_2_0 config GCC_FOLDER string @@ -77,4 +96,5 @@ config GCC_FOLDER default "gcc-arm-none-eabi-6-2017-q2-update" if GCC_VERSION_6_1_2017Q2 default "gcc-arm-none-eabi-8-2018-q4-major" if GCC_VERSION_8_2018Q4 # default "gcc-arm-none-eabi-8-2019-q3-update" if GCC_VERSION_8_2019Q3 - + default "riscv64-unknown-elf-toolchain-10.2.0-2020.12.8-x86_64-linux-ubuntu14" if GCC_VERSION_10_2_0_2020_12 + default "toolchain_gcc_t-head_linux-master" if GCC_VERSION_T_HEAD_10_2_0 diff --git a/arch/cortex-m23/m2351/src/m2351_badge/nonsecure/Makefile b/arch/cortex-m23/m2351/src/m2351_badge/nonsecure/Makefile index 68dc07fa..b1f44d08 100644 --- a/arch/cortex-m23/m2351/src/m2351_badge/nonsecure/Makefile +++ b/arch/cortex-m23/m2351/src/m2351_badge/nonsecure/Makefile @@ -77,7 +77,7 @@ $(COBJS_NS): $(OBJDIR)/%$(OBJEXT): %.c $(Q) $(CC) -c $(CFLAGS) $< -o $@ mtower_ns$(EXEEXT): $(OBJS_NS) - $(Q) $(MAKE) -C $(TOPDIR)/freertos/ TOPDIR="$(TOPDIR)" libFreeRTOS_ns.a + $(Q) $(MAKE) -C $(TOPDIR)/freertos/ TOPDIR="$(TOPDIR)" FREE_RTOS_ARCH=ARM_V8M libFreeRTOS_ns.a $(Q) $(MAKE) -C ../../StdDriver/src/ TOPDIR="$(TOPDIR)" libm2351_StdDriver_ns.a @echo "LD: mTower_ns$(EXEEXT)" $(Q) $(CC) $(CFLAGS) -Wl,--section-start=.text=$(CONFIG_START_ADDRESS_BL33) -Tnonsecure.ld $(OBJS_NS) $(LIBPATHS) $(LIBS) -o $(OBJDIR)/bl33.elf diff --git a/arch/cortex-m23/m2351/src/numaker_pfm_m2351/nonsecure/Makefile b/arch/cortex-m23/m2351/src/numaker_pfm_m2351/nonsecure/Makefile index f136afd8..e022fe35 100644 --- a/arch/cortex-m23/m2351/src/numaker_pfm_m2351/nonsecure/Makefile +++ b/arch/cortex-m23/m2351/src/numaker_pfm_m2351/nonsecure/Makefile @@ -77,7 +77,7 @@ $(COBJS_NS): $(OBJDIR)/%$(OBJEXT): %.c $(Q) $(CC) -c $(CFLAGS) $< -o $@ mtower_ns$(EXEEXT): $(OBJS_NS) - $(Q) $(MAKE) -C $(TOPDIR)/freertos/ TOPDIR="$(TOPDIR)" libFreeRTOS_ns.a + $(Q) $(MAKE) -C $(TOPDIR)/freertos/ TOPDIR="$(TOPDIR)" FREE_RTOS_ARCH=ARM_V8M libFreeRTOS_ns.a $(Q) $(MAKE) -C ../../StdDriver/src/ TOPDIR="$(TOPDIR)" libm2351_StdDriver_ns.a @echo "LD: mTower_ns$(EXEEXT)" $(Q) $(CC) $(CFLAGS) -Wl,--section-start=.text=$(CONFIG_START_ADDRESS_BL33) -Tnonsecure.ld $(OBJS_NS) $(LIBPATHS) $(LIBS) -o $(OBJDIR)/bl33.elf diff --git a/arch/cortex-m33/mps2/src/mps2_an505_qemu/nonsecure/Makefile b/arch/cortex-m33/mps2/src/mps2_an505_qemu/nonsecure/Makefile index ba244521..3d610630 100644 --- a/arch/cortex-m33/mps2/src/mps2_an505_qemu/nonsecure/Makefile +++ b/arch/cortex-m33/mps2/src/mps2_an505_qemu/nonsecure/Makefile @@ -75,7 +75,6 @@ OBJS_NS = $(AOBJS_NS) $(COBJS_NS) LIBPATHS = -L. -L"$(TOPDIR)/lib" -# LIBS = -lm -lc -lnosys -lStdDriver_ns -lnsc -lFreeRTOS_ns LIBS = -lm -lc -lnosys -lnsc -lFreeRTOS_ns $(AOBJS_NS): $(OBJDIR)/%$(OBJEXT): %.S @@ -90,7 +89,7 @@ $(COBJS_NS): $(OBJDIR)/%$(OBJEXT): %.c $(Q) $(CC) -c $(CFLAGS) $< -o $@ mtower_ns$(EXEEXT): $(OBJS_NS) - $(Q) $(MAKE) -C $(TOPDIR)/freertos/ TOPDIR="$(TOPDIR)" libFreeRTOS_ns.a + $(Q) $(MAKE) -C $(TOPDIR)/freertos/ TOPDIR="$(TOPDIR)" FREE_RTOS_ARCH=ARM_V8M libFreeRTOS_ns.a # $(Q) $(MAKE) -C ../../StdDriver/src/ TOPDIR="$(TOPDIR)" libStdDriver_ns.a @echo "LD: mTower_ns$(EXEEXT)" $(Q) $(CC) $(CFLAGS) -Tnonsecure.ld $(OBJS_NS) $(LIBPATHS) $(LIBS) -Wl,-Map=output.map -o $(OBJDIR)/bl33.elf diff --git a/arch/riscv32/Kconfig b/arch/riscv32/Kconfig new file mode 100644 index 00000000..5966ff7b --- /dev/null +++ b/arch/riscv32/Kconfig @@ -0,0 +1,36 @@ +# +# For a description of the syntax of this configuration file, +# see the file kconfig-language.txt in the docs folder of mTower repository. +# + +choice + prompt "MCU Family" + default FE310 + +config ARCH_FAMILY_FE310 + bool "FE310" + ---help--- + The FE310 architecture + +config ARCH_FAMILY_BL808 + bool "BL808" + ---help--- + The BL808 architecture + +endchoice + +config ARCH_FAMILY + string + default "fe310" if ARCH_FAMILY_FE310 + default "bl808" if ARCH_FAMILY_BL808 + +if ARCH_FAMILY_FE310 +comment "FE310 Configuration Options" +source arch/riscv32/fe310/Kconfig +endif + +if ARCH_FAMILY_BL808 +comment "BL808 Configuration Options" +source arch/riscv32/bl808/Kconfig +endif + \ No newline at end of file diff --git a/arch/riscv32/fe310/Kconfig b/arch/riscv32/fe310/Kconfig new file mode 100644 index 00000000..188955dd --- /dev/null +++ b/arch/riscv32/fe310/Kconfig @@ -0,0 +1,2481 @@ +# +# For a description of the syntax of this configuration file, +# see the file kconfig-language.txt in the docs folder of mTower repository. +# + +choice + prompt "FE310 platform selection" + default PLATFORM_SPARKFUN_RED_BOARD + +config PLATFORM_SPARKFUN_RED_BOARD + bool "Sparkfun RED board" + ---help--- + Sparkfun RED board. + +endchoice + +config PLATFORM + string + default "sparkfun_redboard" if PLATFORM_SPARKFUN_RED_BOARD + + +if PLATFORM_SPARKFUN_RED_BOARD +#comment "Sparkfun RED board Peripheral Configuration Options" +# source arch/riscv32/fe310/src/sparkfun_redboard/Kconfig +endif # PLATFORM_SPARKFUN_RED_BOARD + +choice + prompt "Non-secure debug UART configuration" + default NONSECURE_DEBUG_UART1 + +config NONSECURE_DEBUG_UART0 + bool "UART0" + ---help--- + UART0 is non-secure debug port. + +config NONSECURE_DEBUG_UART1 + bool "UART1" + ---help--- + UART1 is non-secure debug port. + +config NONSECURE_DEBUG_UART2 + bool "UART2" + ---help--- + UART2 is non-secure debug port. + +config NONSECURE_DEBUG_UART3 + bool "UART3" + ---help--- + UART3 is non-secure debug port. + +config NONSECURE_DEBUG_UART4 + bool "UART4" + ---help--- + UART4 is non-secure debug port. + +config NONSECURE_DEBUG_UART5 + bool "UART5" + ---help--- + UART5 is non-secure debug port. +endchoice + +config NONSECURE_DEBUG_UART + string + default "UART0_NS" if NONSECURE_DEBUG_UART0 + default "UART1_NS" if NONSECURE_DEBUG_UART1 + default "UART2_NS" if NONSECURE_DEBUG_UART2 + default "UART3_NS" if NONSECURE_DEBUG_UART3 + default "UART4_NS" if NONSECURE_DEBUG_UART4 + default "UART5_NS" if NONSECURE_DEBUG_UART5 + +choice + prompt "Non-secure debug font color configuration" + default NONSECURE_DEBUG_RED + +config NONSECURE_FONT_RED + bool "RED" + ---help--- + Red is non-secure debug color. + +config NONSECURE_FONT_GREEN + bool "GREEN" + ---help--- + Green is non-secure debug color. + +config NONSECURE_FONT_YELLOW + bool "YELLOW" + ---help--- + Yellow is non-secure debug color. + +config NONSECURE_FONT_BLUE + bool "BLUE" + ---help--- + Blue is non-secure debug color. + +config NONSECURE_FONT_GRAY + bool "GRAY" + ---help--- + Gray is non-secure debug color. +endchoice + +config NONSECURE_FONT_COLOR + int + default 1 if NONSECURE_FONT_RED + default 2 if NONSECURE_FONT_GREEN + default 3 if NONSECURE_FONT_YELLOW + default 4 if NONSECURE_FONT_BLUE + default 7 if NONSECURE_FONT_GRAY + +choice + prompt "Secure debug UART configuration" + default SECURE_DEBUG_UART0 + +config SECURE_DEBUG_UART0 + bool "UART0" + ---help--- + UART0 is secure debug port. + +config SECURE_DEBUG_UART1 + bool "UART1" + ---help--- + UART1 is secure debug port. + +config SECURE_DEBUG_UART2 + bool "UART2" + ---help--- + UART2 is secure debug port. + +config SECURE_DEBUG_UART3 + bool "UART3" + ---help--- + UART3 is secure debug port. + +config SECURE_DEBUG_UART4 + bool "UART4" + ---help--- + UART4 is secure debug port. + +config SECURE_DEBUG_UART5 + bool "UART5" + ---help--- + UART5 is secure debug port. +endchoice + +config SECURE_DEBUG_UART + string + default "UART0" if SECURE_DEBUG_UART0 && !NONSECURE_DEBUG_UART0 + default "UART1" if SECURE_DEBUG_UART1 && !NONSECURE_DEBUG_UART1 + default "UART2" if SECURE_DEBUG_UART2 && !NONSECURE_DEBUG_UART2 + default "UART3" if SECURE_DEBUG_UART3 && !NONSECURE_DEBUG_UART3 + default "UART4" if SECURE_DEBUG_UART4 && !NONSECURE_DEBUG_UART4 + default "UART5" if SECURE_DEBUG_UART5 && !NONSECURE_DEBUG_UART5 + default "UART0_NS" if SECURE_DEBUG_UART0 && NONSECURE_DEBUG_UART0 + default "UART1_NS" if SECURE_DEBUG_UART1 && NONSECURE_DEBUG_UART1 + default "UART2_NS" if SECURE_DEBUG_UART2 && NONSECURE_DEBUG_UART2 + default "UART3_NS" if SECURE_DEBUG_UART3 && NONSECURE_DEBUG_UART3 + default "UART4_NS" if SECURE_DEBUG_UART4 && NONSECURE_DEBUG_UART4 + default "UART5_NS" if SECURE_DEBUG_UART5 && NONSECURE_DEBUG_UART5 + +choice + prompt "Secure debug font color configuration" + default SECURE_DEBUG_RED + +config SECURE_FONT_RED + bool "RED" + ---help--- + Red is secure debug color. + +config SECURE_FONT_GREEN + bool "GREEN" + ---help--- + Green is secure debug color. + +config SECURE_FONT_YELLOW + bool "YELLOW" + ---help--- + Yellow is secure debug color. + +config SECURE_FONT_BLUE + bool "BLUE" + ---help--- + Blue is secure debug color. + +config SECURE_FONT_GRAY + bool "GRAY" + ---help--- + Gray is secure debug color. +endchoice + +config SECURE_FONT_COLOR + int + default 1 if SECURE_FONT_RED + default 2 if SECURE_FONT_GREEN + default 3 if SECURE_FONT_YELLOW + default 4 if SECURE_FONT_BLUE + default 7 if NONSECURE_FONT_GRAY + +menu "Secure Attribution Configuration" + +menu "GPIO Secure Attribution Configuration" + +choice + prompt "Port A secure configuration" + default GPIO_SECURE_PA + +config GPIO_NONSECURE_PA + bool "PA non-secure" + ---help--- + GPIO PA is non-secure. + +config GPIO_SECURE_PA + bool "PA secure" + ---help--- + GPIO PA is secure. +endchoice + +choice + prompt "Port B secure configuration" + default GPIO_SECURE_PB + +config GPIO_NONSECURE_PB + bool "PB non-secure" + ---help--- + GPIO PB is non-secure. + +config GPIO_SECURE_PB + bool "PB secure" + ---help--- + GPIO PB is secure. +endchoice + +choice + prompt "Port C secure configuration" + default GPIO_SECURE_PC + +config GPIO_NONSECURE_PC + bool "PC non-secure" + ---help--- + GPIO PC is non-secure. + +config GPIO_SECURE_PC + bool "PC secure" + ---help--- + GPIO PC is secure. +endchoice + +choice + prompt "Port D secure configuration" + default GPIO_SECURE_PD + +config GPIO_NONSECURE_PD + bool "PD non-secure" + ---help--- + GPIO PD is non-secure. + +config GPIO_SECURE_PD + bool "PD secure" + ---help--- + GPIO PD is secure. +endchoice + +choice + prompt "Port E secure configuration" + default GPIO_SECURE_PE + +config GPIO_NONSECURE_PE + bool "PE non-secure" + ---help--- + GPIO PE is non-secure. + +config GPIO_SECURE_PE + bool "PE secure" + ---help--- + GPIO PE is secure. +endchoice + +choice + prompt "Port F secure configuration" + default GPIO_SECURE_PF + +config GPIO_NONSECURE_PF + bool "PF non-secure" + ---help--- + GPIO PF is non-secure. + +config GPIO_SECURE_PF + bool "PF secure" + ---help--- + GPIO PF is secure. +endchoice + +choice + prompt "Port G secure configuration" + default GPIO_SECURE_PG + +config GPIO_NONSECURE_PG + bool "PG non-secure" + ---help--- + GPIO PG is non-secure. + +config GPIO_SECURE_PG + bool "PG secure" + ---help--- + GPIO PG is secure. +endchoice + +choice + prompt "Port H secure configuration" + default GPIO_SECURE_PH + +config GPIO_NONSECURE_PH + bool "PH non-secure" + ---help--- + GPIO PH is non-secure. + +config GPIO_SECURE_PH + bool "PH secure" + ---help--- + GPIO PH is secure. +endchoice + +endmenu # GPIO Secure Attribution Configuration + +choice + prompt "Secure SRAM size" + default SCU_SECURE_SRAM_32 + +config SCU_SECURE_SRAM_0 + bool "Secure SRAM 0KB" +config SCU_SECURE_SRAM_8 + bool "Secure SRAM 8KB" +config SCU_SECURE_SRAM_16 + bool "Secure SRAM 16KB" +config SCU_SECURE_SRAM_24 + bool "Secure SRAM 24KB" +config SCU_SECURE_SRAM_32 + bool "Secure SRAM 32KB" +config SCU_SECURE_SRAM_40 + bool "Secure SRAM 40KB" +config SCU_SECURE_SRAM_48 + bool "Secure SRAM 48KB" +config SCU_SECURE_SRAM_56 + bool "Secure SRAM 56KB" +config SCU_SECURE_SRAM_64 + bool "Secure SRAM 64KB" +config SCU_SECURE_SRAM_72 + bool "Secure SRAM 72KB" +config SCU_SECURE_SRAM_80 + bool "Secure SRAM 80KB" +config SCU_SECURE_SRAM_88 + bool "Secure SRAM 88KB" +config SCU_SECURE_SRAM_96 + bool "Secure SRAM 96KB" +endchoice + +config SCU_SECURE_SRAM_SIZE + hex + default 0x0000 if SCU_SECURE_SRAM_0 + default 0x2000 if SCU_SECURE_SRAM_8 + default 0x4000 if SCU_SECURE_SRAM_16 + default 0x6000 if SCU_SECURE_SRAM_24 + default 0x8000 if SCU_SECURE_SRAM_32 + default 0xA000 if SCU_SECURE_SRAM_40 + default 0xC000 if SCU_SECURE_SRAM_48 + default 0xE000 if SCU_SECURE_SRAM_56 + default 0x10000 if SCU_SECURE_SRAM_64 + default 0x12000 if SCU_SECURE_SRAM_72 + default 0x14000 if SCU_SECURE_SRAM_80 + default 0x16000 if SCU_SECURE_SRAM_88 + default 0x18000 if SCU_SECURE_SRAM_96 + +config FMC_SECURE_ROM_SIZE + hex "Secure Flash ROM Size" + default 0x40000 + range 0x1000 0x40000 + ---help--- + "Secure Flash ROM Size." + +menu "Peripheral Secure Attribution Configuration" + +choice + prompt "USBH secure configuration" + default USBH_SECURE + +config USBH_NONSECURE + bool "USBH non-secure" + ---help--- + USBH is non-secure. + +config USBH_SECURE + bool "USBH secure" + ---help--- + USBH is secure. +endchoice + +choice + prompt "SDO secure configuration" + default SDO_SECURE + +config SDO_NONSECURE + bool "SDO non-secure" + ---help--- + SDO is non-secure. + +config SDO_SECURE + bool "SDO secure" + ---help--- + SDO is secure. +endchoice + +choice + prompt "EBI secure configuration" + default EBI_SECURE + +config EBI_NONSECURE + bool "EBI non-secure" + ---help--- + EBI is non-secure. + +config EBI_SECURE + bool "EBI secure" + ---help--- + EBI is secure. +endchoice + +choice + prompt "PDMA1 secure configuration" + default PDMA1_SECURE + +config PDMA1_NONSECURE + bool "PDMA1 non-secure" + ---help--- + PDMA1 is non-secure. + +config PDMA1_SECURE + bool "PDMA1 secure" + ---help--- + PDMA1 is secure. +endchoice + +choice + prompt "CRC secure configuration" + default CRC_SECURE + +config CRC_NONSECURE + bool "CRC non-secure" + ---help--- + CRC is non-secure. + +config CRC_SECURE + bool "CRC secure" + ---help--- + CRC is secure. +endchoice + +choice + prompt "CRPT secure configuration" + default CRPT_SECURE + +config CRPT_NONSECURE + bool "CRPT non-secure" + ---help--- + CRPT is non-secure. + +config CRPT_SECURE + bool "CRPT secure" + ---help--- + CRPT is secure. +endchoice + +choice + prompt "RTC secure configuration" + default RTC_SECURE + +config RTC_NONSECURE + bool "RTC non-secure" + ---help--- + RTC is non-secure. + +config RTC_SECURE + bool "RTC secure" + ---help--- + RTC is secure. +endchoice + +choice + prompt "EADC secure configuration" + default EADC_SECURE + +config EADC_NONSECURE + bool "EADC non-secure" + ---help--- + EADC is non-secure. + +config EADC_SECURE + bool "EADC secure" + ---help--- + EADC is secure. +endchoice + +choice + prompt "ACMP01 secure configuration" + default ACMP01_SECURE + +config ACMP01_NONSECURE + bool "ACMP01 non-secure" + ---help--- + ACMP01 is non-secure. + +config ACMP01_SECURE + bool "ACMP01 secure" + ---help--- + ACMP01 is secure. +endchoice + +choice + prompt "DAC secure configuration" + default DAC_SECURE + +config DAC_NONSECURE + bool "DAC non-secure" + ---help--- + DAC is non-secure. + +config DAC_SECURE + bool "DAC secure" + ---help--- + DAC is secure. +endchoice + +choice + prompt "I2S0 secure configuration" + default I2S0_SECURE + +config I2S0_NONSECURE + bool "I2S0 non-secure" + ---help--- + I2S0 is non-secure. + +config I2S0_SECURE + bool "I2S0 secure" + ---help--- + I2S0 is secure. +endchoice + +choice + prompt "OTG secure configuration" + default OTG_SECURE + +config OTG_NONSECURE + bool "OTG non-secure" + ---help--- + OTG is non-secure. + +config OTG_SECURE + bool "OTG secure" + ---help--- + OTG is secure. +endchoice + +choice + prompt "TMR23 secure configuration" + default TMR23_SECURE + +config TMR23_NONSECURE + bool "TMR23 non-secure" + ---help--- + TMR23 is non-secure. + +config TMR23_SECURE + bool "TMR23 secure" + ---help--- + TMR23 is secure. +endchoice + +choice + prompt "EPWM0 secure configuration" + default EPWM0_SECURE + +config EPWM0_NONSECURE + bool "EPWM0 non-secure" + ---help--- + EPWM0 is non-secure. + +config EPWM0_SECURE + bool "EPWM0 secure" + ---help--- + EPWM0 is secure. +endchoice + +choice + prompt "EPWM1 secure configuration" + default EPWM1_SECURE + +config EPWM1_NONSECURE + bool "EPWM1 non-secure" + ---help--- + EPWM1 is non-secure. + +config EPWM1_SECURE + bool "EPWM1 secure" + ---help--- + EPWM1 is secure. +endchoice + +choice + prompt "BPWM0 secure configuration" + default BPWM0_SECURE + +config BPWM0_NONSECURE + bool "BPWM0 non-secure" + ---help--- + BPWM0 is non-secure. + +config BPWM0_SECURE + bool "BPWM0 secure" + ---help--- + BPWM0 is secure. +endchoice + +choice + prompt "BPWM1 secure configuration" + default BPWM1_SECURE + +config BPWM1_NONSECURE + bool "BPWM1 non-secure" + ---help--- + BPWM1 is non-secure. + +config BPWM1_SECURE + bool "BPWM1 secure" + ---help--- + BPWM1 is secure. +endchoice + +choice + prompt "QSPI0 secure configuration" + default QSPI0_SECURE + +config QSPI0_NONSECURE + bool "QSPI0 non-secure" + ---help--- + QSPI0 is non-secure. + +config QSPI0_SECURE + bool "QSPI0 secure" + ---help--- + QSPI0 is secure. +endchoice + +choice + prompt "SPI0 secure configuration" + default SPI0_SECURE + +config SPI0_NONSECURE + bool "SPI0 non-secure" + ---help--- + SPI0 is non-secure. + +config SPI0_SECURE + bool "SPI0 secure" + ---help--- + SPI0 is secure. +endchoice + +choice + prompt "SPI1 secure configuration" + default SPI1_SECURE + +config SPI1_NONSECURE + bool "SPI1 non-secure" + ---help--- + SPI1 is non-secure. + +config SPI1_SECURE + bool "SPI1 secure" + ---help--- + SPI1 is secure. +endchoice + +choice + prompt "SPI2 secure configuration" + default SPI2_SECURE + +config SPI2_NONSECURE + bool "SPI2 non-secure" + ---help--- + SPI2 is non-secure. + +config SPI2_SECURE + bool "SPI2 secure" + ---help--- + SPI2 is secure. +endchoice + +choice + prompt "SPI3 secure configuration" + default SPI3_SECURE + +config SPI3_NONSECURE + bool "SPI3 non-secure" + ---help--- + SPI3 is non-secure. + +config SPI3_SECURE + bool "SPI3 secure" + ---help--- + SPI3 is secure. +endchoice + +choice + prompt "UART0 secure configuration" + default UART0_SECURE + +config UART0_NONSECURE + bool "UART0 non-secure" + ---help--- + UART0 is non-secure. + +config UART0_SECURE + bool "UART0 secure" + ---help--- + UART0 is secure. +endchoice + +choice + prompt "UART1 secure configuration" + default UART1_SECURE + +config UART1_NONSECURE + bool "UART1 non-secure" + ---help--- + UART1 is non-secure. + +config UART1_SECURE + bool "UART1 secure" + ---help--- + UART1 is secure. +endchoice + +choice + prompt "UART2 secure configuration" + default UART2_SECURE + +config UART2_NONSECURE + bool "UART2 non-secure" + ---help--- + UART2 is non-secure. + +config UART2_SECURE + bool "UART2 secure" + ---help--- + UART2 is secure. +endchoice + +choice + prompt "UART3 secure configuration" + default UART3_SECURE + +config UART3_NONSECURE + bool "UART3 non-secure" + ---help--- + UART3 is non-secure. + +config UART3_SECURE + bool "UART3 secure" + ---help--- + UART3 is secure. +endchoice + +choice + prompt "UART4 secure configuration" + default UART4_SECURE + +config UART4_NONSECURE + bool "UART4 non-secure" + ---help--- + UART4 is non-secure. + +config UART4_SECURE + bool "UART4 secure" + ---help--- + UART4 is secure. +endchoice + +choice + prompt "UART5 secure configuration" + default UART5_SECURE + +config UART5_NONSECURE + bool "UART5 non-secure" + ---help--- + UART5 is non-secure. + +config UART5_SECURE + bool "UART5 secure" + ---help--- + UART5 is secure. +endchoice + +choice + prompt "I2C0 secure configuration" + default I2C0_SECURE + +config I2C0_NONSECURE + bool "I2C0 non-secure" + ---help--- + I2C0 is non-secure. + +config I2C0_SECURE + bool "I2C0 secure" + ---help--- + I2C0 is secure. +endchoice + +choice + prompt "I2C1 secure configuration" + default I2C1_SECURE + +config I2C1_NONSECURE + bool "I2C1 non-secure" + ---help--- + I2C1 is non-secure. + +config I2C1_SECURE + bool "I2C1 secure" + ---help--- + I2C1 is secure. +endchoice + +choice + prompt "I2C2 secure configuration" + default I2C2_SECURE + +config I2C2_NONSECURE + bool "I2C2 non-secure" + ---help--- + I2C2 is non-secure. + +config I2C2_SECURE + bool "I2C2 secure" + ---help--- + I2C2 is secure. +endchoice + +choice + prompt "SC0 secure configuration" + default SC0_SECURE + +config SC0_NONSECURE + bool "SC0 non-secure" + ---help--- + SC0 is non-secure. + +config SC0_SECURE + bool "SC0 secure" + ---help--- + SC0 is secure. +endchoice + +choice + prompt "SC1 secure configuration" + default SC1_SECURE + +config SC1_NONSECURE + bool "SC1 non-secure" + ---help--- + SC1 is non-secure. + +config SC1_SECURE + bool "SC1 secure" + ---help--- + SC1 is secure. +endchoice + +choice + prompt "SC2 secure configuration" + default SC2_SECURE + +config SC2_NONSECURE + bool "SC2 non-secure" + ---help--- + SC2 is non-secure. + +config SC2_SECURE + bool "SC2 secure" + ---help--- + SC2 is secure. +endchoice + +choice + prompt "CAN0 secure configuration" + default CAN0_SECURE + +config CAN0_NONSECURE + bool "CAN0 non-secure" + ---help--- + CAN0 is non-secure. + +config CAN0_SECURE + bool "CAN0 secure" + ---help--- + CAN0 is secure. +endchoice + +choice + prompt "QEI0 secure configuration" + default QEI0_SECURE + +config QEI0_NONSECURE + bool "QEI0 non-secure" + ---help--- + QEI0 is non-secure. + +config QEI0_SECURE + bool "QEI0 secure" + ---help--- + QEI0 is secure. +endchoice + +choice + prompt "QEI1 secure configuration" + default QEI1_SECURE + +config QEI1_NONSECURE + bool "QEI1 non-secure" + ---help--- + QEI1 is non-secure. + +config QEI1_SECURE + bool "QEI1 secure" + ---help--- + QEI1 is secure. +endchoice + +choice + prompt "ECAP0 secure configuration" + default ECAP0_SECURE + +config ECAP0_NONSECURE + bool "ECAP0 non-secure" + ---help--- + ECAP0 is non-secure. + +config ECAP0_SECURE + bool "ECAP0 secure" + ---help--- + ECAP0 is secure. +endchoice + +choice + prompt "ECAP1 secure configuration" + default ECAP1_SECURE + +config ECAP1_NONSECURE + bool "ECAP1 non-secure" + ---help--- + ECAP1 is non-secure. + +config ECAP1_SECURE + bool "ECAP1 secure" + ---help--- + ECAP1 is secure. +endchoice + +choice + prompt "TRNG secure configuration" + default TRNG_SECURE + +config TRNG_NONSECURE + bool "TRNG non-secure" + ---help--- + TRNG is non-secure. + +config TRNG_SECURE + bool "TRNG secure" + ---help--- + TRNG is secure. +endchoice + +choice + prompt "USBD secure configuration" + default USBD_SECURE + +config USBD_NONSECURE + bool "USBD non-secure" + ---help--- + USBD is non-secure. + +config USBD_SECURE + bool "USBD secure" + ---help--- + USBD is secure. +endchoice + +choice + prompt "USCI0 secure configuration" + default USCI0_SECURE + +config USCI0_NONSECURE + bool "USCI0 non-secure" + ---help--- + USCI0 is non-secure. + +config USCI0_SECURE + bool "USCI0 secure" + ---help--- + USCI0 is secure. +endchoice + +choice + prompt "USCI1 secure configuration" + default USCI1_SECURE + +config USCI1_NONSECURE + bool "USCI1 non-secure" + ---help--- + USCI1 is non-secure. + +config USCI1_SECURE + bool "USCI1 secure" + ---help--- + USCI1 is secure. +endchoice + +endmenu # Peripheral Secure Attribution Configuration + +menu "Assign Interrupt to Secure or Non-secure Vector" +choice + prompt "RTC Interrupt target secure configuration" + default RTC_INTERRUPT_SECURE + +config RTC_INTERRUPT_NONSECURE + bool "RTC interrupt non-secure" + ---help--- + RTC interrupt is non-secure. + +config RTC_INTERRUPT_SECURE + bool "RTC interrupt secure" + ---help--- + RTC interrupt is secure. +endchoice + +choice + prompt "TAMPER Interrupt target secure configuration" + default TAMPER_INTERRUPT_SECURE + +config TAMPER_INTERRUPT_NONSECURE + bool "TAMPER interrupt non-secure" + ---help--- + TAMPER interrupt is non-secure. + +config TAMPER_INTERRUPT_SECURE + bool "TAMPER interrupt secure" + ---help--- + TAMPER interrupt is secure. +endchoice + +choice + prompt "EINT0 Interrupt target secure configuration" + default EINT0_INTERRUPT_SECURE + +config EINT0_INTERRUPT_NONSECURE + bool "EINT0 interrupt non-secure" + ---help--- + EINT0 interrupt is non-secure. + +config EINT0_INTERRUPT_SECURE + bool "EINT0 interrupt secure" + ---help--- + EINT0 interrupt is secure. +endchoice + +choice + prompt "EINT1 Interrupt target secure configuration" + default EINT1_INTERRUPT_SECURE + +config EINT1_INTERRUPT_NONSECURE + bool "EINT1 interrupt non-secure" + ---help--- + EINT1 interrupt is non-secure. + +config EINT1_INTERRUPT_SECURE + bool "EINT1 interrupt secure" + ---help--- + EINT1 interrupt is secure. +endchoice + +choice + prompt "EINT2 Interrupt target secure configuration" + default EINT2_INTERRUPT_SECURE + +config EINT2_INTERRUPT_NONSECURE + bool "EINT2 interrupt non-secure" + ---help--- + EINT2 interrupt is non-secure. + +config EINT2_INTERRUPT_SECURE + bool "EINT2 interrupt secure" + ---help--- + EINT2 interrupt is secure. +endchoice + +choice + prompt "EINT3 Interrupt target secure configuration" + default EINT3_INTERRUPT_SECURE + +config EINT3_INTERRUPT_NONSECURE + bool "EINT3 interrupt non-secure" + ---help--- + EINT3 interrupt is non-secure. + +config EINT3_INTERRUPT_SECURE + bool "EINT3 interrupt secure" + ---help--- + EINT3 interrupt is secure. +endchoice + +choice + prompt "EINT4 Interrupt target secure configuration" + default EINT4_INTERRUPT_SECURE + +config EINT4_INTERRUPT_NONSECURE + bool "EINT4 interrupt non-secure" + ---help--- + EINT4 interrupt is non-secure. + +config EINT4_INTERRUPT_SECURE + bool "EINT4 interrupt secure" + ---help--- + EINT4 interrupt is secure. +endchoice + +choice + prompt "EINT5 Interrupt target secure configuration" + default EINT5_INTERRUPT_SECURE + +config EINT5_INTERRUPT_NONSECURE + bool "EINT5 interrupt non-secure" + ---help--- + EINT5 interrupt is non-secure. + +config EINT5_INTERRUPT_SECURE + bool "EINT5 interrupt secure" + ---help--- + EINT5 interrupt is secure. +endchoice + +choice + prompt "GPA Interrupt target secure configuration" + default GPA_INTERRUPT_SECURE + +config GPA_INTERRUPT_NONSECURE + bool "GPA interrupt non-secure" + ---help--- + GPA interrupt is non-secure. + +config GPA_INTERRUPT_SECURE + bool "GPA interrupt secure" + ---help--- + GPA interrupt is secure. +endchoice + +choice + prompt "GPB Interrupt target secure configuration" + default GPB_INTERRUPT_SECURE + +config GPB_INTERRUPT_NONSECURE + bool "GPB interrupt non-secure" + ---help--- + GPB interrupt is non-secure. + +config GPB_INTERRUPT_SECURE + bool "GPB interrupt secure" + ---help--- + GPB interrupt is secure. +endchoice + +choice + prompt "GPC Interrupt target secure configuration" + default GPC_INTERRUPT_SECURE + +config GPC_INTERRUPT_NONSECURE + bool "GPC interrupt non-secure" + ---help--- + GPC interrupt is non-secure. + +config GPC_INTERRUPT_SECURE + bool "GPC interrupt secure" + ---help--- + GPC interrupt is secure. +endchoice + +choice + prompt "GPD Interrupt target secure configuration" + default GPD_INTERRUPT_SECURE + +config GPD_INTERRUPT_NONSECURE + bool "GPD interrupt non-secure" + ---help--- + GPD interrupt is non-secure. + +config GPD_INTERRUPT_SECURE + bool "GPD interrupt secure" + ---help--- + GPD interrupt is secure. +endchoice + +choice + prompt "GPE Interrupt target secure configuration" + default GPE_INTERRUPT_SECURE + +config GPE_INTERRUPT_NONSECURE + bool "GPE interrupt non-secure" + ---help--- + GPE interrupt is non-secure. + +config GPE_INTERRUPT_SECURE + bool "GPE interrupt secure" + ---help--- + GPE interrupt is secure. +endchoice + +choice + prompt "GPF Interrupt target secure configuration" + default GPF_INTERRUPT_SECURE + +config GPF_INTERRUPT_NONSECURE + bool "GPF interrupt non-secure" + ---help--- + GPF interrupt is non-secure. + +config GPF_INTERRUPT_SECURE + bool "GPF interrupt secure" + ---help--- + GPF interrupt is secure. +endchoice + +choice + prompt "QSPI0 Interrupt target secure configuration" + default QSPI0_INTERRUPT_SECURE + +config QSPI0_INTERRUPT_NONSECURE + bool "QSPI0 interrupt non-secure" + ---help--- + QSPI0 interrupt is non-secure. + +config QSPI0_INTERRUPT_SECURE + bool "QSPI0 interrupt secure" + ---help--- + QSPI0 interrupt is secure. +endchoice + +choice + prompt "SPI0 Interrupt target secure configuration" + default SPI0_INTERRUPT_SECURE + +config SPI0_INTERRUPT_NONSECURE + bool "SPI0 interrupt non-secure" + ---help--- + SPI0 interrupt is non-secure. + +config SPI0_INTERRUPT_SECURE + bool "SPI0 interrupt secure" + ---help--- + SPI0 interrupt is secure. +endchoice + +choice + prompt "BRAKE0 Interrupt target secure configuration" + default BRAKE0_INTERRUPT_SECURE + +config BRAKE0_INTERRUPT_NONSECURE + bool "BRAKE0 interrupt non-secure" + ---help--- + BRAKE0 interrupt is non-secure. + +config BRAKE0_INTERRUPT_SECURE + bool "BRAKE0 interrupt secure" + ---help--- + BRAKE0 interrupt is secure. +endchoice + +choice + prompt "EPWM0_P0 Interrupt target secure configuration" + default EPWM0_P0_INTERRUPT_SECURE + +config EPWM0_P0_INTERRUPT_NONSECURE + bool "EPWM0_P0 interrupt non-secure" + ---help--- + EPWM0_P0 interrupt is non-secure. + +config EPWM0_P0_INTERRUPT_SECURE + bool "EPWM0_P0 interrupt secure" + ---help--- + EPWM0_P0 interrupt is secure. +endchoice + +choice + prompt "EPWM0_P1 Interrupt target secure configuration" + default EPWM0_P1_INTERRUPT_SECURE + +config EPWM0_P1_INTERRUPT_NONSECURE + bool "EPWM0_P1 interrupt non-secure" + ---help--- + EPWM0_P1 interrupt is non-secure. + +config EPWM0_P1_INTERRUPT_SECURE + bool "EPWM0_P1 interrupt secure" + ---help--- + EPWM0_P1 interrupt is secure. +endchoice + +choice + prompt "EPWM0_P2 Interrupt target secure configuration" + default EPWM0_P2_INTERRUPT_SECURE + +config EPWM0_P2_INTERRUPT_NONSECURE + bool "EPWM0_P2 interrupt non-secure" + ---help--- + EPWM0_P2 interrupt is non-secure. + +config EPWM0_P2_INTERRUPT_SECURE + bool "EPWM0_P2 interrupt secure" + ---help--- + EPWM0_P2 interrupt is secure. +endchoice + +choice + prompt "BRAKE1 Interrupt target secure configuration" + default BRAKE1_INTERRUPT_SECURE + +config BRAKE1_INTERRUPT_NONSECURE + bool "BRAKE1 interrupt non-secure" + ---help--- + BRAKE1 interrupt is non-secure. + +config BRAKE1_INTERRUPT_SECURE + bool "BRAKE1 interrupt secure" + ---help--- + BRAKE1 interrupt is secure. +endchoice + +choice + prompt "EPWM1_P0 Interrupt target secure configuration" + default EPWM1_P0_INTERRUPT_SECURE + +config EPWM1_P0_INTERRUPT_NONSECURE + bool "EPWM1_P0 interrupt non-secure" + ---help--- + EPWM1_P0 interrupt is non-secure. + +config EPWM1_P0_INTERRUPT_SECURE + bool "EPWM1_P0 interrupt secure" + ---help--- + EPWM1_P0 interrupt is secure. +endchoice + +choice + prompt "EPWM1_P1 Interrupt target secure configuration" + default EPWM1_P1_INTERRUPT_SECURE + +config EPWM1_P1_INTERRUPT_NONSECURE + bool "EPWM1_P1 interrupt non-secure" + ---help--- + EPWM1_P1 interrupt is non-secure. + +config EPWM1_P1_INTERRUPT_SECURE + bool "EPWM1_P1 interrupt secure" + ---help--- + EPWM1_P1 interrupt is secure. +endchoice + +choice + prompt "EPWM1_P2 Interrupt target secure configuration" + default EPWM1_P2_INTERRUPT_SECURE + +config EPWM1_P2_INTERRUPT_NONSECURE + bool "EPWM1_P2 interrupt non-secure" + ---help--- + EPWM1_P2 interrupt is non-secure. + +config EPWM1_P2_INTERRUPT_SECURE + bool "EPWM1_P2 interrupt secure" + ---help--- + EPWM1_P2 interrupt is secure. +endchoice + +choice + prompt "TMR2 Interrupt target secure configuration" + default TMR2_INTERRUPT_SECURE + +config TMR2_INTERRUPT_NONSECURE + bool "TMR2 interrupt non-secure" + ---help--- + TMR2 interrupt is non-secure. + +config TMR2_INTERRUPT_SECURE + bool "TMR2 interrupt secure" + ---help--- + TMR2 interrupt is secure. +endchoice + +choice + prompt "TMR3 Interrupt target secure configuration" + default TMR3_INTERRUPT_SECURE + +config TMR3_INTERRUPT_NONSECURE + bool "TMR3 interrupt non-secure" + ---help--- + TMR3 interrupt is non-secure. + +config TMR3_INTERRUPT_SECURE + bool "TMR3 interrupt secure" + ---help--- + TMR3 interrupt is secure. +endchoice + +choice + prompt "UART0 Interrupt target secure configuration" + default UART0_INTERRUPT_SECURE + +config UART0_INTERRUPT_NONSECURE + bool "UART0 interrupt non-secure" + ---help--- + UART0 interrupt is non-secure. + +config UART0_INTERRUPT_SECURE + bool "UART0 interrupt secure" + ---help--- + UART0 interrupt is secure. +endchoice + +choice + prompt "UART1 Interrupt target secure configuration" + default UART1_INTERRUPT_SECURE + +config UART1_INTERRUPT_NONSECURE + bool "UART1 interrupt non-secure" + ---help--- + UART1 interrupt is non-secure. + +config UART1_INTERRUPT_SECURE + bool "UART1 interrupt secure" + ---help--- + UART1 interrupt is secure. +endchoice + +choice + prompt "I2C0 Interrupt target secure configuration" + default I2C0_INTERRUPT_SECURE + +config I2C0_INTERRUPT_NONSECURE + bool "I2C0 interrupt non-secure" + ---help--- + I2C0 interrupt is non-secure. + +config I2C0_INTERRUPT_SECURE + bool "I2C0 interrupt secure" + ---help--- + I2C0 interrupt is secure. +endchoice + +choice + prompt "I2C1 Interrupt target secure configuration" + default I2C1_INTERRUPT_SECURE + +config I2C1_INTERRUPT_NONSECURE + bool "I2C1 interrupt non-secure" + ---help--- + I2C1 interrupt is non-secure. + +config I2C1_INTERRUPT_SECURE + bool "I2C1 interrupt secure" + ---help--- + I2C1 interrupt is secure. +endchoice + +choice + prompt "DAC Interrupt target secure configuration" + default DAC_INTERRUPT_SECURE + +config DAC_INTERRUPT_NONSECURE + bool "DAC interrupt non-secure" + ---help--- + DAC interrupt is non-secure. + +config DAC_INTERRUPT_SECURE + bool "DAC interrupt secure" + ---help--- + DAC interrupt is secure. +endchoice + +choice + prompt "EADC0 Interrupt target secure configuration" + default EADC0_INTERRUPT_SECURE + +config EADC0_INTERRUPT_NONSECURE + bool "EADC0 interrupt non-secure" + ---help--- + EADC0 interrupt is non-secure. + +config EADC0_INTERRUPT_SECURE + bool "EADC0 interrupt secure" + ---help--- + EADC0 interrupt is secure. +endchoice + +choice + prompt "EADC1 Interrupt target secure configuration" + default EADC1_INTERRUPT_SECURE + +config EADC1_INTERRUPT_NONSECURE + bool "EADC1 interrupt non-secure" + ---help--- + EADC1 interrupt is non-secure. + +config EADC1_INTERRUPT_SECURE + bool "EADC1 interrupt secure" + ---help--- + EADC1 interrupt is secure. +endchoice + +choice + prompt "ACMP01 Interrupt target secure configuration" + default ACMP01_INTERRUPT_SECURE + +config ACMP01_INTERRUPT_NONSECURE + bool "ACMP01 interrupt non-secure" + ---help--- + ACMP01 interrupt is non-secure. + +config ACMP01_INTERRUPT_SECURE + bool "ACMP01 interrupt secure" + ---help--- + ACMP01 interrupt is secure. +endchoice + +choice + prompt "EADC2 Interrupt target secure configuration" + default EADC2_INTERRUPT_SECURE + +config EADC2_INTERRUPT_NONSECURE + bool "EADC2 interrupt non-secure" + ---help--- + EADC2 interrupt is non-secure. + +config EADC2_INTERRUPT_SECURE + bool "EADC2 interrupt secure" + ---help--- + EADC2 interrupt is secure. +endchoice + +choice + prompt "EADC3 Interrupt target secure configuration" + default EADC3_INTERRUPT_SECURE + +config EADC3_INTERRUPT_NONSECURE + bool "EADC3 interrupt non-secure" + ---help--- + EADC3 interrupt is non-secure. + +config EADC3_INTERRUPT_SECURE + bool "EADC3 interrupt secure" + ---help--- + EADC3 interrupt is secure. +endchoice + +choice + prompt "UART2 Interrupt target secure configuration" + default UART2_INTERRUPT_SECURE + +config UART2_INTERRUPT_NONSECURE + bool "UART2 interrupt non-secure" + ---help--- + UART2 interrupt is non-secure. + +config UART2_INTERRUPT_SECURE + bool "UART2 interrupt secure" + ---help--- + UART2 interrupt is secure. +endchoice + +choice + prompt "UART3 Interrupt target secure configuration" + default UART3_INTERRUPT_SECURE + +config UART3_INTERRUPT_NONSECURE + bool "UART3 interrupt non-secure" + ---help--- + UART3 interrupt is non-secure. + +config UART3_INTERRUPT_SECURE + bool "UART3 interrupt secure" + ---help--- + UART3 interrupt is secure. +endchoice + +choice + prompt "SPI1 Interrupt target secure configuration" + default SPI1_INTERRUPT_SECURE + +config SPI1_INTERRUPT_NONSECURE + bool "SPI1 interrupt non-secure" + ---help--- + SPI1 interrupt is non-secure. + +config SPI1_INTERRUPT_SECURE + bool "SPI1 interrupt secure" + ---help--- + SPI1 interrupt is secure. +endchoice + +choice + prompt "SPI2 Interrupt target secure configuration" + default SPI2_INTERRUPT_SECURE + +config SPI2_INTERRUPT_NONSECURE + bool "SPI2 interrupt non-secure" + ---help--- + SPI2 interrupt is non-secure. + +config SPI2_INTERRUPT_SECURE + bool "SPI2 interrupt secure" + ---help--- + SPI2 interrupt is secure. +endchoice + +choice + prompt "USBD Interrupt target secure configuration" + default USBD_INTERRUPT_SECURE + +config USBD_INTERRUPT_NONSECURE + bool "USBD interrupt non-secure" + ---help--- + USBD interrupt is non-secure. + +config USBD_INTERRUPT_SECURE + bool "USBD interrupt secure" + ---help--- + USBD interrupt is secure. +endchoice + +choice + prompt "USBH Interrupt target secure configuration" + default USBH_INTERRUPT_SECURE + +config USBH_INTERRUPT_NONSECURE + bool "USBH interrupt non-secure" + ---help--- + USBH interrupt is non-secure. + +config USBH_INTERRUPT_SECURE + bool "USBH interrupt secure" + ---help--- + USBH interrupt is secure. +endchoice + +choice + prompt "USBOTG Interrupt target secure configuration" + default USBOTG_INTERRUPT_SECURE + +config USBOTG_INTERRUPT_NONSECURE + bool "USBOTG interrupt non-secure" + ---help--- + USBOTG interrupt is non-secure. + +config USBOTG_INTERRUPT_SECURE + bool "USBOTG interrupt secure" + ---help--- + USBOTG interrupt is secure. +endchoice + +choice + prompt "CAN0 Interrupt target secure configuration" + default CAN0_INTERRUPT_SECURE + +config CAN0_INTERRUPT_NONSECURE + bool "CAN0 interrupt non-secure" + ---help--- + CAN0 interrupt is non-secure. + +config CAN0_INTERRUPT_SECURE + bool "CAN0 interrupt secure" + ---help--- + CAN0 interrupt is secure. +endchoice + +choice + prompt "SC0 Interrupt target secure configuration" + default SC0_INTERRUPT_SECURE + +config SC0_INTERRUPT_NONSECURE + bool "SC0 interrupt non-secure" + ---help--- + SC0 interrupt is non-secure. + +config SC0_INTERRUPT_SECURE + bool "SC0 interrupt secure" + ---help--- + SC0 interrupt is secure. +endchoice + +choice + prompt "SC1 Interrupt target secure configuration" + default SC1_INTERRUPT_SECURE + +config SC1_INTERRUPT_NONSECURE + bool "SC1 interrupt non-secure" + ---help--- + SC1 interrupt is non-secure. + +config SC1_INTERRUPT_SECURE + bool "SC1 interrupt secure" + ---help--- + SC1 interrupt is secure. +endchoice + +choice + prompt "SC2 Interrupt target secure configuration" + default SC2_INTERRUPT_SECURE + +config SC2_INTERRUPT_NONSECURE + bool "SC2 interrupt non-secure" + ---help--- + SC2 interrupt is non-secure. + +config SC2_INTERRUPT_SECURE + bool "SC2 interrupt secure" + ---help--- + SC2 interrupt is secure. +endchoice + +choice + prompt "SPI3 Interrupt target secure configuration" + default SPI3_INTERRUPT_SECURE + +config SPI3_INTERRUPT_NONSECURE + bool "SPI3 interrupt non-secure" + ---help--- + SPI3 interrupt is non-secure. + +config SPI3_INTERRUPT_SECURE + bool "SPI3 interrupt secure" + ---help--- + SPI3 interrupt is secure. +endchoice + +choice + prompt "SDH0 Interrupt target secure configuration" + default SDH0_INTERRUPT_SECURE + +config SDH0_INTERRUPT_NONSECURE + bool "SDH0 interrupt non-secure" + ---help--- + SDH0 interrupt is non-secure. + +config SDH0_INTERRUPT_SECURE + bool "SDH0 interrupt secure" + ---help--- + SDH0 interrupt is secure. +endchoice + +choice + prompt "I2S0 Interrupt target secure configuration" + default I2S0_INTERRUPT_SECURE + +config I2S0_INTERRUPT_NONSECURE + bool "I2S0 interrupt non-secure" + ---help--- + I2S0 interrupt is non-secure. + +config I2S0_INTERRUPT_SECURE + bool "I2S0 interrupt secure" + ---help--- + I2S0 interrupt is secure. +endchoice + +choice + prompt "CRYPTO Interrupt target secure configuration" + default CRYPTO_INTERRUPT_SECURE + +config CRYPTO_INTERRUPT_NONSECURE + bool "CRYPTO interrupt non-secure" + ---help--- + CRYPTO interrupt is non-secure. + +config CRYPTO_INTERRUPT_SECURE + bool "CRYPTO interrupt secure" + ---help--- + CRYPTO interrupt is secure. +endchoice + +choice + prompt "GPG Interrupt target secure configuration" + default GPG_INTERRUPT_SECURE + +config GPG_INTERRUPT_NONSECURE + bool "GPG interrupt non-secure" + ---help--- + GPG interrupt is non-secure. + +config GPG_INTERRUPT_SECURE + bool "GPG interrupt secure" + ---help--- + GPG interrupt is secure. +endchoice + +choice + prompt "EINT6 Interrupt target secure configuration" + default EINT6_INTERRUPT_SECURE + +config EINT6_INTERRUPT_NONSECURE + bool "EINT6 interrupt non-secure" + ---help--- + EINT6 interrupt is non-secure. + +config EINT6_INTERRUPT_SECURE + bool "EINT6 interrupt secure" + ---help--- + EINT6 interrupt is secure. +endchoice + +choice + prompt "UART4 Interrupt target secure configuration" + default UART4_INTERRUPT_SECURE + +config UART4_INTERRUPT_NONSECURE + bool "UART4 interrupt non-secure" + ---help--- + UART4 interrupt is non-secure. + +config UART4_INTERRUPT_SECURE + bool "UART4 interrupt secure" + ---help--- + UART4 interrupt is secure. +endchoice + +choice + prompt "UART5 Interrupt target secure configuration" + default UART5_INTERRUPT_SECURE + +config UART5_INTERRUPT_NONSECURE + bool "UART5 interrupt non-secure" + ---help--- + UART5 interrupt is non-secure. + +config UART5_INTERRUPT_SECURE + bool "UART5 interrupt secure" + ---help--- + UART5 interrupt is secure. +endchoice + +choice + prompt "USCI0 Interrupt target secure configuration" + default USCI0_INTERRUPT_SECURE + +config USCI0_INTERRUPT_NONSECURE + bool "USCI0 interrupt non-secure" + ---help--- + USCI0 interrupt is non-secure. + +config USCI0_INTERRUPT_SECURE + bool "USCI0 interrupt secure" + ---help--- + USCI0 interrupt is secure. +endchoice + +choice + prompt "USCI1 Interrupt target secure configuration" + default USCI1_INTERRUPT_SECURE + +config USCI1_INTERRUPT_NONSECURE + bool "USCI1 interrupt non-secure" + ---help--- + USCI1 interrupt is non-secure. + +config USCI1_INTERRUPT_SECURE + bool "USCI1 interrupt secure" + ---help--- + USCI1 interrupt is secure. +endchoice + +choice + prompt "BPWM0 Interrupt target secure configuration" + default BPWM0_INTERRUPT_SECURE + +config BPWM0_INTERRUPT_NONSECURE + bool "BPWM0 interrupt non-secure" + ---help--- + BPWM0 interrupt is non-secure. + +config BPWM0_INTERRUPT_SECURE + bool "BPWM0 interrupt secure" + ---help--- + BPWM0 interrupt is secure. +endchoice + +choice + prompt "BPWM1 Interrupt target secure configuration" + default BPWM1_INTERRUPT_SECURE + +config BPWM1_INTERRUPT_NONSECURE + bool "BPWM1 interrupt non-secure" + ---help--- + BPWM1 interrupt is non-secure. + +config BPWM1_INTERRUPT_SECURE + bool "BPWM1 interrupt secure" + ---help--- + BPWM1 interrupt is secure. +endchoice + +choice + prompt "I2C2 Interrupt target secure configuration" + default I2C2_INTERRUPT_SECURE + +config I2C2_INTERRUPT_NONSECURE + bool "I2C2 interrupt non-secure" + ---help--- + I2C2 interrupt is non-secure. + +config I2C2_INTERRUPT_SECURE + bool "I2C2 interrupt secure" + ---help--- + I2C2 interrupt is secure. +endchoice + +choice + prompt "QEI0 Interrupt target secure configuration" + default QEI0_INTERRUPT_SECURE + +config QEI0_INTERRUPT_NONSECURE + bool "QEI0 interrupt non-secure" + ---help--- + QEI0 interrupt is non-secure. + +config QEI0_INTERRUPT_SECURE + bool "QEI0 interrupt secure" + ---help--- + QEI0 interrupt is secure. +endchoice + +choice + prompt "QEI1 Interrupt target secure configuration" + default QEI1_INTERRUPT_SECURE + +config QEI1_INTERRUPT_NONSECURE + bool "QEI1 interrupt non-secure" + ---help--- + QEI1 interrupt is non-secure. + +config QEI1_INTERRUPT_SECURE + bool "QEI1 interrupt secure" + ---help--- + QEI1 interrupt is secure. +endchoice + +choice + prompt "ECAP0 Interrupt target secure configuration" + default ECAP0_INTERRUPT_SECURE + +config ECAP0_INTERRUPT_NONSECURE + bool "ECAP0 interrupt non-secure" + ---help--- + ECAP0 interrupt is non-secure. + +config ECAP0_INTERRUPT_SECURE + bool "ECAP0 interrupt secure" + ---help--- + ECAP0 interrupt is secure. +endchoice + +choice + prompt "ECAP1 Interrupt target secure configuration" + default ECAP1_INTERRUPT_SECURE + +config ECAP1_INTERRUPT_NONSECURE + bool "ECAP1 interrupt non-secure" + ---help--- + ECAP1 interrupt is non-secure. + +config ECAP1_INTERRUPT_SECURE + bool "ECAP1 interrupt secure" + ---help--- + ECAP1 interrupt is secure. +endchoice + +choice + prompt "GPH Interrupt target secure configuration" + default GPH_INTERRUPT_SECURE + +config GPH_INTERRUPT_NONSECURE + bool "GPH interrupt non-secure" + ---help--- + GPH interrupt is non-secure. + +config GPH_INTERRUPT_SECURE + bool "GPH interrupt secure" + ---help--- + GPH interrupt is secure. +endchoice + +choice + prompt "EINT7 Interrupt target secure configuration" + default EINT7_INTERRUPT_SECURE + +config EINT7_INTERRUPT_NONSECURE + bool "EINT7 interrupt non-secure" + ---help--- + EINT7 interrupt is non-secure. + +config EINT7_INTERRUPT_SECURE + bool "EINT7 interrupt secure" + ---help--- + EINT7 interrupt is secure. +endchoice + +choice + prompt "PDMA1 Interrupt target secure configuration" + default PDMA1_INTERRUPT_SECURE + +config PDMA1_INTERRUPT_NONSECURE + bool "PDMA1 interrupt non-secure" + ---help--- + PDMA1 interrupt is non-secure. + +config PDMA1_INTERRUPT_SECURE + bool "PDMA1 interrupt secure" + ---help--- + PDMA1 interrupt is secure. +endchoice + +choice + prompt "TRNG Interrupt target secure configuration" + default TRNG_INTERRUPT_SECURE + +config TRNG_INTERRUPT_NONSECURE + bool "TRNG interrupt non-secure" + ---help--- + TRNG interrupt is non-secure. + +config TRNG_INTERRUPT_SECURE + bool "TRNG interrupt secure" + ---help--- + TRNG interrupt is secure. +endchoice + +endmenu # Assign Interrupt to Secure or Non-secure Vector + +menu "Enable secure violation interrupts" + +config APB0IEN_SECURE_VIOLATION_INTERRUPT + bool "APB0 secure violation interrupt enable" + default n + ---help--- + APB0 secure violation interrupt enable. + +config APB1IEN_SECURE_VIOLATION_INTERRUPT + bool "APB1 secure violation interrupt enable" + default n + ---help--- + APB1 secure violation interrupt enable. + +config GPIOIEN_SECURE_VIOLATION_INTERRUPT + bool "GPIO secure violation interrupt enable" + default n + ---help--- + GPIO secure violation interrupt enable. + +config EBIIEN_SECURE_VIOLATION_INTERRUPT + bool "EBI secure violation interrupt enable" + default n + ---help--- + EBI secure violation interrupt enable. + +config USBHIEN_SECURE_VIOLATION_INTERRUPT + bool "USBH secure violation interrupt enable" + default n + ---help--- + USBH secure violation interrupt enable. + +config CRCIEN_SECURE_VIOLATION_INTERRUPT + bool "CRC secure violation interrupt enable" + default n + ---help--- + CRC secure violation interrupt enable. + +config SDH0IEN_SECURE_VIOLATION_INTERRUPT + bool "SDH0 secure violation interrupt enable" + default n + ---help--- + SDH0 secure violation interrupt enable. + +config PDMA0IEN_SECURE_VIOLATION_INTERRUPT + bool "PDMA0 secure violation interrupt enable" + default n + ---help--- + PDMA0 secure violation interrupt enable. + +config PDMA1IEN_SECURE_VIOLATION_INTERRUPT + bool "PDMA1 secure violation interrupt enable" + default n + ---help--- + PDMA1 secure violation interrupt enable. + +config SRAM0IEN_SECURE_VIOLATION_INTERRUPT + bool "SRAM0 secure violation interrupt enable" + default n + ---help--- + SRAM0 secure violation interrupt enable. + +config SRAM1IEN_SECURE_VIOLATION_INTERRUPT + bool "SRAM1 secure violation interrupt enable" + default n + ---help--- + SRAM1 secure violation interrupt enable. + +config FMCIEN_SECURE_VIOLATION_INTERRUPT + bool "FMC secure violation interrupt enable" + default n + ---help--- + FMC secure violation interrupt enable. + +config FLASHIEN_SECURE_VIOLATION_INTERRUPT + bool "FLASH secure violation interrupt enable" + default n + ---help--- + FLASH secure violation interrupt enable. + +config SCUIEN_SECURE_VIOLATION_INTERRUPT + bool "SCU secure violation interrupt enable" + default n + ---help--- + SCU secure violation interrupt enable. + +config SYSIEN_SECURE_VIOLATION_INTERRUPT + bool "SYS secure violation interrupt enable" + default n + ---help--- + SYS secure violation interrupt enable. + +config CRPTIEN_SECURE_VIOLATION_INTERRUPT + bool "CRPT secure violation interrupt enable" + default n + ---help--- + CRPT secure violation interrupt enable. + +endmenu # Enable sercure violation interrupts + +config SAU_CONTROL + bool "Secure Attribute Unit (SAU) Control" + default y + ---help--- + "Secure Attribute Unit (SAU) Control". + +config ENABLE_SAU + bool "Enable SAU" + depends on SAU_CONTROL + default y + ---help--- + "Enable SAU". + +choice + prompt "All Memory Attribute When SAU is disabled" + depends on SAU_CONTROL + default ALL_MEMORY_SECURE + +config ALL_MEMORY_NONSECURE + bool "All Memory non-secure" + ---help--- + All Memory is non-secure. + +config ALL_MEMORY_SECURE + bool "All Memory secure" + ---help--- + All Memory is secure. +endchoice + +menu "Enable and Set Secure/Non-Secure region" + +config SAU_INIT_REGION0 + bool "Setup SAU Region 0" + default y + ---help--- + Setup SAU Region 0. + +config SAU_INIT_START0 + hex "Start address of SAU region 0" + depends on SAU_INIT_REGION0 + default 0x20000000 + ---help--- + "Start address of SAU region 0." + +config SAU_INIT_END0 + hex "End address of SAU region 0" + depends on SAU_INIT_REGION0 + default 0x20008000 + ---help--- + "End address of SAU region 0." + +choice + prompt "Region 0 secure configuration" + depends on SAU_INIT_REGION0 + default REGION0_SECURE + +config REGION0_NONSECURE + bool "Region 0 non-secure" + ---help--- + Region 0 is non-secure. + +config REGION0_SECURE + bool "Region 0 secure" + ---help--- + Region 0 is secure. +endchoice + +config SAU_INIT_REGION1 + bool "Setup SAU Region 1" + default n + ---help--- + Setup SAU Region 1. + +config SAU_INIT_START1 + hex "Start address of SAU region 1" + depends on SAU_INIT_REGION1 + default 0x10040000 + ---help--- + "Start address of SAU region 1." + +config SAU_INIT_END1 + hex "End address of SAU region 1" + depends on SAU_INIT_REGION1 + default 0x1007FFFF + ---help--- + "End address of SAU region 1." + +choice + prompt "Region 1 secure configuration" + depends on SAU_INIT_REGION1 + default REGION1_NONSECURE + +config REGION1_NONSECURE + bool "Region 1 non-secure" + ---help--- + Region 1 is non-secure. + +config REGION1_SECURE + bool "Region 1 secure" + ---help--- + Region 1 is secure. +endchoice + +config SAU_INIT_REGION2 + bool "Setup SAU Region 2" + default n + ---help--- + Setup SAU Region 2. + +config SAU_INIT_START2 + hex "Start address of SAU region 2" + depends on SAU_INIT_REGION2 + default 0x2000F000 + ---help--- + "Start address of SAU region 2." + +config SAU_INIT_END2 + hex "End address of SAU region 2" + depends on SAU_INIT_REGION2 + default 0x2000FFFF + ---help--- + "End address of SAU region 2." + +choice + prompt "Region 2 secure configuration" + depends on SAU_INIT_REGION2 + default REGION2_SECURE + +config REGION2_NONSECURE + bool "Region 2 non-secure" + ---help--- + Region 2 is non-secure. + +config REGION2_SECURE + bool "Region 2 secure" + ---help--- + Region 2 is secure. +endchoice + +config SAU_INIT_REGION3 + bool "Setup SAU Region 3" + default y + ---help--- + Setup SAU Region 3. + +config SAU_INIT_START3 + hex "Start address of SAU region 3" + depends on SAU_INIT_REGION3 + default 0x0003F000 + ---help--- + "Start address of SAU region 3." + +config SAU_INIT_END3 + hex "End address of SAU region 3" + depends on SAU_INIT_REGION3 + default 0x0003F7FF + ---help--- + "End address of SAU region 3." + +choice + prompt "Region 3 secure configuration" + depends on SAU_INIT_REGION3 + default REGION3_SECURE + +config REGION3_NONSECURE + bool "Region 3 non-secure" + ---help--- + Region 3 is non-secure. + +config REGION3_SECURE + bool "Region 3 secure" + ---help--- + Region 3 is secure. +endchoice + +config SAU_INIT_REGION4 + bool "Setup SAU Region 4" + default y + ---help--- + Setup SAU Region 4. + +config SAU_INIT_START4 + hex "Start address of SAU region 4" + depends on SAU_INIT_REGION4 + default 0x10040000 + ---help--- + "Start address of SAU region 4." + +config SAU_INIT_END4 + hex "End address of SAU region 4" + depends on SAU_INIT_REGION4 + default 0x1007FFFF + ---help--- + "End address of SAU region 4." + +choice + prompt "Region 4 secure configuration" + depends on SAU_INIT_REGION4 + default REGION4_NONSECURE + +config REGION4_NONSECURE + bool "Region 4 non-secure" + ---help--- + Region 4 is non-secure. + +config REGION4_SECURE + bool "Region 4 secure" + ---help--- + Region 4 is secure. +endchoice + +config SAU_INIT_REGION5 + bool "Setup SAU Region 5" + default y + ---help--- + Setup SAU Region 5. + +config SAU_INIT_START5 + hex "Start address of SAU region 5" + depends on SAU_INIT_REGION5 + default 0x00807E00 + ---help--- + "Start address of SAU region 5." + +config SAU_INIT_END5 + hex "End address of SAU region 5" + depends on SAU_INIT_REGION5 + default 0x00807FFF + ---help--- + "End address of SAU region 5." + +choice + prompt "Region 5 secure configuration" + depends on SAU_INIT_REGION5 + default REGION5_SECURE + +config REGION5_NONSECURE + bool "Region 5 non-secure" + ---help--- + Region 5 is non-secure. + +config REGION5_SECURE + bool "Region 5 secure" + ---help--- + Region 5 is secure. +endchoice + +config SAU_INIT_REGION6 + bool "Setup SAU Region 6" + default y + ---help--- + Setup SAU Region 6. + +config SAU_INIT_START6 + hex "Start address of SAU region 6" + depends on SAU_INIT_REGION6 + default 0x30008000 + ---help--- + "Start address of SAU region 6." + +config SAU_INIT_END6 + hex "End address of SAU region 6" + depends on SAU_INIT_REGION6 + default 0x30017FFF + ---help--- + "End address of SAU region 6." + +choice + prompt "Region 6 secure configuration" + depends on SAU_INIT_REGION6 + default REGION6_NONSECURE + +config REGION6_NONSECURE + bool "Region 6 non-secure" + ---help--- + Region 6 is non-secure. + +config REGION6_SECURE + bool "Region 6 secure" + ---help--- + Region 6 is secure. +endchoice + +config SAU_INIT_REGION7 + bool "Setup SAU Region 7" + default y + ---help--- + Setup SAU Region 7. + +config SAU_INIT_START7 + hex "Start address of SAU region 7" + depends on SAU_INIT_REGION7 + default 0x50000000 + ---help--- + "Start address of SAU region 7." + +config SAU_INIT_END7 + hex "End address of SAU region 7" + depends on SAU_INIT_REGION7 + default 0x5FFFFFFF + ---help--- + "End address of SAU region 7." + +choice + prompt "Region 7 secure configuration" + depends on SAU_INIT_REGION7 + default REGION7_NONSECURE + +config REGION7_NONSECURE + bool "Region 7 non-secure" + ---help--- + Region 7 is non-secure. + +config REGION7_SECURE + bool "Region 7 secure" + ---help--- + Region 7 is secure. +endchoice + +endmenu # Enable and Set Secure/Non-Secure region + +endmenu # Secure Attribution Configuration + +#if PLATFORM_M2351_BADGE +#comment "M2351-Badge Peripheral Configuration Options" +#source arch/cortex-m23/m2351/src/m2351_badge/Kconfig +#endif # PLATFORM_M2351_BADGE + +#if PLATFORM_NUMAKER_PFM_M2351 +#comment "NuMaker-PFM-M2351 Peripheral Configuration Options" +#source arch/cortex-m23/m2351/src/numaker_pfm_m2351/Kconfig +#endif # PLATFORM_NUMAKER_PFM_M2351 diff --git a/arch/riscv32/fe310/src/Makefile b/arch/riscv32/fe310/src/Makefile new file mode 100644 index 00000000..cf4c8331 --- /dev/null +++ b/arch/riscv32/fe310/src/Makefile @@ -0,0 +1,106 @@ +############################################################################ +# arch/cortex-m23/m2351/src/StdDriver/Makefile +# +# Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved. +# Author: Taras Drozdovskyi t.drozdovsky@samsung.com +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +############################################################################ + +-include $(TOPDIR)/Make.defs +-include $(TOPDIR)/.config + +CFLAGS += -I../../CMSIS/Include +CFLAGS += -I../../StdDriver/inc +CFLAGS += -I../../Device/Nuvoton/M2351/Include +CFLAGS += -I$(TOPDIR)/include/mtower +CFLAGS += -I$(TOPDIR)/arch/$(CONFIG_ARCH)/$(CONFIG_ARCH_FAMILY)/src/$(CONFIG_PLATFORM) + +ifeq ($(WORLD), secure) +CFLAGS += -mcmse +endif + +CFLAGS += -Wno-unused-parameter -Wno-undef -Wno-maybe-uninitialized +CFLAGS += -Wno-unused-variable -Wno-pointer-compare + +#SRCS += acmp.c +#SRCS += bpwm.c +#SRCS += can.c +#SRCS += crc.c +#SRCS += crypto.c +#SRCS += dac.c +#SRCS += eadc.c +#SRCS += ebi.c +#SRCS += ecap.c +#SRCS += epwm.c +SRCS += fmc.c +SRCS += gpio.c +#SRCS += i2c.c +#SRCS += i2s.c +#SRCS += pdma.c +#SRCS += qei.c +#SRCS += qspi.c +SRCS += retarget.c +#SRCS += rtc.c +#SRCS += sc.c +#SRCS += scuart.c +#SRCS += sdh.c +#SRCS += spi.c +SRCS += sys.c +#SRCS += timer.c +#SRCS += timer_pwm.c +SRCS += uart.c +#SRCS += usbd.c +#SRCS += usci_i2c.c +#SRCS += usci_spi.c +#SRCS += usci_uart.c +#SRCS += wdt.c +#SRCS += wwdt.c + +ifeq ($(WORLD), secure) +SRCS += clk.c +endif + +ifneq ($(WORLD), secure) +CFLAGS += -DDEBUG_PORT=$(subst ",,$(CONFIG_NONSECURE_DEBUG_UART)) +CFLAGS += -DDEBUG_OUT_PREFIX=$(CONFIG_NONSECURE_FONT_COLOR) +OBJDIR = $(TOPDIR)/build/nonsecure$(subst $(TOPDIR),,$(shell pwd)) +endif + +ifeq ($(WORLD), secure) +CFLAGS += -DDEBUG_PORT=$(subst ",,$(CONFIG_SECURE_DEBUG_UART)) +CFLAGS += -DDEBUG_OUT_PREFIX=$(CONFIG_SECURE_FONT_COLOR) +OBJDIR = $(TOPDIR)/build/secure$(subst $(TOPDIR),,$(shell pwd)) +endif + +OBJS = $(addprefix $(OBJDIR)/, $(SRCS:.c=.o)) + +current_dir = $(subst $(TOPDIR),,$(shell pwd)) + +$(OBJDIR)/%.o : %.c + $(Q) mkdir -p $(OBJDIR)/$(dir $<) + @echo "CC: $<" + $(Q) $(CC) $(CFLAGS) -c -o $@ $^ + +libm2351_StdDriver_s.a: $(OBJS) + $(Q) $(AR) $(OBJDIR)/$@ $(OBJS) + $(Q) cp $(OBJDIR)/$@ $(TOPDIR)/lib/$@ + +libm2351_StdDriver_ns.a: $(OBJS) + $(Q) $(AR) $(OBJDIR)/$@ $(OBJS) + $(Q) cp $(OBJDIR)/$@ $(TOPDIR)/lib/$@ + +clean: + rm -f libm2351_StdDriver.a + diff --git a/arch/riscv32/fe310/src/freedom-metal/metal/atomic.h b/arch/riscv32/fe310/src/freedom-metal/metal/atomic.h new file mode 100644 index 00000000..32a33abd --- /dev/null +++ b/arch/riscv32/fe310/src/freedom-metal/metal/atomic.h @@ -0,0 +1,259 @@ +/* Copyright 2019 SiFive, Inc */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#ifndef METAL__ATOMIC_H +#define METAL__ATOMIC_H + +#include + +#include + +typedef volatile int32_t metal_atomic_t; + +#define METAL_ATOMIC_DECLARE(name) \ + __attribute((section(".data.atomics"))) metal_atomic_t name + +#define _METAL_STORE_AMO_ACCESS_FAULT 7 + +/* This macro stores the memory address in mtval like a normal store/amo access + * fault, triggers a trap, and then if execution returns, returns 0 as an + * arbitrary choice */ +#define _METAL_TRAP_AMO_ACCESS(addr) \ + __asm__("csrw mtval, %[atomic]" ::[atomic] "r"(a)); \ + _metal_trap(_METAL_STORE_AMO_ACCESS_FAULT); \ + return 0; + +/*! + * @brief Check if the platform supports atomic operations + * + * @return 1 if atomic operations are supported, 0 if not + */ +__inline__ int32_t metal_atomic_available(void) { +#ifdef __riscv_atomic + return 1; +#else + return 0; +#endif +} + +/*! + * @brief Atomically increment a metal_atomic_t and return its old value + * + * If atomics are not supported on the platform, this function will trap with + * a Store/AMO access fault. + * + * @param a The pointer to the value to increment + * @param increment the amount to increment the value + * + * @return The previous value of the metal_atomic_t + */ +__inline__ int32_t metal_atomic_add(metal_atomic_t *a, int32_t increment) { +#ifdef __riscv_atomic + int32_t old; + __asm__ volatile("amoadd.w %[old], %[increment], (%[atomic])" + : [old] "=r"(old) + : [increment] "r"(increment), [atomic] "r"(a) + : "memory"); + return old; +#else + _METAL_TRAP_AMO_ACCESS(a); +#endif +} + +/*! + * @brief Atomically bitwise-AND a metal_atomic_t and return its old value + * + * If atomics are not supported on the platform, this function will trap with + * a Store/AMO access fault. + * + * @param a The pointer to the value to bitwise-AND + * @param mask the bitmask to AND + * + * @return The previous value of the metal_atomic_t + */ +__inline__ int32_t metal_atomic_and(metal_atomic_t *a, int32_t mask) { +#ifdef __riscv_atomic + int32_t old; + __asm__ volatile("amoand.w %[old], %[mask], (%[atomic])" + : [old] "=r"(old) + : [mask] "r"(mask), [atomic] "r"(a) + : "memory"); + return old; +#else + _METAL_TRAP_AMO_ACCESS(a); +#endif +} + +/*! + * @brief Atomically bitwise-OR a metal_atomic_t and return its old value + * + * If atomics are not supported on the platform, this function will trap with + * a Store/AMO access fault. + * + * @param a The pointer to the value to bitwise-OR + * @param mask the bitmask to OR + * + * @return The previous value of the metal_atomic_t + */ +__inline__ int32_t metal_atomic_or(metal_atomic_t *a, int32_t mask) { +#ifdef __riscv_atomic + int32_t old; + __asm__ volatile("amoor.w %[old], %[mask], (%[atomic])" + : [old] "=r"(old) + : [mask] "r"(mask), [atomic] "r"(a) + : "memory"); + return old; +#else + _METAL_TRAP_AMO_ACCESS(a); +#endif +} + +/*! + * @brief Atomically swap a metal_atomic_t and return its old value + * + * If atomics are not supported on the platform, this function will trap with + * a Store/AMO access fault. + * + * @param a The pointer to the value to swap + * @param new_value the value to store in the metal_atomic_t + * + * @return The previous value of the metal_atomic_t + */ +__inline__ int32_t metal_atomic_swap(metal_atomic_t *a, int32_t new_value) { +#ifdef __riscv_atomic + int32_t old; + __asm__ volatile("amoswap.w %[old], %[newval], (%[atomic])" + : [old] "=r"(old) + : [newval] "r"(new_value), [atomic] "r"(a) + : "memory"); + return old; +#else + _METAL_TRAP_AMO_ACCESS(a); +#endif +} + +/*! + * @brief Atomically bitwise-XOR a metal_atomic_t and return its old value + * + * If atomics are not supported on the platform, this function will trap with + * a Store/AMO access fault. + * + * @param a The pointer to the value to bitwise-XOR + * @param mask the bitmask to XOR + * + * @return The previous value of the metal_atomic_t + */ +__inline__ int32_t metal_atomic_xor(metal_atomic_t *a, int32_t mask) { +#ifdef __riscv_atomic + int32_t old; + __asm__ volatile("amoxor.w %[old], %[mask], (%[atomic])" + : [old] "=r"(old) + : [mask] "r"(mask), [atomic] "r"(a) + : "memory"); + return old; +#else + _METAL_TRAP_AMO_ACCESS(a); +#endif +} + +/*! + * @brief Atomically set the value of a memory location to the greater of + * its current value or a value to compare it with. + * + * If atomics are not supported on the platform, this function will trap with + * a Store/AMO access fault. + * + * @param a The pointer to the value to swap + * @param compare the value to compare with the value in memory + * + * @return The previous value of the metal_atomic_t + */ +__inline__ int32_t metal_atomic_max(metal_atomic_t *a, int32_t compare) { +#ifdef __riscv_atomic + int32_t old; + __asm__ volatile("amomax.w %[old], %[compare], (%[atomic])" + : [old] "=r"(old) + : [compare] "r"(compare), [atomic] "r"(a) + : "memory"); + return old; +#else + _METAL_TRAP_AMO_ACCESS(a); +#endif +} + +/*! + * @brief Atomically set the value of a memory location to the (unsigned) + * greater of its current value or a value to compare it with. + * + * If atomics are not supported on the platform, this function will trap with + * a Store/AMO access fault. + * + * @param a The pointer to the value to swap + * @param compare the value to compare with the value in memory + * + * @return The previous value of the metal_atomic_t + */ +__inline__ uint32_t metal_atomic_max_u(metal_atomic_t *a, uint32_t compare) { +#ifdef __riscv_atomic + int32_t old; + __asm__ volatile("amomaxu.w %[old], %[compare], (%[atomic])" + : [old] "=r"(old) + : [compare] "r"(compare), [atomic] "r"(a) + : "memory"); + return old; +#else + _METAL_TRAP_AMO_ACCESS(a); +#endif +} + +/*! + * @brief Atomically set the value of a memory location to the lesser of + * its current value or a value to compare it with. + * + * If atomics are not supported on the platform, this function will trap with + * a Store/AMO access fault. + * + * @param a The pointer to the value to swap + * @param compare the value to compare with the value in memory + * + * @return The previous value of the metal_atomic_t + */ +__inline__ int32_t metal_atomic_min(metal_atomic_t *a, int32_t compare) { +#ifdef __riscv_atomic + int32_t old; + __asm__ volatile("amomin.w %[old], %[compare], (%[atomic])" + : [old] "=r"(old) + : [compare] "r"(compare), [atomic] "r"(a) + : "memory"); + return old; +#else + _METAL_TRAP_AMO_ACCESS(a); +#endif +} + +/*! + * @brief Atomically set the value of a memory location to the (unsigned) lesser + * of its current value or a value to compare it with. + * + * If atomics are not supported on the platform, this function will trap with + * a Store/AMO access fault. + * + * @param a The pointer to the value to swap + * @param compare the value to compare with the value in memory + * + * @return The previous value of the metal_atomic_t + */ +__inline__ uint32_t metal_atomic_min_u(metal_atomic_t *a, uint32_t compare) { +#ifdef __riscv_atomic + int32_t old; + __asm__ volatile("amominu.w %[old], %[compare], (%[atomic])" + : [old] "=r"(old) + : [compare] "r"(compare), [atomic] "r"(a) + : "memory"); + return old; +#else + _METAL_TRAP_AMO_ACCESS(a); +#endif +} + +#endif /* METAL__ATOMIC_H */ diff --git a/arch/riscv32/fe310/src/freedom-metal/metal/button.h b/arch/riscv32/fe310/src/freedom-metal/metal/button.h new file mode 100644 index 00000000..bef64596 --- /dev/null +++ b/arch/riscv32/fe310/src/freedom-metal/metal/button.h @@ -0,0 +1,63 @@ +/* Copyright 2018 SiFive, Inc */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#ifndef METAL__BUTTON_H +#define METAL__BUTTON_H + +/*! + * @file button.h + * API for interfacing with physical buttons + */ + +#include + +struct metal_button; + +struct metal_button_vtable { + int (*button_exist)(struct metal_button *button, char *label); + struct metal_interrupt *(*interrupt_controller)( + struct metal_button *button); + int (*get_interrupt_id)(struct metal_button *button); +}; + +/*! + * @brief A button device handle + * + * A `struct metal_button` is an implementation-defined object which represents + * a button on a development board. + */ +struct metal_button { + const struct metal_button_vtable *vtable; +}; + +/*! + * @brief Get a reference to a button + * + * @param label The DeviceTree label for the button + * @return A handle for the button + */ +struct metal_button *metal_button_get(char *label); + +/*! + * @brief Get the interrupt controller for a button + * + * @param button The handle for the button + * @return A pointer to the interrupt controller responsible for handling + * button interrupts. + */ +__inline__ struct metal_interrupt * +metal_button_interrupt_controller(struct metal_button *button) { + return button->vtable->interrupt_controller(button); +} + +/*! + * @brief Get the interrupt id for a button + * + * @param button The handle for the button + * @return The interrupt id corresponding to a button. + */ +__inline__ int metal_button_get_interrupt_id(struct metal_button *button) { + return button->vtable->get_interrupt_id(button); +} + +#endif diff --git a/arch/riscv32/fe310/src/freedom-metal/metal/cache.h b/arch/riscv32/fe310/src/freedom-metal/metal/cache.h new file mode 100644 index 00000000..673a8b13 --- /dev/null +++ b/arch/riscv32/fe310/src/freedom-metal/metal/cache.h @@ -0,0 +1,108 @@ +/* Copyright 2020 SiFive, Inc */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#ifndef METAL__CACHE_H +#define METAL__CACHE_H + +/*! + * @file cache.h + * + * @brief API for configuring caches + */ +#include + +/*! + * @brief a handle for a cache + * Note: To be deprecated in next release. + */ +struct metal_cache { + uint8_t __no_empty_structs; +}; + +/*! + * @brief Initialize L2 cache controller. + * Enables all available cache ways. + * @param None + * @return 0 If no error + */ +int metal_l2cache_init(void); + +/*! + * @brief Get the current number of enabled L2 cache ways + * @param None + * @return The current number of enabled L2 cache ways + */ +int metal_l2cache_get_enabled_ways(void); + +/*! + * @brief Enable the requested number of L2 cache ways + * @param ways Number of ways to enable + * @return 0 if the ways are successfully enabled + */ +int metal_l2cache_set_enabled_ways(int ways); + +/*! + * @brief Initialize a cache + * @param cache The handle for the cache to initialize + * @param ways The number of ways to enable + * + * Initializes a cache with the requested number of ways enabled. + * Note: API to be deprecated in next release. + */ +__inline__ void metal_cache_init(struct metal_cache *cache, int ways) { + metal_l2cache_init(); +} + +/*! + * @brief Get the current number of enabled cache ways + * @param cache The handle for the cache + * @return The current number of enabled cache ways + * Note: API to be deprecated in next release. + */ +__inline__ int metal_cache_get_enabled_ways(struct metal_cache *cache) { + return metal_l2cache_get_enabled_ways(); +} + +/*! + * @brief Enable the requested number of cache ways + * @param cache The handle for the cache + * @param ways The number of ways to enabled + * @return 0 if the ways are successfully enabled + * Note: API to be deprecated in next release. + */ +__inline__ int metal_cache_set_enabled_ways(struct metal_cache *cache, + int ways) { + return metal_l2cache_set_enabled_ways(ways); +} + +/*! + * @brief Check if dcache is supported on the core + * @param hartid The core to check + * @return 1 if dcache is present + */ +int metal_dcache_l1_available(int hartid); + +/*! + * @brief Flush dcache for L1 on the requested core with write back + * @param hartid The core to flush + * @param address The virtual address of cacheline to invalidate + * @return None + */ +void metal_dcache_l1_flush(int hartid, uintptr_t address); + +/*! + * @brief Discard dcache for L1 on the requested core with no write back + * @param hartid The core to discard + * @param address The virtual address of cacheline to invalidate + * @return None + */ +void metal_dcache_l1_discard(int hartid, uintptr_t address); + +/*! + * @brief Check if icache is supported on the core + * @param hartid The core to check + * @return 1 if icache is present + */ +int metal_icache_l1_available(int hartid); + +#endif diff --git a/arch/riscv32/fe310/src/freedom-metal/metal/clock.h b/arch/riscv32/fe310/src/freedom-metal/metal/clock.h new file mode 100644 index 00000000..cfe29f6b --- /dev/null +++ b/arch/riscv32/fe310/src/freedom-metal/metal/clock.h @@ -0,0 +1,164 @@ +/* Copyright 2018 SiFive, Inc */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#ifndef METAL__CLOCK_H +#define METAL__CLOCK_H + +/*! + * @file clock.h + * @brief API for manipulating clock sources + * + * The clock interface allows for controlling the rate of various clocks in the + * system. + */ + +struct metal_clock; + +#include + +/* The generic interface to all clocks. */ +struct __metal_clock_vtable { + long (*get_rate_hz)(const struct metal_clock *clk); + long (*set_rate_hz)(struct metal_clock *clk, long hz); +}; + +/*! + * @brief Function signature of clock rate change callbacks + */ +typedef void (*metal_clock_rate_change_callback)(void *priv); + +struct _metal_clock_callback_t; +struct _metal_clock_callback_t { + /* The callback function */ + metal_clock_rate_change_callback callback; + + /* Private data for the callback function */ + void *priv; + + struct _metal_clock_callback_t *_next; +}; + +/*! + * @brief Type for the linked list of callbacks for clock rate changes + */ +typedef struct _metal_clock_callback_t metal_clock_callback; + +/*! + * @brief Call all callbacks in the linked list, if any are registered + */ +__inline__ void +_metal_clock_call_all_callbacks(const metal_clock_callback *const list) { + const metal_clock_callback *current = list; + while (current) { + current->callback(current->priv); + current = current->_next; + } +} + +/*! + * @brief Append a callback to the linked list and return the head of the list + */ +__inline__ metal_clock_callback * +_metal_clock_append_to_callbacks(metal_clock_callback *list, + metal_clock_callback *const cb) { + cb->_next = NULL; + + if (!list) { + return cb; + } + + metal_clock_callback *current = list; + + while ((current->_next) != NULL) { + current = current->_next; + } + + current->_next = cb; + + return list; +} + +/*! + * @struct metal_clock + * @brief The handle for a clock + * + * Clocks are defined as a pointer to a `struct metal_clock`, the contents of + * which are implementation defined. Users of the clock interface must call + * functions which accept a `struct metal_clock *` as an argument to interract + * with the clock. + * + * Note that no mechanism for obtaining a pointer to a `struct metal_clock` has + * been defined, making it impossible to call any of these functions without + * invoking implementation-defined behavior. + */ +struct metal_clock { + const struct __metal_clock_vtable *vtable; + + /* Pre-rate change callback linked list */ + metal_clock_callback *_pre_rate_change_callback; + + /* Post-rate change callback linked list */ + metal_clock_callback *_post_rate_change_callback; +}; + +/*! + * @brief Returns the current rate of the given clock + * + * @param clk The handle for the clock + * @return The current rate of the clock in Hz + */ +__inline__ long metal_clock_get_rate_hz(const struct metal_clock *clk) { + return clk->vtable->get_rate_hz(clk); +} + +/*! + * @brief Set the current rate of a clock + * + * @param clk The handle for the clock + * @param hz The desired rate in Hz + * @return The new rate of the clock in Hz. + * + * Attempts to set the current rate of the given clock to as close as possible + * to the given rate in Hz. Returns the actual value that's been selected, which + * could be anything! + * + * Prior to and after the rate change of the clock, this will call the + * registered pre- and post-rate change callbacks. + */ +__inline__ long metal_clock_set_rate_hz(struct metal_clock *clk, long hz) { + _metal_clock_call_all_callbacks(clk->_pre_rate_change_callback); + + long out = clk->vtable->set_rate_hz(clk, hz); + + _metal_clock_call_all_callbacks(clk->_post_rate_change_callback); + + return out; +} + +/*! + * @brief Register a callback that must be called before a rate change + * + * @param clk The handle for the clock + * @param cb The callback to be registered + */ +__inline__ void +metal_clock_register_pre_rate_change_callback(struct metal_clock *clk, + metal_clock_callback *cb) { + clk->_pre_rate_change_callback = + _metal_clock_append_to_callbacks(clk->_pre_rate_change_callback, cb); +} + +/*! + * @brief Registers a callback that must be called after a rate change + * + * @param clk The handle for the clock + * @param cb The callback to be registered + */ +__inline__ void +metal_clock_register_post_rate_change_callback(struct metal_clock *clk, + metal_clock_callback *cb) { + clk->_post_rate_change_callback = + _metal_clock_append_to_callbacks(clk->_post_rate_change_callback, cb); +} + +#endif diff --git a/arch/riscv32/fe310/src/freedom-metal/metal/compiler.h b/arch/riscv32/fe310/src/freedom-metal/metal/compiler.h new file mode 100644 index 00000000..80ca5fee --- /dev/null +++ b/arch/riscv32/fe310/src/freedom-metal/metal/compiler.h @@ -0,0 +1,23 @@ +/* Copyright 2018 SiFive, Inc */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#ifndef METAL__COMPILER_H +#define METAL__COMPILER_H + +#define __METAL_DECLARE_VTABLE(type) extern const struct type type; + +#define __METAL_DEFINE_VTABLE(type) const struct type type + +#define __METAL_GET_FIELD(reg, mask) \ + (((reg) & (mask)) / ((mask) & ~((mask) << 1))) + +/* Set field with mask for a given value */ +#define __METAL_SET_FIELD(reg, mask, val) \ + (((reg) & ~(mask)) | (((val) * ((mask) & ~((mask) << 1))) & (mask))) + +#define __METAL_MIN(a, b) ((a) < (b) ? (a) : (b)) +#define __METAL_MAX(a, b) ((a) > (b) ? (a) : (b)) + +void _metal_trap(int ecode); + +#endif diff --git a/arch/riscv32/fe310/src/freedom-metal/metal/cpu.h b/arch/riscv32/fe310/src/freedom-metal/metal/cpu.h new file mode 100644 index 00000000..98d7e668 --- /dev/null +++ b/arch/riscv32/fe310/src/freedom-metal/metal/cpu.h @@ -0,0 +1,307 @@ +/* Copyright 2018 SiFive, Inc */ + +/* SPDX-License-Identifier: Apache-2.0 */ + +/*! @file cpu.h + * @brief API for accessing CPU capabilities. + */ + +#ifndef METAL__CPU_H +#define METAL__CPU_H + +#include +#include + +struct metal_cpu; + +/*! + * @brief Function signature for exception handlers + */ +typedef void (*metal_exception_handler_t)(struct metal_cpu *cpu, int ecode); + +struct metal_cpu_vtable { + unsigned long long (*mcycle_get)(struct metal_cpu *cpu); + unsigned long long (*timebase_get)(struct metal_cpu *cpu); + unsigned long long (*mtime_get)(struct metal_cpu *cpu); + int (*mtimecmp_set)(struct metal_cpu *cpu, unsigned long long time); + struct metal_interrupt *(*tmr_controller_interrupt)(struct metal_cpu *cpu); + int (*get_tmr_interrupt_id)(struct metal_cpu *cpu); + struct metal_interrupt *(*sw_controller_interrupt)(struct metal_cpu *cpu); + int (*get_sw_interrupt_id)(struct metal_cpu *cpu); + int (*set_sw_ipi)(struct metal_cpu *cpu, int hartid); + int (*clear_sw_ipi)(struct metal_cpu *cpu, int hartid); + int (*get_msip)(struct metal_cpu *cpu, int hartid); + struct metal_interrupt *(*controller_interrupt)(struct metal_cpu *cpu); + int (*exception_register)(struct metal_cpu *cpu, int ecode, + metal_exception_handler_t handler); + int (*get_ilen)(struct metal_cpu *cpu, uintptr_t epc); + uintptr_t (*get_epc)(struct metal_cpu *cpu); + int (*set_epc)(struct metal_cpu *cpu, uintptr_t epc); + struct metal_buserror *(*get_buserror)(struct metal_cpu *cpu); +}; + +/*! @brief A device handle for a CPU hart + */ +struct metal_cpu { + const struct metal_cpu_vtable *vtable; +}; + +/*! @brief Get a reference to a CPU hart + * + * @param hartid The ID of the desired CPU hart + * @return A pointer to the CPU device handle + */ +struct metal_cpu *metal_cpu_get(unsigned int hartid); + +/*! @brief Get the hartid of the CPU hart executing this function + * + * @return The hartid of the current CPU hart */ +int metal_cpu_get_current_hartid(void); + +/*! @brief Get the number of CPU harts + * + * @return The number of CPU harts */ +int metal_cpu_get_num_harts(void); + +/*! @brief Get the CPU cycle count timer value + * + * Get the value of the cycle count timer for a given CPU + * + * @param cpu The CPU device handle + * @return The value of the CPU cycle count timer + */ +__inline__ unsigned long long metal_cpu_get_timer(struct metal_cpu *cpu) { + return cpu->vtable->mcycle_get(cpu); +} + +/*! @brief Get the timebase of the CPU + * + * Get the value of the timebase of the cycle count timer + * + * @param cpu The CPU device handle + * @return The value of the cycle count timer timebase + */ +__inline__ unsigned long long metal_cpu_get_timebase(struct metal_cpu *cpu) { + return cpu->vtable->timebase_get(cpu); +} + +/*! @brief Get the value of the mtime RTC + * + * Get the value of the mtime real-time clock. The CPU interrupt controller + * must be initialized before this function is called or the return value + * will be 0. + * + * @param cpu The CPU device handle + * @return The value of mtime, or 0 if failure + */ +__inline__ unsigned long long metal_cpu_get_mtime(struct metal_cpu *cpu) { + return cpu->vtable->mtime_get(cpu); +} + +/*! @brief Set the value of the RTC mtimecmp RTC + * + * Set the value of the mtime real-time clock compare register. The CPU + * interrupt controller must be initialized before this function is called + * or the return value will be -1; + * + * @param cpu The CPU device handle + * @param time The value to set the compare register to + * @return The value of mtimecmp or -1 if error + */ +__inline__ int metal_cpu_set_mtimecmp(struct metal_cpu *cpu, + unsigned long long time) { + return cpu->vtable->mtimecmp_set(cpu, time); +} + +/*! @brief Get a reference to RTC timer interrupt controller + * + * Get a reference to the interrupt controller for the real-time clock + * interrupt. The controller returned by this function must be initialized + * before any interrupts are registered or enabled with it. + * + * @param cpu The CPU device handle + * @return A pointer to the timer interrupt handle + */ +__inline__ struct metal_interrupt * +metal_cpu_timer_interrupt_controller(struct metal_cpu *cpu) { + return cpu->vtable->tmr_controller_interrupt(cpu); +} + +/*! @brief Get the RTC timer interrupt id + * + * Get the interrupt ID of the real-time clock interrupt + * + * @param cpu The CPU device handle + * @return The timer interrupt ID + */ +__inline__ int metal_cpu_timer_get_interrupt_id(struct metal_cpu *cpu) { + return cpu->vtable->get_tmr_interrupt_id(cpu); +} + +/*! @brief Get a reference to the software interrupt controller + * + * Get a reference to the interrupt controller for the software/inter-process + * interrupt. The controller returned by this function must be initialized + * before any interrupts are registered or enabled with it. + * + * @param cpu The CPU device handle + * @return A pointer to the software interrupt handle + */ +__inline__ struct metal_interrupt * +metal_cpu_software_interrupt_controller(struct metal_cpu *cpu) { + return cpu->vtable->sw_controller_interrupt(cpu); +} + +/*! @brief Get the software interrupt id + * + * Get the interrupt ID for the software/inter-process interrupt + * + * @param cpu The CPU device handle + * @return the software interrupt ID + */ +__inline__ int metal_cpu_software_get_interrupt_id(struct metal_cpu *cpu) { + return cpu->vtable->get_sw_interrupt_id(cpu); +} + +/*! + * @brief Set the inter-process interrupt for a hart + * + * Trigger a software/inter-process interrupt for a hart. The CPU interrupt + * controller for the CPU handle passed to this function must be initialized + * before this function is called. + * + * @param cpu The CPU device handle + * @param hartid The CPU hart ID to be interrupted + * @return 0 upon success + */ +__inline__ int metal_cpu_software_set_ipi(struct metal_cpu *cpu, int hartid) { + return cpu->vtable->set_sw_ipi(cpu, hartid); +} + +/*! + * @brief Clear the inter-process interrupt for a hart + * + * Clear the software/inter-process interrupt for a hart. The CPU interrupt + * controller for the CPU handle passed to this function must be initialized + * before this function is called. + * + * @param cpu The CPU device handle + * @param hartid The CPU hart ID to clear + * @return 0 upon success + */ +__inline__ int metal_cpu_software_clear_ipi(struct metal_cpu *cpu, int hartid) { + return cpu->vtable->clear_sw_ipi(cpu, hartid); +} + +/*! + * @brief Get the value of MSIP for the given hart + * + * Get the value of the machine software interrupt pending bit for + * the given hart. The CPU interrupt controller for the CPU handle passed + * as argument to this function must be initialized before this function + * is called. + * + * @param cpu the CPU device handle + * @param hartid The CPU hart to read + * @return 0 upon success + */ +__inline__ int metal_cpu_get_msip(struct metal_cpu *cpu, int hartid) { + return cpu->vtable->get_msip(cpu, hartid); +} + +/*! + * @brief Get the interrupt controller for the CPU + * + * Get the CPU interrupt controller. The controller returned by this + * function must be initialized before any interrupts are registered + * or enabled and before any exception handlers are registered with + * this CPU. + * + * @param cpu The CPU device handle + * @return The handle for the CPU interrupt controller + */ +__inline__ struct metal_interrupt * +metal_cpu_interrupt_controller(struct metal_cpu *cpu) { + return cpu->vtable->controller_interrupt(cpu); +} + +/*! + * @brief Register an exception handler + * + * Register an exception handler for the CPU. The CPU interrupt controller must + * be initialized before this function is called. + * + * @param cpu The CPU device handle + * @param ecode The exception code to register a handler for + * @param handler Callback function for the exception handler + * @return 0 upon success + */ +__inline__ int metal_cpu_exception_register(struct metal_cpu *cpu, int ecode, + metal_exception_handler_t handler) { + return cpu->vtable->exception_register(cpu, ecode, handler); +} + +/*! + * @brief Get the length of an instruction in bytes + * + * Get the length of an instruction in bytes. + * + * On RISC-V platforms, this is useful for detecting whether an instruction is + * compressed (2 bytes long) or uncompressed (4 bytes long). + * + * This function is useful in conjuction with `metal_cpu_get_exception_pc()` + * and `metal_cpu_set_exception_pc()` in order to cause the exception handler to + * return execution after the faulting instruction. + * + * @param cpu The CPU device handle + * @param epc The address of the instruction to measure + * @return the length of the instruction in bytes + */ +__inline__ int metal_cpu_get_instruction_length(struct metal_cpu *cpu, + uintptr_t epc) { + return cpu->vtable->get_ilen(cpu, epc); +} + +/*! + * @brief Get the program counter of the current exception. + * + * This function must be called within an exception handler. The behavior is + * undefined outside of an exception handler. + * + * @param cpu The CPU device handle + * @return The value of the program counter at the time of the exception + */ +__inline__ uintptr_t metal_cpu_get_exception_pc(struct metal_cpu *cpu) { + return cpu->vtable->get_epc(cpu); +} + +/*! + * @brief Set the exception program counter + * + * This function must be called within an exception handler. The behavior + * is undefined outside of an exception handler. + * + * This function can be used to cause an exception handler to return execution + * to an address other than the one that caused the exception. + * + * @param cpu the CPU device handle + * @param epc The address to set the exception program counter to + * @return 0 upon success + */ +__inline__ int metal_cpu_set_exception_pc(struct metal_cpu *cpu, + uintptr_t epc) { + return cpu->vtable->set_epc(cpu, epc); +} + +/*! + * @brief Get the handle for the hart's bus error unit + * + * @param cpu The CPU device handle + * @return A pointer to the bus error unit handle + */ +__inline__ struct metal_buserror * +metal_cpu_get_buserror(struct metal_cpu *cpu) { + return cpu->vtable->get_buserror(cpu); +} + +#endif diff --git a/arch/riscv32/fe310/src/freedom-metal/metal/csr.h b/arch/riscv32/fe310/src/freedom-metal/metal/csr.h new file mode 100644 index 00000000..8375d8a4 --- /dev/null +++ b/arch/riscv32/fe310/src/freedom-metal/metal/csr.h @@ -0,0 +1,32 @@ +/* Copyright 2019 SiFive, Inc */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#ifndef METAL__CSR_H +#define METAL__CSR_H + +#include +#include +#include + +/*! + * @file csr.h + * @brief A collection of APIs for get and set CSR registers + */ + +/*! + * @brief Read a given CSR register without checking validity of CSR offset + * @param crs Register label or hex value offset to read from + * @param value Variable name of uintprt_t type to get the value + */ +#define METAL_CPU_GET_CSR(reg, value) \ + __asm__ volatile("csrr %0, " #reg : "=r"(value)); + +/*! + * @brief Write to a given CSR register without checking validity of CSR offset + * @param crs Register label or hex value offset to write to + * @param value Variable name of uintprt_t type to set the value + */ +#define METAL_CPU_SET_CSR(reg, value) \ + __asm__ volatile("csrw " #reg ", %0" : : "r"(value)); + +#endif // METAL__CSR_H diff --git a/arch/riscv32/fe310/src/freedom-metal/metal/drivers/fixed-clock.h b/arch/riscv32/fe310/src/freedom-metal/metal/drivers/fixed-clock.h new file mode 100644 index 00000000..b25f5414 --- /dev/null +++ b/arch/riscv32/fe310/src/freedom-metal/metal/drivers/fixed-clock.h @@ -0,0 +1,22 @@ +/* Copyright 2018 SiFive, Inc */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#ifndef METAL__DRIVERS__FIXED_CLOCK_H +#define METAL__DRIVERS__FIXED_CLOCK_H + +struct __metal_driver_fixed_clock; + +#include +#include + +struct __metal_driver_vtable_fixed_clock { + struct __metal_clock_vtable clock; +}; + +__METAL_DECLARE_VTABLE(__metal_driver_vtable_fixed_clock) + +struct __metal_driver_fixed_clock { + struct metal_clock clock; +}; + +#endif diff --git a/arch/riscv32/fe310/src/freedom-metal/metal/drivers/fixed-factor-clock.h b/arch/riscv32/fe310/src/freedom-metal/metal/drivers/fixed-factor-clock.h new file mode 100644 index 00000000..84e4fd58 --- /dev/null +++ b/arch/riscv32/fe310/src/freedom-metal/metal/drivers/fixed-factor-clock.h @@ -0,0 +1,22 @@ +/* Copyright 2018 SiFive, Inc */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#ifndef METAL__DRIVERS__FIXED_FACTOR_CLOCK_H +#define METAL__DRIVERS__FIXED_FACTOR_CLOCK_H + +struct __metal_driver_fixed_factor_clock; + +#include +#include + +struct __metal_driver_vtable_fixed_factor_clock { + struct __metal_clock_vtable clock; +}; + +__METAL_DECLARE_VTABLE(__metal_driver_vtable_fixed_factor_clock) + +struct __metal_driver_fixed_factor_clock { + struct metal_clock clock; +}; + +#endif diff --git a/arch/riscv32/fe310/src/freedom-metal/metal/drivers/riscv_clint0.h b/arch/riscv32/fe310/src/freedom-metal/metal/drivers/riscv_clint0.h new file mode 100644 index 00000000..ceda473e --- /dev/null +++ b/arch/riscv32/fe310/src/freedom-metal/metal/drivers/riscv_clint0.h @@ -0,0 +1,27 @@ +/* Copyright 2018 SiFive, Inc */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#ifndef METAL__DRIVERS__RISCV_CLINT0_H +#define METAL__DRIVERS__RISCV_CLINT0_H + +#include +#include + +struct __metal_driver_vtable_riscv_clint0 { + struct metal_interrupt_vtable clint_vtable; +}; + +__METAL_DECLARE_VTABLE(__metal_driver_vtable_riscv_clint0) + +#define __METAL_MACHINE_MACROS +#include +struct __metal_driver_riscv_clint0 { + struct metal_interrupt controller; + int init_done; +}; +#undef __METAL_MACHINE_MACROS + +int __metal_driver_riscv_clint0_command_request( + struct metal_interrupt *controller, int command, void *data); + +#endif diff --git a/arch/riscv32/fe310/src/freedom-metal/metal/drivers/riscv_cpu.h b/arch/riscv32/fe310/src/freedom-metal/metal/drivers/riscv_cpu.h new file mode 100644 index 00000000..f3005f01 --- /dev/null +++ b/arch/riscv32/fe310/src/freedom-metal/metal/drivers/riscv_cpu.h @@ -0,0 +1,196 @@ +/* Copyright 2018 SiFive, Inc */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#ifndef METAL__DRIVERS__RISCV_CPU_H +#define METAL__DRIVERS__RISCV_CPU_H + +#include +#include +#include + +#define METAL_MAX_CORES 8 +#define METAL_MAX_MI 32 /* Per ISA MCause interrupts 32+ are Reserved */ +#define METAL_MAX_ME 12 /* Per ISA Exception codes 12+ are Reserved */ +#define METAL_DEFAULT_RTC_FREQ 32768 + +#define METAL_DISABLE 0 +#define METAL_ENABLE 1 + +#define METAL_ISA_A_EXTENSIONS 0x0001 +#define METAL_ISA_C_EXTENSIONS 0x0004 +#define METAL_ISA_D_EXTENSIONS 0x0008 +#define METAL_ISA_E_EXTENSIONS 0x0010 +#define METAL_ISA_F_EXTENSIONS 0x0020 +#define METAL_ISA_G_EXTENSIONS 0x0040 +#define METAL_ISA_I_EXTENSIONS 0x0100 +#define METAL_ISA_M_EXTENSIONS 0x1000 +#define METAL_ISA_N_EXTENSIONS 0x2000 +#define METAL_ISA_Q_EXTENSIONS 0x10000 +#define METAL_ISA_S_EXTENSIONS 0x40000 +#define METAL_ISA_U_EXTENSIONS 0x100000 +#define METAL_ISA_V_EXTENSIONS 0x200000 +#define METAL_ISA_XL32_EXTENSIONS 0x40000000UL +#define METAL_ISA_XL64_EXTENSIONS 0x8000000000000000UL +#define METAL_ISA_XL128_EXTENSIONS 0xC000000000000000UL + +#define METAL_MTVEC_DIRECT 0x00 +#define METAL_MTVEC_VECTORED 0x01 +#define METAL_MTVEC_CLIC 0x02 +#define METAL_MTVEC_CLIC_VECTORED 0x03 +#define METAL_MTVEC_CLIC_RESERVED 0x3C +#define METAL_MTVEC_MASK 0x3F +#if __riscv_xlen == 32 +#define METAL_MCAUSE_INTR 0x80000000UL +#define METAL_MCAUSE_CAUSE 0x000003FFUL +#else +#define METAL_MCAUSE_INTR 0x8000000000000000UL +#define METAL_MCAUSE_CAUSE 0x00000000000003FFUL +#endif +#define METAL_MCAUSE_MINHV 0x40000000UL +#define METAL_MCAUSE_MPP 0x30000000UL +#define METAL_MCAUSE_MPIE 0x08000000UL +#define METAL_MCAUSE_MPIL 0x00FF0000UL +#define METAL_MSTATUS_MIE 0x00000008UL +#define METAL_MSTATUS_MPIE 0x00000080UL +#define METAL_MSTATUS_MPP 0x00001800UL +#define METAL_MSTATUS_FS_INIT 0x00002000UL +#define METAL_MSTATUS_FS_CLEAN 0x00004000UL +#define METAL_MSTATUS_FS_DIRTY 0x00006000UL +#define METAL_MSTATUS_MPRV 0x00020000UL +#define METAL_MSTATUS_MXR 0x00080000UL +#define METAL_MINTSTATUS_MIL 0xFF000000UL +#define METAL_MINTSTATUS_SIL 0x0000FF00UL +#define METAL_MINTSTATUS_UIL 0x000000FFUL + +#define METAL_LOCAL_INTR(X) (16 + X) +#define METAL_MCAUSE_EVAL(cause) (cause & METAL_MCAUSE_INTR) +#define METAL_INTERRUPT(cause) (METAL_MCAUSE_EVAL(cause) ? 1 : 0) +#define METAL_EXCEPTION(cause) (METAL_MCAUSE_EVAL(cause) ? 0 : 1) +#define METAL_SW_INTR_EXCEPTION (METAL_MCAUSE_INTR + 3) +#define METAL_TMR_INTR_EXCEPTION (METAL_MCAUSE_INTR + 7) +#define METAL_EXT_INTR_EXCEPTION (METAL_MCAUSE_INTR + 11) +#define METAL_LOCAL_INTR_EXCEPTION(X) (METAL_MCAUSE_INTR + METAL_LOCAL_INTR(X)) +#define METAL_LOCAL_INTR_RESERVE0 1 +#define METAL_LOCAL_INTR_RESERVE1 2 +#define METAL_LOCAL_INTR_RESERVE2 4 +#define METAL_LOCAL_INTERRUPT_SW 8 /* Bit3 0x008 */ +#define METAL_LOCAL_INTR_RESERVE4 16 +#define METAL_LOCAL_INTR_RESERVE5 32 +#define METAL_LOCAL_INTR_RESERVE6 64 +#define METAL_LOCAL_INTERRUPT_TMR 128 /* Bit7 0x080 */ +#define METAL_LOCAL_INTR_RESERVE8 256 +#define METAL_LOCAL_INTR_RESERVE9 512 +#define METAL_LOCAL_INTR_RESERVE10 1024 +#define METAL_LOCAL_INTERRUPT_EXT 2048 /* Bit11 0x800 */ +/* Bit12 to Bit15 are Reserved */ +#define METAL_LOCAL_INTERRUPT(X) \ + (0x10000 << X) /* Bit16+ Start of Custom Local Interrupt */ +#define METAL_MIE_INTERRUPT METAL_MSTATUS_MIE + +#define METAL_INSN_LENGTH_MASK 3 +#define METAL_INSN_NOT_COMPRESSED 3 + +typedef enum { + METAL_MACHINE_PRIVILEGE_MODE, + METAL_SUPERVISOR_PRIVILEGE_MODE, + METAL_USER_PRIVILEGE_MODE, +} metal_privilege_mode_e; + +typedef enum { + METAL_INTERRUPT_ID_BASE, + METAL_INTERRUPT_ID_SW = (METAL_INTERRUPT_ID_BASE + 3), + METAL_INTERRUPT_ID_TMR = (METAL_INTERRUPT_ID_BASE + 7), + METAL_INTERRUPT_ID_EXT = (METAL_INTERRUPT_ID_BASE + 11), + METAL_INTERRUPT_ID_CSW = (METAL_INTERRUPT_ID_BASE + 12), + METAL_INTERRUPT_ID_LC0 = (METAL_INTERRUPT_ID_BASE + METAL_LOCAL_INTR(0)), + METAL_INTERRUPT_ID_LC1 = (METAL_INTERRUPT_ID_BASE + METAL_LOCAL_INTR(1)), + METAL_INTERRUPT_ID_LC2 = (METAL_INTERRUPT_ID_BASE + METAL_LOCAL_INTR(2)), + METAL_INTERRUPT_ID_LC3 = (METAL_INTERRUPT_ID_BASE + METAL_LOCAL_INTR(3)), + METAL_INTERRUPT_ID_LC4 = (METAL_INTERRUPT_ID_BASE + METAL_LOCAL_INTR(4)), + METAL_INTERRUPT_ID_LC5 = (METAL_INTERRUPT_ID_BASE + METAL_LOCAL_INTR(5)), + METAL_INTERRUPT_ID_LC6 = (METAL_INTERRUPT_ID_BASE + METAL_LOCAL_INTR(6)), + METAL_INTERRUPT_ID_LC7 = (METAL_INTERRUPT_ID_BASE + METAL_LOCAL_INTR(7)), + METAL_INTERRUPT_ID_LC8 = (METAL_INTERRUPT_ID_BASE + METAL_LOCAL_INTR(8)), + METAL_INTERRUPT_ID_LC9 = (METAL_INTERRUPT_ID_BASE + METAL_LOCAL_INTR(9)), + METAL_INTERRUPT_ID_LC10 = (METAL_INTERRUPT_ID_BASE + METAL_LOCAL_INTR(10)), + METAL_INTERRUPT_ID_LC11 = (METAL_INTERRUPT_ID_BASE + METAL_LOCAL_INTR(11)), + METAL_INTERRUPT_ID_LC12 = (METAL_INTERRUPT_ID_BASE + METAL_LOCAL_INTR(12)), + METAL_INTERRUPT_ID_LC13 = (METAL_INTERRUPT_ID_BASE + METAL_LOCAL_INTR(13)), + METAL_INTERRUPT_ID_LC14 = (METAL_INTERRUPT_ID_BASE + METAL_LOCAL_INTR(14)), + METAL_INTERRUPT_ID_LC15 = (METAL_INTERRUPT_ID_BASE + METAL_LOCAL_INTR(15)), + METAL_INTERRUPT_ID_LCMX, + METAL_INTERRUPT_ID_GL0 = METAL_INTERRUPT_ID_LCMX, + METAL_INTERRUPT_ID_GLMX = (METAL_MCAUSE_CAUSE + 1), + METAL_INTERRUPT_ID_BEU = 128, +} metal_interrupt_id_e; + +typedef enum { + METAL_IAM_EXCEPTION_CODE, /* Instruction address misaligned */ + METAL_IAF_EXCEPTION_CODE, /* Instruction access faultd */ + METAL_II_EXCEPTION_CODE, /* Illegal instruction */ + METAL_BREAK_EXCEPTION_CODE, /* Breakpoint */ + METAL_LAM_EXCEPTION_CODE, /* Load address misaligned */ + METAL_LAF_EXCEPTION_CODE, /* Load access fault */ + METAL_SAMOAM_EXCEPTION_CODE, /* Store/AMO address misaligned */ + METAL_SAMOAF_EXCEPTION_CODE, /* Store/AMO access fault */ + METAL_ECALL_U_EXCEPTION_CODE, /* Environment call from U-mode */ + METAL_R9_EXCEPTION_CODE, /* Reserved */ + METAL_R10_EXCEPTION_CODE, /* Reserved */ + METAL_ECALL_M_EXCEPTION_CODE, /* Environment call from M-mode */ + METAL_MAX_EXCEPTION_CODE, +} metal_exception_code_e; + +typedef enum { + METAL_TIMER_MTIME_GET = 1, + METAL_SOFTWARE_IPI_CLEAR, + METAL_SOFTWARE_IPI_SET, + METAL_SOFTWARE_MSIP_GET, + METAL_MAX_INTERRUPT_GET, + METAL_INDEX_INTERRUPT_GET, +} metal_interrup_cmd_e; + +typedef struct __metal_interrupt_data { + long long pad : 64; + metal_interrupt_handler_t handler; + void *sub_int; + void *exint_data; +} __metal_interrupt_data; + +/* CPU interrupt controller */ + +uintptr_t __metal_myhart_id(void); + +struct __metal_driver_vtable_riscv_cpu_intc { + struct metal_interrupt_vtable controller_vtable; +}; + +void __metal_interrupt_global_enable(void); +void __metal_interrupt_global_disable(void); +metal_vector_mode __metal_controller_interrupt_vector_mode(void); +void __metal_controller_interrupt_vector(metal_vector_mode mode, + void *vec_table); + +__METAL_DECLARE_VTABLE(__metal_driver_vtable_riscv_cpu_intc) + +struct __metal_driver_riscv_cpu_intc { + struct metal_interrupt controller; + int init_done; + uintptr_t metal_mtvec_table[METAL_MAX_MI]; + __metal_interrupt_data metal_int_table[METAL_MAX_MI]; + __metal_interrupt_data metal_int_beu; + metal_exception_handler_t metal_exception_table[METAL_MAX_ME]; +}; + +/* CPU driver*/ +struct __metal_driver_vtable_cpu { + struct metal_cpu_vtable cpu_vtable; +}; + +__METAL_DECLARE_VTABLE(__metal_driver_vtable_cpu) + +struct __metal_driver_cpu { + struct metal_cpu cpu; + unsigned int hpm_count; /* Available HPM counters per CPU */ +}; + +#endif diff --git a/arch/riscv32/fe310/src/freedom-metal/metal/drivers/riscv_plic0.h b/arch/riscv32/fe310/src/freedom-metal/metal/drivers/riscv_plic0.h new file mode 100644 index 00000000..fac76fbc --- /dev/null +++ b/arch/riscv32/fe310/src/freedom-metal/metal/drivers/riscv_plic0.h @@ -0,0 +1,31 @@ +/* Copyright 2018 SiFive, Inc */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#ifndef METAL__DRIVERS__RISCV_PLIC0_H +#define METAL__DRIVERS__RISCV_PLIC0_H + +#include +#include + +#define METAL_PLIC_SOURCE_MASK 0x1F +#define METAL_PLIC_SOURCE_SHIFT 5 +#define METAL_PLIC_SOURCE_PRIORITY_SHIFT 2 +#define METAL_PLIC_SOURCE_PENDING_SHIFT 0 + +struct __metal_driver_vtable_riscv_plic0 { + struct metal_interrupt_vtable plic_vtable; +}; + +__METAL_DECLARE_VTABLE(__metal_driver_vtable_riscv_plic0) + +#define __METAL_MACHINE_MACROS +#include +struct __metal_driver_riscv_plic0 { + struct metal_interrupt controller; + int init_done; + metal_interrupt_handler_t metal_exint_table[__METAL_PLIC_SUBINTERRUPTS]; + __metal_interrupt_data metal_exdata_table[__METAL_PLIC_SUBINTERRUPTS]; +}; +#undef __METAL_MACHINE_MACROS + +#endif diff --git a/arch/riscv32/fe310/src/freedom-metal/metal/drivers/sifive_buserror0.h b/arch/riscv32/fe310/src/freedom-metal/metal/drivers/sifive_buserror0.h new file mode 100644 index 00000000..20972109 --- /dev/null +++ b/arch/riscv32/fe310/src/freedom-metal/metal/drivers/sifive_buserror0.h @@ -0,0 +1,184 @@ +/* Copyright 2020 SiFive, Inc */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#ifndef METAL__DRIVERS__SIFIVE_BUSERROR0_H +#define METAL__DRIVERS__SIFIVE_BUSERROR0_H + +/*! + * @file sifive_buserror0.h + * + * @brief API for configuring the SiFive Bus Error Unit + */ + +#include +#include +#include + +/*! + * @brief The set of possible events handled by a SiFive Bus Error Unit + */ +typedef enum { + /*! @brief No event or error has been detected */ + METAL_BUSERROR_EVENT_NONE = 0, + + /*! @brief A correctable ECC error has occurred in the I$ or ITIM */ + METAL_BUSERROR_EVENT_INST_CORRECTABLE_ECC_ERROR = (1 << 2), + /*! @brief An uncorrectable ECC error has occurred in the I$ or ITIM */ + METAL_BUSERROR_EVENT_INST_UNCORRECTABLE_ECC_ERROR = (1 << 3), + /*! @brief A TileLink load or store bus error has occurred */ + METAL_BUSERROR_EVENT_LOAD_STORE_ERROR = (1 << 5), + /*! @brief A correctable ECC error has occurred in the D$ or DTIM */ + METAL_BUSERROR_EVENT_DATA_CORRECTABLE_ECC_ERROR = (1 << 6), + /*! @brief An uncorrectable ECC error has occurred in the D$ or DTIM */ + METAL_BUSERROR_EVENT_DATA_UNCORRECTABLE_ECC_ERROR = (1 << 7), + + /*! @brief Used to set/clear all interrupts or query/clear all accrued + events */ + METAL_BUSERROR_EVENT_ALL = + METAL_BUSERROR_EVENT_INST_CORRECTABLE_ECC_ERROR | + METAL_BUSERROR_EVENT_INST_UNCORRECTABLE_ECC_ERROR | + METAL_BUSERROR_EVENT_LOAD_STORE_ERROR | + METAL_BUSERROR_EVENT_DATA_CORRECTABLE_ECC_ERROR | + METAL_BUSERROR_EVENT_DATA_UNCORRECTABLE_ECC_ERROR, + /*! @brief A synonym of METAL_BUSERROR_EVENT_ALL */ + METAL_BUSERROR_EVENT_ANY = METAL_BUSERROR_EVENT_ALL, + + /*! @brief A value which is impossible for the bus error unit to report. + * Indicates an error has occurred if provided as a return value. */ + METAL_BUSERROR_EVENT_INVALID = (1 << 8), +} metal_buserror_event_t; + +/*! + * @brief The handle for a bus error unit + */ +struct metal_buserror { + uint8_t __no_empty_structs; +}; + +/*! + * @brief Enable bus error events + * + * Enabling bus error events causes them to be registered as accrued and, + * if the corresponding interrupt is inabled, trigger interrupts. + * + * @param beu The bus error unit handle + * @param events A mask of error events to enable + * @param enabled True if the mask should be enabled, false if they should be + * disabled + * @return 0 upon success + */ +int metal_buserror_set_event_enabled(struct metal_buserror *beu, + metal_buserror_event_t events, + bool enabled); + +/*! + * @brief Get enabled bus error events + * @param beu The bus error unit handle + * @return A mask of all enabled events + */ +metal_buserror_event_t +metal_buserror_get_event_enabled(struct metal_buserror *beu); + +/*! + * @brief Enable or disable the platform interrupt + * + * @param beu The bus error unit handle + * @param event The error event which would trigger the interrupt + * @param enabled True if the interrupt should be enabled + * @return 0 upon success + */ +int metal_buserror_set_platform_interrupt(struct metal_buserror *beu, + metal_buserror_event_t events, + bool enabled); + +/*! + * @brief Enable or disable the hart-local interrupt + * + * @param beu The bus error unit handle + * @param event The error event which would trigger the interrupt + * @param enabled True if the interrupt should be enabled + * @return 0 upon success + */ +int metal_buserror_set_local_interrupt(struct metal_buserror *beu, + metal_buserror_event_t events, + bool enabled); + +/*! + * @brief Get the error event which caused the most recent interrupt + * + * This method should be called from within the interrupt handler for the bus + * error unit interrupt + * + * @param beu The bus error unit handle + * @return The event which caused the interrupt + */ +metal_buserror_event_t metal_buserror_get_cause(struct metal_buserror *beu); + +/*! + * @brief Clear the cause register for the bus error unit + * + * This method should be called from within the interrupt handler for the bus + * error unit to un-latch the cause register for the next event + * + * @param beu The bus error unit handle + * @return 0 upon success + */ +int metal_buserror_clear_cause(struct metal_buserror *beu); + +/*! + * @brief Get the physical address of the error event + * + * This method should be called from within the interrupt handler for the bus + * error unit. + * + * @param beu The bus error unit handle + * @return The address of the error event + */ +uintptr_t metal_buserror_get_event_address(struct metal_buserror *beu); + +/*! + * @brief Returns true if the event is set in the accrued register + * + * @param beu The bus error unit handle + * @param event The event to query + * @return True if the event is set in the accrued register + */ +bool metal_buserror_is_event_accrued(struct metal_buserror *beu, + metal_buserror_event_t events); + +/*! + * @brief Clear the given event from the accrued register + * + * @param beu The bus error unit handle + * @param event The event to clear + * @return 0 upon success + */ +int metal_buserror_clear_event_accrued(struct metal_buserror *beu, + metal_buserror_event_t events); + +/*! + * @brief get the platform-level interrupt parent of the bus error unit + * + * @param beu The bus error unit handle + * @return A pointer to the interrupt parent + */ +struct metal_interrupt * +metal_buserror_get_platform_interrupt_parent(struct metal_buserror *beu); + +/*! + * @brief Get the platform-level interrupt id for the bus error unit interrupt + * + * @param beu The bus error unit handle + * @return The interrupt id + */ +int metal_buserror_get_platform_interrupt_id(struct metal_buserror *beu); + +/*! + * @brief Get the hart-local interrupt id for the bus error unit interrupt + * + * @param beu The bus error unit handle + * @return The interrupt id + */ +int metal_buserror_get_local_interrupt_id(struct metal_buserror *beu); + +#endif diff --git a/arch/riscv32/fe310/src/freedom-metal/metal/drivers/sifive_ccache0.h b/arch/riscv32/fe310/src/freedom-metal/metal/drivers/sifive_ccache0.h new file mode 100644 index 00000000..ce74be60 --- /dev/null +++ b/arch/riscv32/fe310/src/freedom-metal/metal/drivers/sifive_ccache0.h @@ -0,0 +1,140 @@ +/* Copyright 2020 SiFive, Inc */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#ifndef METAL__DRIVERS__SIFIVE_CCACHE0_H +#define METAL__DRIVERS__SIFIVE_CCACHE0_H + +/*! + * @file sifive_ccache0.h + * + * @brief API for configuring the SiFive cache controller + */ + +#include +#include + +/*! @brief Cache configuration data */ +typedef struct { + uint32_t num_bank; + uint32_t num_ways; + uint32_t num_sets; + uint32_t block_size; +} sifive_ccache0_config; + +/*! @brief Set of values for ECC error type */ +typedef enum { + SIFIVE_CCACHE0_DATA = 0, + SIFIVE_CCACHE0_DIR = 1, +} sifive_ccache0_ecc_errtype_t; + +/*! @brief Initialize cache controller, enables all available + * cache-ways. + * Note: If LIM is in use, corresponding cache ways are not enabled. + * @param None. + * @return 0 If no error.*/ +int sifive_ccache0_init(void); + +/*! @brief Get cache configuration data. + * @param config User specified data buffer. + * @return None.*/ +void sifive_ccache0_get_config(sifive_ccache0_config *config); + +/*! @brief Get currently active cache ways. + * @param None. + * @return Number of cache ways enabled.*/ +uint32_t sifive_ccache0_get_enabled_ways(void); + +/*! @brief Enable specified cache ways. + * @param ways Number of ways to be enabled. + * @return 0 If no error.*/ +int sifive_ccache0_set_enabled_ways(uint32_t ways); + +/*! @brief Inject ECC error into data or meta-data. + * @param bitindex Bit index to be corrupted on next cache operation. + * @param type ECC error target location. + * @return None.*/ +void sifive_ccache0_inject_ecc_error(uint32_t bitindex, + sifive_ccache0_ecc_errtype_t type); + +/*! @brief Flush out entire cache block containing given address. + * @param flush_addr Address for the cache block to be flushed. + * @return None.*/ +void sifive_ccache0_flush(uintptr_t flush_addr); + +/*! @brief Get most recently ECC corrected address. + * @param type ECC error target location. + * @return Last corrected ECC address.*/ +uintptr_t sifive_ccache0_get_ecc_fix_addr(sifive_ccache0_ecc_errtype_t type); + +/*! @brief Get number of times ECC errors were corrected. + * Clears related ECC interrupt signals. + * @param type ECC error target location. + * @return Corrected ECC error count.*/ +uint32_t sifive_ccache0_get_ecc_fix_count(sifive_ccache0_ecc_errtype_t type); + +/*! @brief Get address location of most recent uncorrected ECC error. + * @param type ECC error target location. + * @return Last uncorrected ECC address.*/ +uintptr_t sifive_ccache0_get_ecc_fail_addr(sifive_ccache0_ecc_errtype_t type); + +/*! @brief Get number of times ECC errors were not corrected. + * Clears related ECC interrupt signals. + * @param type ECC error target location. + * @return Uncorrected ECC error count.*/ +uint32_t sifive_ccache0_get_ecc_fail_count(sifive_ccache0_ecc_errtype_t type); + +/*! @brief Get currently active way enable mask value for the given master ID. + * @param master_id Cache controller master ID. + * @return Way enable mask. */ +uint64_t sifive_ccache0_get_way_mask(uint32_t master_id); + +/*! @brief Set way enable mask for the given master ID. + * @param master_id Cache controller master ID. + * @param waymask Specify ways to be enabled. + * @return 0 If no error.*/ +int sifive_ccache0_set_way_mask(uint32_t master_id, uint64_t waymask); + +/*! @brief Select cache performance events to be counted. + * @param counter Cache performance monitor counter index. + * @param mask Event selection mask. + * @return None.*/ +void sifive_ccache0_set_pmevent_selector(uint32_t counter, uint64_t mask); + +/*! @brief Get currently set events for the given counter index. + * @param counter Cache performance monitor counter index. + * @return Event selection mask.*/ +uint64_t sifive_ccache0_get_pmevent_selector(uint32_t counter); + +/*! @brief Clears specified cache performance counter. + * @param counter Cache performance monitor counter index. + * @return None.*/ +void sifive_ccache0_clr_pmevent_counter(uint32_t counter); + +/*! @brief Reads specified cache performance counter. + * @param counter Cache performance monitor counter index. + * @return Counter value.*/ +uint64_t sifive_ccache0_get_pmevent_counter(uint32_t counter); + +/*! @brief Select cache clients to be excluded from performance monitoring. + * @param mask Client disable mask. + * @return None.*/ +void sifive_ccache0_set_client_filter(uint64_t mask); + +/*! @brief Get currently set cache client disable mask. + * @param None. + * @return Client disable mask.*/ +uint64_t sifive_ccache0_get_client_filter(void); + +/*! @brief Get interrupt IDs for the cache controller. + * @param src Interrupt trigger source index. + * @return Interrupt id.*/ +int sifive_ccache0_get_interrupt_id(uint32_t src); + +/*! @brief Get interrupt controller of the cache. + * The interrupt controller must be initialized before any interrupts can be + * registered or enabled with it. + * @param None. + * @return Handle for the interrupt controller.*/ +struct metal_interrupt *sifive_ccache0_interrupt_controller(void); + +#endif diff --git a/arch/riscv32/fe310/src/freedom-metal/metal/drivers/sifive_clic0.h b/arch/riscv32/fe310/src/freedom-metal/metal/drivers/sifive_clic0.h new file mode 100644 index 00000000..b8ff8227 --- /dev/null +++ b/arch/riscv32/fe310/src/freedom-metal/metal/drivers/sifive_clic0.h @@ -0,0 +1,48 @@ +/* Copyright 2018 SiFive, Inc */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#ifndef METAL__DRIVERS__SIFIVE_CLIC0_H +#define METAL__DRIVERS__SIFIVE_CLIC0_H + +#include +#include + +#define METAL_CLIC_MAX_NMBITS 2 +#define METAL_CLIC_MAX_NLBITS 8 +#define METAL_CLIC_MAX_NVBITS 1 + +#define METAL_SIFIVE_CLIC0_CLICCFG_NMBITS_MMODE 0x00 +#define METAL_SIFIVE_CLIC0_CLICCFG_NMBITS_SMODE1 0x20 +#define METAL_SIFIVE_CLIC0_CLICCFG_NMBITS_SMODE2 0x40 +#define METAL_SIFIVE_CLIC0_CLICCFG_NMBITS_MASK 0x60 +#define METAL_SIFIVE_CLIC0_CLICCFG_NLBITS_MASK 0x1E +#define METAL_SIFIVE_CLIC0_CLICCFG_NVBIT_MASK 0x01 + +#define METAL_CLIC_ICTRL_SMODE1_MASK 0x7F /* b8 set imply M-mode */ +#define METAL_CLIC_ICTRL_SMODE2_MASK 0x3F /* b8 set M-mode, b7 clear U-mode */ + +#define METAL_MAX_INTERRUPT_LEVEL ((1 << METAL_CLIC_MAX_NLBITS) - 1) + +struct __metal_driver_vtable_sifive_clic0 { + struct metal_interrupt_vtable clic_vtable; +}; + +__METAL_DECLARE_VTABLE(__metal_driver_vtable_sifive_clic0) + +#define __METAL_MACHINE_MACROS +#include +struct __metal_driver_sifive_clic0 { + struct metal_interrupt controller; + int init_done; + struct { + } __attribute__((aligned(64))); + metal_interrupt_vector_handler_t + metal_mtvt_table[__METAL_CLIC_SUBINTERRUPTS]; + __metal_interrupt_data metal_exint_table[__METAL_CLIC_SUBINTERRUPTS]; +}; +#undef __METAL_MACHINE_MACROS + +int __metal_driver_sifive_clic0_command_request( + struct metal_interrupt *controller, int command, void *data); + +#endif diff --git a/arch/riscv32/fe310/src/freedom-metal/metal/drivers/sifive_fe310-g000_hfrosc.h b/arch/riscv32/fe310/src/freedom-metal/metal/drivers/sifive_fe310-g000_hfrosc.h new file mode 100644 index 00000000..d60d3a3b --- /dev/null +++ b/arch/riscv32/fe310/src/freedom-metal/metal/drivers/sifive_fe310-g000_hfrosc.h @@ -0,0 +1,22 @@ +/* Copyright 2018 SiFive, Inc */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#ifndef METAL__DRIVERS__SIFIVE_FE310_G000_HFROSC_H +#define METAL__DRIVERS__SIFIVE_FE310_G000_HFROSC_H + +#include +#include +#include +#include + +struct __metal_driver_vtable_sifive_fe310_g000_hfrosc { + struct __metal_clock_vtable clock; +}; + +__METAL_DECLARE_VTABLE(__metal_driver_vtable_sifive_fe310_g000_hfrosc) + +struct __metal_driver_sifive_fe310_g000_hfrosc { + struct metal_clock clock; +}; + +#endif diff --git a/arch/riscv32/fe310/src/freedom-metal/metal/drivers/sifive_fe310-g000_hfxosc.h b/arch/riscv32/fe310/src/freedom-metal/metal/drivers/sifive_fe310-g000_hfxosc.h new file mode 100644 index 00000000..b86926fb --- /dev/null +++ b/arch/riscv32/fe310/src/freedom-metal/metal/drivers/sifive_fe310-g000_hfxosc.h @@ -0,0 +1,20 @@ +/* Copyright 2018 SiFive, Inc */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#ifndef METAL__DRIVERS__SIFIVE_FE310_G000_HFXOSC_H +#define METAL__DRIVERS__SIFIVE_FE310_G000_HFXOSC_H + +#include +#include + +struct __metal_driver_vtable_sifive_fe310_g000_hfxosc { + struct __metal_clock_vtable clock; +}; + +__METAL_DECLARE_VTABLE(__metal_driver_vtable_sifive_fe310_g000_hfxosc) + +struct __metal_driver_sifive_fe310_g000_hfxosc { + struct metal_clock clock; +}; + +#endif diff --git a/arch/riscv32/fe310/src/freedom-metal/metal/drivers/sifive_fe310-g000_lfrosc.h b/arch/riscv32/fe310/src/freedom-metal/metal/drivers/sifive_fe310-g000_lfrosc.h new file mode 100644 index 00000000..2650584a --- /dev/null +++ b/arch/riscv32/fe310/src/freedom-metal/metal/drivers/sifive_fe310-g000_lfrosc.h @@ -0,0 +1,21 @@ +/* Copyright 2019 SiFive, Inc */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#ifndef METAL__DRIVERS__SIFIVE_FE310_G000_LFROSC_H +#define METAL__DRIVERS__SIFIVE_FE310_G000_LFROSC_H + +#include +#include +#include + +struct __metal_driver_vtable_sifive_fe310_g000_lfrosc { + struct __metal_clock_vtable clock; +}; + +__METAL_DECLARE_VTABLE(__metal_driver_vtable_sifive_fe310_g000_lfrosc) + +struct __metal_driver_sifive_fe310_g000_lfrosc { + struct metal_clock clock; +}; + +#endif diff --git a/arch/riscv32/fe310/src/freedom-metal/metal/drivers/sifive_fe310-g000_pll.h b/arch/riscv32/fe310/src/freedom-metal/metal/drivers/sifive_fe310-g000_pll.h new file mode 100644 index 00000000..67f818f7 --- /dev/null +++ b/arch/riscv32/fe310/src/freedom-metal/metal/drivers/sifive_fe310-g000_pll.h @@ -0,0 +1,26 @@ +/* Copyright 2018 SiFive, Inc */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include + +#ifndef METAL__DRIVERS__SIFIVE_FE310_G000_PLL_H +#define METAL__DRIVERS__SIFIVE_FE310_G000_PLL_H + +struct __metal_driver_sifive_fe310_g000_pll; + +#include +#include +#include + +struct __metal_driver_vtable_sifive_fe310_g000_pll { + void (*init)(struct __metal_driver_sifive_fe310_g000_pll *pll); + struct __metal_clock_vtable clock; +}; + +__METAL_DECLARE_VTABLE(__metal_driver_vtable_sifive_fe310_g000_pll) + +struct __metal_driver_sifive_fe310_g000_pll { + struct metal_clock clock; +}; + +#endif diff --git a/arch/riscv32/fe310/src/freedom-metal/metal/drivers/sifive_fe310-g000_prci.h b/arch/riscv32/fe310/src/freedom-metal/metal/drivers/sifive_fe310-g000_prci.h new file mode 100644 index 00000000..4fca3016 --- /dev/null +++ b/arch/riscv32/fe310/src/freedom-metal/metal/drivers/sifive_fe310-g000_prci.h @@ -0,0 +1,25 @@ +/* Copyright 2018 SiFive, Inc */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#ifndef METAL__DRIVERS__SIFIVE_FE310_G000_PRCI_H +#define METAL__DRIVERS__SIFIVE_FE310_G000_PRCI_H + +#include +#include + +struct __metal_driver_sifive_fe310_g000_prci; + +struct __metal_driver_vtable_sifive_fe310_g000_prci { + long (*get_reg)(const struct __metal_driver_sifive_fe310_g000_prci *, + long offset); + long (*set_reg)(const struct __metal_driver_sifive_fe310_g000_prci *, + long offset, long value); +}; + +__METAL_DECLARE_VTABLE(__metal_driver_vtable_sifive_fe310_g000_prci) + +struct __metal_driver_sifive_fe310_g000_prci { + const struct __metal_driver_vtable_sifive_fe310_g000_prci *vtable; +}; + +#endif diff --git a/arch/riscv32/fe310/src/freedom-metal/metal/drivers/sifive_global-external-interrupts0.h b/arch/riscv32/fe310/src/freedom-metal/metal/drivers/sifive_global-external-interrupts0.h new file mode 100644 index 00000000..9e6f2faf --- /dev/null +++ b/arch/riscv32/fe310/src/freedom-metal/metal/drivers/sifive_global-external-interrupts0.h @@ -0,0 +1,21 @@ +/* Copyright 2018 SiFive, Inc */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#ifndef METAL__DRIVERS__SIFIVE_GLOBAL_EXTERNAL_INTERRUPTS0_H +#define METAL__DRIVERS__SIFIVE_GLOBAL_EXTERNAL_INTERRUPTS0_H + +#include +#include + +struct __metal_driver_vtable_sifive_global_external_interrupts0 { + struct metal_interrupt_vtable global0_vtable; +}; + +__METAL_DECLARE_VTABLE(__metal_driver_vtable_sifive_global_external_interrupts0) + +struct __metal_driver_sifive_global_external_interrupts0 { + struct metal_interrupt irc; + int init_done; +}; + +#endif diff --git a/arch/riscv32/fe310/src/freedom-metal/metal/drivers/sifive_gpio-buttons.h b/arch/riscv32/fe310/src/freedom-metal/metal/drivers/sifive_gpio-buttons.h new file mode 100644 index 00000000..7227eee0 --- /dev/null +++ b/arch/riscv32/fe310/src/freedom-metal/metal/drivers/sifive_gpio-buttons.h @@ -0,0 +1,21 @@ +/* Copyright 2018 SiFive, Inc */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#ifndef METAL__DRIVERS__SIFIVE_GPIO_BUTTONS_H +#define METAL__DRIVERS__SIFIVE_GPIO_BUTTONS_H + +#include +#include +#include + +struct __metal_driver_vtable_sifive_button { + struct metal_button_vtable button_vtable; +}; + +__METAL_DECLARE_VTABLE(__metal_driver_vtable_sifive_button) + +struct __metal_driver_sifive_gpio_button { + struct metal_button button; +}; + +#endif diff --git a/arch/riscv32/fe310/src/freedom-metal/metal/drivers/sifive_gpio-leds.h b/arch/riscv32/fe310/src/freedom-metal/metal/drivers/sifive_gpio-leds.h new file mode 100644 index 00000000..abfca01c --- /dev/null +++ b/arch/riscv32/fe310/src/freedom-metal/metal/drivers/sifive_gpio-leds.h @@ -0,0 +1,21 @@ +/* Copyright 2018 SiFive, Inc */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#ifndef METAL__DRIVERS__SIFIVE_GPIO_LEDS_H +#define METAL__DRIVERS__SIFIVE_GPIO_LEDS_H + +#include +#include +#include + +struct __metal_driver_vtable_sifive_led { + struct metal_led_vtable led_vtable; +}; + +__METAL_DECLARE_VTABLE(__metal_driver_vtable_sifive_led) + +struct __metal_driver_sifive_gpio_led { + struct metal_led led; +}; + +#endif diff --git a/arch/riscv32/fe310/src/freedom-metal/metal/drivers/sifive_gpio-switches.h b/arch/riscv32/fe310/src/freedom-metal/metal/drivers/sifive_gpio-switches.h new file mode 100644 index 00000000..be55a044 --- /dev/null +++ b/arch/riscv32/fe310/src/freedom-metal/metal/drivers/sifive_gpio-switches.h @@ -0,0 +1,21 @@ +/* Copyright 2018 SiFive, Inc */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#ifndef METAL__DRIVERS__SIFIVE_GPIO_SWITCHES_H +#define METAL__DRIVERS__SIFIVE_GPIO_SWITCHES_H + +#include +#include +#include + +struct __metal_driver_vtable_sifive_switch { + struct metal_switch_vtable switch_vtable; +}; + +__METAL_DECLARE_VTABLE(__metal_driver_vtable_sifive_switch) + +struct __metal_driver_sifive_gpio_switch { + struct metal_switch flip; +}; + +#endif diff --git a/arch/riscv32/fe310/src/freedom-metal/metal/drivers/sifive_gpio0.h b/arch/riscv32/fe310/src/freedom-metal/metal/drivers/sifive_gpio0.h new file mode 100644 index 00000000..50314222 --- /dev/null +++ b/arch/riscv32/fe310/src/freedom-metal/metal/drivers/sifive_gpio0.h @@ -0,0 +1,22 @@ +/* Copyright 2018 SiFive, Inc */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#ifndef METAL__DRIVERS__SIFIVE_GPIO0_H +#define METAL__DRIVERS__SIFIVE_GPIO0_H + +#include +#include + +struct __metal_driver_vtable_sifive_gpio0 { + const struct __metal_gpio_vtable gpio; +}; + +// struct __metal_driver_sifive_gpio0; + +__METAL_DECLARE_VTABLE(__metal_driver_vtable_sifive_gpio0) + +struct __metal_driver_sifive_gpio0 { + struct metal_gpio gpio; +}; + +#endif diff --git a/arch/riscv32/fe310/src/freedom-metal/metal/drivers/sifive_hca1_regs.h b/arch/riscv32/fe310/src/freedom-metal/metal/drivers/sifive_hca1_regs.h new file mode 100644 index 00000000..c3c96334 --- /dev/null +++ b/arch/riscv32/fe310/src/freedom-metal/metal/drivers/sifive_hca1_regs.h @@ -0,0 +1,829 @@ +/** + * Copyright 2021 SiFive, Inc + * SPDX-License-Identifier: Apache-2.0 + * + * HCA registers + * @file sifive_hca1_regs.h + * @brief registers definition of HCA revision 1.0.0 + */ + +#ifndef SIFIVE_HCA1_REGS_H_ +#define SIFIVE_HCA1_REGS_H_ + +#include + +/* clang-format off */ + +/* following defines should be used for structure members */ +#define __IM volatile const /**< Defines 'read only' structure member permissions */ +#define __OM volatile /**< Defines 'write only' structure member permissions */ +#define __IOM volatile /**< Defines 'read / write' structure member permissions */ + +typedef struct _HCA { + __IM uint32_t REV; /**< Offset: 0x000 (R/ ) HCA Revision */ + uint32_t _reserved0; + __IOM uint32_t CR; /**< Offset: 0x008 (R/W) HCA Control */ + uint32_t _reserved1; + __IM uint32_t SR; /**< Offset: 0x010 (R/ ) HCA Status */ + uint32_t _reserved2; + __OM uint32_t FIFO_IN; /**< Offset: 0x018 ( /W) AES/SHA/PKA FIFO Input */ + uint32_t _reserved3[7U]; + __IM uint32_t FIFO_OUT; /**< Offset: 0x038 (R/ ) FIFO Output */ + uint32_t _reserved4[13U]; + __IOM uint32_t DMA_CR; /**< Offset: 0x070 (R/W) DMA control */ + uint32_t _reserved5; + __IOM uint32_t DMA_LEN; /**< Offset: 0x078 (R/W) DMA length */ + uint32_t _reserved6; + __IOM uint64_t DMA_SRC; /**< Offset: 0x080 (R/W) DMA source address */ + __IOM uint64_t DMA_DEST; /**< Offset: 0x088 (R/W) DMA destination address */ +} HCA_Type; + +/** + * Structure type to access HCA Revision (HCA_REV) + */ +typedef union _HCA_REV { + struct { + uint32_t PATCHREV:4; /**< bit: 0..3 HCA Patch Revision */ + uint32_t MINORREV:4; /**< bit: 4..7 HCA Minor Revision */ + uint32_t MAJORREV:4; /**< bit: 8..11 HCA Major Revision */ + uint32_t _reserved0:4; /**< bit: 12..15 (reserved) */ + uint32_t CONFIG:5; /**< bit: 16..20 HCA Configuration */ + uint32_t _reserved1:11; /**< bit: 21..31 (reserved) */ + } b; /**< Structure used for bit access */ + uint32_t w; /**< Structure used for word access */ +} HCA_REV_Type; + + +/* HCA Revision: Patch Revision */ +#define HCA_REV_PATCHREV_Pos 0U +#define HCA_REV_PATCHREV_Msk (0xFU << HCA_REV_PATCHREV_Pos) + +/* HCA Revision: Minor Revision */ +#define HCA_REV_MINORREV_Pos 4U +#define HCA_REV_MINORREV_Msk (0xFU << HCA_REV_MINORREV_Pos) + +/* HCA Revision: Major Revision */ +#define HCA_REV_MAJORREV_Pos 8U +#define HCA_REV_MAJORREV_Msk (0xFU << HCA_REV_MAJORREV_Pos) + +/* HCA Revision: Configuration */ +#define HCA_REV_CONFIG_Pos 16U +#define HCA_REV_CONFIG_Msk (0x1FU << HCA_REV_CONFIG_Pos) + +/** + * Structure type to access HCA Control (HCA_CR) + */ +typedef union _HCA_CR { + struct { + uint32_t LE:1; /**< bit: 0 Little endian enable, 0: Big Endian, 1: Little Endian */ + uint32_t INVLDFIFOS:1; /**< bit: 1 Invalidate FIFOs */ + uint32_t _reserved0:14; /**< bit: 2..15 (reserved) */ + uint32_t CRYPTODIE:1; /**< bit: 16 AES/SHA/PKA completion interrupt enable */ + uint32_t OFIFOIE:1; /**< bit: 17 Output FIFO not empty interrupt enable */ + uint32_t DMADIE:1; /**< bit: 18 DMA completion interrupt enable */ + uint32_t DMAERRIE:1; /**< bit: 19 DMA error interrupt enable */ + uint32_t _reserved1:4; /**< bit: 20..23 (reserved) */ + uint32_t CRYPTODIS:1; /**< bit: 24 AES/SHA/PKA completion interrupt status */ + uint32_t OFIFOIS:1; /**< bit: 25 Output FIFO not empty interrupt status */ + uint32_t DMADIS:1; /**< bit: 26 DMA completion interrupt status */ + uint32_t DMAERRIS:1; /**< bit: 27 DMA error interrupt status */ + uint32_t _reserved2:4; /**< bit: 28..31 (reserved) */ + } b; /**< Structure used for bit access */ + uint32_t w; /**< Structure used for word access */ +} HCA_CR_Type; + +/* HCA Control: Little endian enable, 0: Big Endian, 1: Little Endian */ +#define HCA_CR_LE_Pos 0U +#define HCA_CR_LE_Msk (1U << HCA_CR_LE_Pos) + +/* HCA Control: Invalidate FIFOs */ +#define HCA_CR_INVLDFIFOS_Pos 1U +#define HCA_CR_INVLDFIFOS_Msk (1U << HCA_CR_INVLDFIFOS_Pos) + +/* HCA Control: AES/SHA/PKA completion interrupt enable */ +#define HCA_CR_CRYPTODIE_Pos 16U +#define HCA_CR_CRYPTODIE_Msk (1U << HCA_CR_CRYPTODIE_Pos) + +/* HCA Control: Output FIFO not empty interrupt enable */ +#define HCA_CR_OFIFOIE_Pos 17U +#define HCA_CR_OFIFOIE_Msk (1U << HCA_CR_OFIFOIE_Pos) + +/* HCA Control: DMA completion interrupt enable */ +#define HCA_CR_DMADIE_Pos 18U +#define HCA_CR_DMADIE_Msk (1U << HCA_CR_DMADIE_Pos) + +/* HCA Control: DMA error interrupt enable */ +#define HCA_CR_DMAERRIE_Pos 19U +#define HCA_CR_DMAERRIE_Msk (1U << HCA_CR_DMAERRIE_Pos) + +/* HCA Control: AES/SHA/PKA completion interrupt status */ +#define HCA_CR_CRYPTODIS_Pos 24U +#define HCA_CR_CRYPTODIS_Msk (1U << HCA_CR_CRYPTODIS_Pos) + +/* HCA Control: Output FIFO not empty interrupt status */ +#define HCA_CR_OFIFOIS_Pos 25U +#define HCA_CR_OFIFOIS_Msk (1U << HCA_CR_OFIFOIS_Pos) + +/* HCA Control: DMA completion interrupt status */ +#define HCA_CR_DMADIS_Pos 26U +#define HCA_CR_DMADIS_Msk (1U << HCA_CR_DMADIS_Pos) + +/* HCA Control: DMA error interrupt status */ +#define HCA_CR_DMAERRIS_Pos 27U +#define HCA_CR_DMAERRIS_Msk (1U << HCA_CR_DMAERRIS_Pos) + +/** + * Structure type to access HCA Status (HCA_SR) + */ +typedef union _HCA_SR { + struct { + uint32_t IFIFOEMPTY:1; /**< bit: 0 Input FIFO Empty */ + uint32_t IFIFOFULL:1; /**< bit: 1 Input FIFO Full */ + uint32_t OFIFOEMPTY:1; /**< bit: 2 Output FIFO Empty */ + uint32_t OFIFOFULL:1; /**< bit: 3 Output FIFO Full */ + uint32_t _reserved0:4; /**< bit: 4..7 (reserved) */ + uint32_t IFIFOCNT:6; /**< bit: 8..13 Input FIFO count */ + uint32_t _reserved1:2; /**< bit: 14..15 (reserved) */ + uint32_t OFIFOCNT:6; /**< bit: 16..21 Output FIFO count */ + uint32_t _reserved2:9; /**< bit: 22..30 (reserved) */ + uint32_t BUSY:1; /**< bit: 31 AES or SHA or PKA is busy */ + } b; /**< Structure used for bit access */ + uint32_t w; /**< Structure used for word access */ +} HCA_SR_Type; + +/* HCA Status: Input FIFO Empty */ +#define HCA_SR_IFIFOEMPTY_Pos 0U +#define HCA_SR_IFIFOEMPTY_Msk (1U << HCA_SR_IFIFOEMPTY_Pos) + +/* HCA Status: Input FIFO Full */ +#define HCA_SR_IFIFOFULL_Pos 1U +#define HCA_SR_IFIFOFULL_Msk (1U << HCA_SR_IFIFOFULL_Pos) + +/* HCA Status: Output FIFO Empty */ +#define HCA_SR_OFIFOEMPTY_Pos 2U +#define HCA_SR_OFIFOEMPTY_Msk (1U << HCA_SR_OFIFOEMPTY_Pos) + +/* HCA Status: Output FIFO Full */ +#define HCA_SR_OFIFOFULL_Pos 3U +#define HCA_SR_OFIFOFULL_Msk (1U << HCA_SR_OFIFOFULL_Pos) + +/* HCA Status: Input FIFO count */ +#define HCA_SR_IFIFOCNT_Pos 8U +#define HCA_SR_IFIFOCNT_Msk (0x3FU << HCA_SR_IFIFOCNT_Pos) + +/* HCA Status: Output FIFO count */ +#define HCA_SR_OFIFOCNT_Pos 16U +#define HCA_SR_OFIFOCNT_Msk (0x3FU << HCA_SR_OFIFOCNT_Pos) + +/* HCA Status: AES or SHA or PKA is busy */ +#define HCA_SR_BUSY_Pos 24U +#define HCA_SR_BUSY_Msk (1U << HCA_SR_BUSY_Pos) + +/* HCA AES/SHA/PKA FIFO Input: Data */ +#define HCA_FIFO_IN_DATA_Pos 0U +#define HCA_FIFO_IN_DATA_Msk 0xFFFFFFFFU + +/* HCA FIFO Output: Output */ +#define HCA_FIFO_OUT_DATA_Pos 0U +#define HCA_FIFO_OUT_DATA_Msk 0xFFFFFFFFU + +/** + * Structure type to access DMA control (DMA_CR) + */ +typedef union _HCA_DMA_CR { + struct { + uint32_t START:1; /**< bit: 0 DMA Start */ + uint32_t _reserved0:7; /**< bit: 1..7 (reserved) */ + uint32_t RRESPERR:1; /**< bit: 8 DMA Read Response error */ + uint32_t WRESPERR:1; /**< bit: 9 DMA Write Response error */ + uint32_t RLEGALERR:1; /**< bit: 10 DMA Read Legal error */ + uint32_t WLEGALERR:1; /**< bit: 11 DMA Write Legal error */ + uint32_t _reserved1:19; /**< bit: 12..30 (reserved) */ + uint32_t BUSY:1; /**< bit: 31 DMA Busy */ + } b; /**< Structure used for bit access */ + uint32_t w; /**< Structure used for word access */ +} HCA_DMA_CR_Type; + +/* HCA DMA control: Start */ +#define HCA_DMA_CR_START_Pos 0U +#define HCA_DMA_CR_START_Msk (1U << HCA_DMA_CR_START_Pos) + +/* HCA DMA control: Read Response error */ +#define HCA_DMA_CR_RRESPERR_Pos 8U +#define HCA_DMA_CR_RRESPERR_Msk (1U << HCA_DMA_CR_RRESPERR_Pos) + +/* HCA DMA control: Write Response error */ +#define HCA_DMA_CR_WRESPERR_Pos 9U +#define HCA_DMA_CR_WRESPERR_Msk (1U << HCA_DMA_CR_WRESPERR_Pos) + +/* HCA DMA control: Read Legal error */ +#define HCA_DMA_CR_RLEGALERR_Pos 10U +#define HCA_DMA_CR_RLEGALERR_Msk (1U << HCA_DMA_CR_RLEGALERR_Pos) + +/* HCA DMA control: Write Legal error */ +#define HCA_DMA_CR_WLEGALERR_Pos 11U +#define HCA_DMA_CR_WLEGALERR_Msk (1U << HCA_DMA_CR_WLEGALERR_Pos) + +/* HCA DMA control: Busy */ +#define HCA_DMA_CR_BUSY_Pos 31U +#define HCA_DMA_CR_BUSY_Msk (1U << HCA_DMA_CR_BUSY_Pos) + +/* HCA DMA length: Length (number of bytes) */ +#define HCA_DMA_LEN_LENGTH_Pos 0U +#define HCA_DMA_LEN_LENGTH_Msk 0xFFFFFFFFU + +/* HCA DMA source address: Source Address */ +#define HCA_DMA_SRC_SRCADDR_Pos 0U +#define HCA_DMA_SRC_SRCADDR_Msk 0xFFFFFFFFU + +/* HCA DMA destination address: Destination Address */ +#define HCA_DMA_DEST_DESTADDR_Pos 0U +#define HCA_DMA_DEST_DESTADDR_Msk 0xFFFFFFFFU + +typedef struct _HCA_AES { + __IM uint32_t REV; /**< Offset: 0x00 (R/ ) AES Revision */ + uint32_t _reserved0; + __IOM uint32_t CR; /**< Offset: 0x08 (R/W) AES control */ + uint32_t _reserved1; + __OM uint64_t KEY[4U]; /**< Offset: 0x10 ( /W) AES Key */ + uint32_t _reserved2[8U]; + __IOM uint64_t INITV[2U]; /**< Offset: 0x50 (R/W) AES Initialization Vector */ + uint32_t _reserved3[4U]; + __IOM uint64_t ALEN; /**< Offset: 0x70 (R/W) AES GCM/CCM AAD Length */ + __IOM uint64_t PLDLEN; /**< Offset: 0x78 (R/W) AES GCM/CCM Payload Length */ + __IM uint64_t AUTH[2U]; /**< Offset: 0x80 (R/ ) AES Authentication Tag */ +} HCA_AES_Type; + +/** + * Structure type to access AES Revision (AES_REV) + */ +typedef union _HCA_AES_REV { + struct { + uint32_t PATCHREV:4; /**< bit: 0..3 AES Patch Revision */ + uint32_t MINORREV:4; /**< bit: 4..7 AES Minor Revision */ + uint32_t MAJORREV:4; /**< bit: 8..11 AES Major Revision */ + uint32_t _reserved0:4; /**< bit: 12..15 (reserved) */ + uint32_t CONFIG:6; /**< bit: 16..21 AES Configuration */ + uint32_t _reserved1:10; /**< bit: 22..31 (reserved) */ + } b; /**< Structure used for bit access */ + uint32_t w; /**< Structure used for word access */ +} HCA_AES_REV_Type; + +/* HCA AES Revision: Patch Revision */ +#define HCA_AES_REV_PATCHREV_Pos 0U +#define HCA_AES_REV_PATCHREV_Msk (0xFU << HCA_AES_REV_PATCHREV_Pos) + +/* HCA AES Revision: Minor Revision */ +#define HCA_AES_REV_MINORREV_Pos 4U +#define HCA_AES_REV_MINORREV_Msk (0xFU << HCA_AES_REV_MINORREV_Pos) + +/* HCA AES Revision: Major Revision */ +#define HCA_AES_REV_MAJORREV_Pos 8U +#define HCA_AES_REV_MAJORREV_Msk (0xFU << HCA_AES_REV_MAJORREV_Pos) + +/* HCA AES Revision: Configuration */ +#define HCA_AES_REV_CONFIG_Pos 16U +#define HCA_AES_REV_CONFIG_Msk (0x3FU << HCA_AES_REV_CONFIG_Pos) + +/** + * Structure type to access AES control (AES_CR) + */ +typedef union _HCA_AES_CR { + struct { + uint32_t EN:1; /**< bit: 0 AES Enable */ + uint32_t MODE:3; /**< bit: 1..3 AES mode: ECB, CBC, CFB, OFB, CTR, GCM, CCM */ + uint32_t _reserved0:2; /**< bit: 4..5 (reserved) */ + uint32_t KEYSZ:2; /**< bit: 6..7 AES key size */ + uint32_t _reserved1:1; /**< bit: 8 (reserved) */ + uint32_t PROCESS:1; /**< bit: 9 AES process type (encryption or decryption) */ + uint32_t INIT:1; /**< bit: 10 AES Chain Initialization */ + uint32_t ABORT:1; /**< bit: 11 Abort GCM/CCM operation */ + uint32_t CCMT:3; /**< bit: 12..14 Encoded tag length parameter for CCM */ + uint32_t CCMQ:3; /**< bit: 15..17 Encoded q parameter for CCM */ + uint32_t _reserved2:13; /**< bit: 18..30 (reserved) */ + uint32_t BUSY:1; /**< bit: 31 AES Busy bit */ + } b; /**< Structure used for bit access */ + uint32_t w; /**< Structure used for word access */ +} HCA_AES_CR_Type; + +/* HCA AES control: Enable */ +#define HCA_AES_CR_EN_Pos 0U +#define HCA_AES_CR_EN_Msk (1U << HCA_AES_CR_EN_Pos) + +/* HCA AES control: Mode: ECB, CBC, CFB, OFB, CTR, GCM, CCM */ +#define HCA_AES_CR_MODE_Pos 1U +#define HCA_AES_CR_MODE_Msk (7U << HCA_AES_CR_MODE_Pos) + +/* HCA AES control: Key size */ +#define HCA_AES_CR_KEYSZ_Pos 6U +#define HCA_AES_CR_KEYSZ_Msk (3U << HCA_AES_CR_KEYSZ_Pos) + +/* HCA AES control: Process type (encryption or decryption) */ +#define HCA_AES_CR_PROCESS_Pos 9U +#define HCA_AES_CR_PROCESS_Msk (1U << HCA_AES_CR_PROCESS_Pos) + +/* HCA AES control: Chain Initialization */ +#define HCA_AES_CR_INIT_Pos 10U +#define HCA_AES_CR_INIT_Msk (1U << HCA_AES_CR_INIT_Pos) + +/* HCA AES control: Abort GCM/CCM operation */ +#define HCA_AES_CR_ABORT_Pos 11U +#define HCA_AES_CR_ABORT_Msk (1U << HCA_AES_CR_ABORT_Pos) + +/* HCA AES control: Encoded tag length parameter for CCM */ +#define HCA_AES_CR_CCMT_Pos 12U +#define HCA_AES_CR_CCMT_Msk (7U << HCA_AES_CR_CCMT_Pos) + +/* HCA AES control: Encoded q parameter for CCM */ +#define HCA_AES_CR_CCMQ_Pos 15U +#define HCA_AES_CR_CCMQ_Msk (7U << HCA_AES_CR_CCMQ_Pos) + +/* HCA AES control: Busy bit */ +#define HCA_AES_CR_BUSY_Pos 31U +#define HCA_AES_CR_BUSY_Msk (1U << HCA_AES_CR_BUSY_Pos) + +/* HCA AES Key: 256-bits key 0 */ +#define HCA_AES_KEY_KEY_0_Pos 0U +#define HCA_AES_KEY_KEY_0_Msk 0xFFFFFFFFFFFFFFFFU + +/* HCA AES Key: 256-bits key 1 */ +#define HCA_AES_KEY_KEY_1_Pos 0U +#define HCA_AES_KEY_KEY_1_Msk 0xFFFFFFFFFFFFFFFFU + +/* HCA AES Key: 256-bits key 2 */ +#define HCA_AES_KEY_KEY_2_Pos 0U +#define HCA_AES_KEY_KEY_2_Msk 0xFFFFFFFFFFFFFFFFU + +/* HCA AES Key: 256-bits key 3 */ +#define HCA_AES_KEY_KEY_3_Pos 0U +#define HCA_AES_KEY_KEY_3_Msk 0xFFFFFFFFFFFFFFFFU + +/* HCA AES Initialization Vector: Initialization Vector 0 */ +#define HCA_AES_INITV_INITV_0_Pos 0U +#define HCA_AES_INITV_INITV_0_Msk 0xFFFFFFFFFFFFFFFFU + +/* HCA AES Initialization Vector: Initialization Vector 1 */ +#define HCA_AES_INITV_INITV_1_Pos 0U +#define HCA_AES_INITV_INITV_1_Msk 0xFFFFFFFFFFFFFFFFU + +/** + * Structure type to access AES GCM/CCM AAD Length (AES_ALEN) + */ +typedef union _HCA_AES_ALEN { + struct { + uint64_t ALEN:61; /**< bit: 0..60 AAD Length */ + uint64_t _reserved0:3; /**< bit: 61..63 (reserved) */ + } b; /**< Structure used for bit access */ + uint64_t w; /**< Structure used for word access */ +} HCA_AES_ALEN_Type; + +/* HCA AES GCM/CCM AAD Length: AAD Length */ +#define HCA_AES_ALEN_ALEN_Pos 0U +#define HCA_AES_ALEN_ALEN_Msk 0x1FFFFFFFFFFFFFFFU + +/** + * Structure type to access AES GCM/CCM Payload Length (AES_PLDLEN) + */ +typedef union _HCA_AES_PLDLEN { + struct { + uint64_t PLDLEN:61; /**< bit: 0..60 Payload Length */ + uint64_t _reserved0:3; /**< bit: 61..63 (reserved) */ + } b; /**< Structure used for bit access */ + uint64_t w; /**< Structure used for word access */ +} HCA_AES_PLDLEN_Type; + +/* HCA AES GCM/CCM Payload Length: Payload Length */ +#define HCA_AES_PLDLEN_PLDLEN_Pos 0U +#define HCA_AES_PLDLEN_PLDLEN_Msk 0x1FFFFFFFFFFFFFFFU + +/* HCA AES Authentication Tag: Authentication Tag 0 */ +#define HCA_AES_AUTH_AUTH_0_Pos 0U +#define HCA_AES_AUTH_AUTH_0_Msk 0xFFFFFFFFFFFFFFFFU + +/* HCA AES Authentication Tag: Authentication Tag 1 */ +#define HCA_AES_AUTH_AUTH_1_Pos 0U +#define HCA_AES_AUTH_AUTH_1_Msk 0xFFFFFFFFFFFFFFFFU + +typedef struct _HCA_SHA { + __IM uint32_t REV; /**< Offset: 0x00 (R/ ) SHA Revision */ + uint32_t _reserved14; + __IOM uint32_t CR; /**< Offset: 0x08 (R/W) SHA control */ + uint32_t _reserved15; + __IM uint64_t HASH[8U]; /**< Offset: 0x10 (R/ ) HASH result from SHA */ +} HCA_SHA_Type; + +/** + * Structure type to access SHA Revision (SHA_REV) + */ +typedef union _HCA_SHA_REV { + struct { + uint32_t PATCHREV:4; /**< bit: 0..3 SHA Patch Revision */ + uint32_t MINORREV:4; /**< bit: 4..7 SHA Minor Revision */ + uint32_t MAJORREV:4; /**< bit: 8..11 SHA Major Revision */ + uint32_t TYPE:4; /**< bit: 12..15 SHA Type */ + uint16_t _reserved0; /**< bit: 16..31 (reserved) */ + } b; /**< Structure used for bit access */ + uint32_t w; /**< Structure used for word access */ +} HCA_SHA_REV_Type; + +/* HCA SHA Revision: Patch Revision */ +#define HCA_SHA_REV_PATCHREV_Pos 0U +#define HCA_SHA_REV_PATCHREV_Msk (0xFU << HCA_SHA_REV_PATCHREV_Pos) + +/* HCA SHA Revision: Minor Revision */ +#define HCA_SHA_REV_MINORREV_Pos 4U +#define HCA_SHA_REV_MINORREV_Msk (0xFU << HCA_SHA_REV_MINORREV_Pos) + +/* HCA SHA Revision: Major Revision */ +#define HCA_SHA_REV_MAJORREV_Pos 8U +#define HCA_SHA_REV_MAJORREV_Msk (0xFU << HCA_SHA_REV_MAJORREV_Pos) + +/* HCA SHA Revision: Type */ +#define HCA_SHA_REV_TYPE_Pos 12U +#define HCA_SHA_REV_TYPE_Msk (0xFU << HCA_SHA_REV_TYPE_Pos) + +/** + * Structure type to access SHA control (SHA_CR) + */ +typedef union _HCA_SHA_CR { + struct { + uint32_t EN:1; /**< bit: 0 SHA Enable */ + uint32_t MODE:2; /**< bit: 1..2 SHA mode: 224, 256, 384, 512 */ + uint32_t _reserved0:1; /**< bit: 3 (reserved) */ + uint32_t INIT:1; /**< bit: 4 Initialize the SHA engine */ + uint32_t LAST:1; /**< bit: 5 Last block flag */ + uint32_t LASTLEN:7; /**< bit: 6..12 Last block length */ + uint32_t _reserved1:18; /**< bit: 13..30 (reserved) */ + uint32_t BUSY:1; /**< bit: 31 SHA Busy bit */ + } b; /**< Structure used for bit access */ + uint32_t w; /**< Structure used for word access */ +} HCA_SHA_CR_Type; + +/* HCA SHA control: Enable */ +#define HCA_SHA_CR_EN_Pos 0U +#define HCA_SHA_CR_EN_Msk (1U << HCA_SHA_CR_EN_Pos) + +/* HCA SHA control: Mode: 224, 256, 384, 512 */ +#define HCA_SHA_CR_MODE_Pos 1U +#define HCA_SHA_CR_MODE_Msk (3U << HCA_SHA_CR_MODE_Pos) + +/* HCA SHA control: Initialize the SHA engine */ +#define HCA_SHA_CR_INIT_Pos 4U +#define HCA_SHA_CR_INIT_Msk (1U << HCA_SHA_CR_INIT_Pos) + +/* HCA SHA control: Last block flag */ +#define HCA_SHA_CR_LAST_Pos 5U +#define HCA_SHA_CR_LAST_Msk (1U << HCA_SHA_CR_LAST_Pos) + +/* HCA SHA control: Last block length */ +#define HCA_SHA_CR_LASTLEN_Pos 6U +#define HCA_SHA_CR_LASTLEN_Msk (0x7FU << HCA_SHA_CR_LASTLEN_Pos) + +/* HCA SHA control: Busy bit */ +#define HCA_SHA_CR_BUSY_Pos 31U +#define HCA_SHA_CR_BUSY_Msk (1U << HCA_SHA_CR_BUSY_Pos) + +/* HCA HASH result from SHA: Result form SHA 0 */ +#define HCA_SHA_HASH_HASH_0_Pos 0U +#define HCA_SHA_HASH_HASH_0_Msk 0xFFFFFFFFFFFFFFFFU + +/* HCA HASH result from SHA: Result form SHA 1 */ +#define HCA_SHA_HASH_HASH_1_Pos 0U +#define HCA_SHA_HASH_HASH_1_Msk 0xFFFFFFFFFFFFFFFFU + +/* HCA HASH result from SHA: Result form SHA 2 */ +#define HCA_SHA_HASH_HASH_2_Pos 0U +#define HCA_SHA_HASH_HASH_2_Msk 0xFFFFFFFFFFFFFFFFU + +/* HCA HASH result from SHA: Result form SHA 3 */ +#define HCA_SHA_HASH_HASH_3_Pos 0U +#define HCA_SHA_HASH_HASH_3_Msk 0xFFFFFFFFFFFFFFFFU + +/* HCA HASH result from SHA: Result form SHA 4 */ +#define HCA_SHA_HASH_HASH_4_Pos 0U +#define HCA_SHA_HASH_HASH_4_Msk 0xFFFFFFFFFFFFFFFFU + +/* HCA HASH result from SHA: Result form SHA 5 */ +#define HCA_SHA_HASH_HASH_5_Pos 0U +#define HCA_SHA_HASH_HASH_5_Msk 0xFFFFFFFFFFFFFFFFU + +/* HCA HASH result from SHA: Result form SHA 6 */ +#define HCA_SHA_HASH_HASH_6_Pos 0U +#define HCA_SHA_HASH_HASH_6_Msk 0xFFFFFFFFFFFFFFFFU + +/* HCA HASH result from SHA: Result form SHA 7 */ +#define HCA_SHA_HASH_HASH_7_Pos 0U +#define HCA_SHA_HASH_HASH_7_Msk 0xFFFFFFFFFFFFFFFFU + +typedef struct _HCA_PKA { + __IM uint32_t REV; /**< Offset: 0x00 (R/ ) PKA Revision */ + uint32_t _reserved0; + __IOM uint32_t CR; /**< Offset: 0x08 (R/W) PKA control cegister */ + uint32_t _reserved1; + __IM uint32_t SR; /**< Offset: 0x10 (R/ ) PKA control */ + uint32_t _reserved2; + __IOM uint64_t OPA; /**< Offset: 0x18 (R/W) PKA Operand A Address */ + uint32_t _reserved3[2U]; + __IOM uint64_t OPB; /**< Offset: 0x28 (R/W) PKA Operand B Address */ + uint32_t _reserved4[2U]; + __IOM uint64_t RES; /**< Offset: 0x38 (R/W) PKA Result Address */ + uint32_t _reserved5[2U]; + __IOM uint64_t MOD; /**< Offset: 0x48 (R/W) PKA Modulo Address */ +} HCA_PKA_Type; + +/** + * Structure type to access PKA Revision (PKA_REV) + */ +typedef union _HCA_PKA_REV { + struct { + uint32_t PATCHREV:4; /**< bit: 0..3 PKA Patch Revision */ + uint32_t MINORREV:4; /**< bit: 4..7 PKA Minor Revision */ + uint32_t MAJORREV:4; /**< bit: 8..11 PKA Major Revision */ + uint32_t _reserved0:4; /**< bit: 12..15 (reserved) */ + uint32_t CONFIG:2; /**< bit: 16..17 PKA Configuration */ + uint32_t _reserved1:14; /**< bit: 18..31 (reserved) */ + } b; /**< Structure used for bit access */ + uint32_t w; /**< Structure used for word access */ +} HCA_PKA_REV_Type; + +/* HCA PKA Revision: Patch Revision */ +#define HCA_PKA_REV_PATCHREV_Pos 0U +#define HCA_PKA_REV_PATCHREV_Msk (0xFU << HCA_PKA_REV_PATCHREV_Pos) + +/* HCA PKA Revision: Minor Revision */ +#define HCA_PKA_REV_MINORREV_Pos 4U +#define HCA_PKA_REV_MINORREV_Msk (0xFU << HCA_PKA_REV_MINORREV_Pos) + +/* HCA PKA Revision: Major Revision */ +#define HCA_PKA_REV_MAJORREV_Pos 8U +#define HCA_PKA_REV_MAJORREV_Msk (0xFU << HCA_PKA_REV_MAJORREV_Pos) + +/* HCA PKA Revision: Configuration */ +#define HCA_PKA_REV_CONFIG_Pos 16U +#define HCA_PKA_REV_CONFIG_Msk (3U << HCA_PKA_REV_CONFIG_Pos) + +/** + * Structure type to access PKA control cegister (PKA_CR) + */ +typedef union _HCA_PKA_CR { + struct { + uint32_t EN:1; /**< bit: 0 PKA Enable */ + uint32_t START:1; /**< bit: 1 Operation Start */ + uint32_t MODULOLOAD:1; /**< bit: 2 Load Modulo */ + uint32_t SRTA:2; /**< bit: 3..4 Store Result To ALU */ + uint32_t FOP:2; /**< bit: 5..6 Fetch Operands */ + uint32_t NSRTM:1; /**< bit: 7 Not Store Result To Memory */ + uint32_t OPCODE:3; /**< bit: 8..10 Operation Opcode */ + uint32_t _reserved0:4; /**< bit: 11..14 (reserved) */ + uint32_t PKAIE:1; /**< bit: 15 PKA Interrupt Enable */ + uint32_t OPW:10; /**< bit: 16..25 Operation Width */ + uint32_t _reserved1:5; /**< bit: 26..30 (reserved) */ + uint32_t PKAERMEM:1; /**< bit: 31 PKA Erase Memory */ + } b; /**< Structure used for bit access */ + uint32_t w; /**< Structure used for word access */ +} HCA_PKA_CR_Type; + +/* HCA PKA control cegister: Enable */ +#define HCA_PKA_CR_EN_Pos 0U +#define HCA_PKA_CR_EN_Msk (1U << HCA_PKA_CR_EN_Pos) + +/* HCA PKA control cegister: Operation Start */ +#define HCA_PKA_CR_START_Pos 1U +#define HCA_PKA_CR_START_Msk (1U << HCA_PKA_CR_START_Pos) + +/* HCA PKA control cegister: Load Modulo */ +#define HCA_PKA_CR_MODULOLOAD_Pos 2U +#define HCA_PKA_CR_MODULOLOAD_Msk (1U << HCA_PKA_CR_MODULOLOAD_Pos) + +/* HCA PKA control cegister: Store Result To ALU */ +#define HCA_PKA_CR_SRTA_Pos 3U +#define HCA_PKA_CR_SRTA_Msk (3U << HCA_PKA_CR_SRTA_Pos) + +/* HCA PKA control cegister: Fetch Operands */ +#define HCA_PKA_CR_FOP_Pos 5U +#define HCA_PKA_CR_FOP_Msk (3U << HCA_PKA_CR_FOP_Pos) + +/* HCA PKA control cegister: Not Store Result To Memory */ +#define HCA_PKA_CR_NSRTM_Pos 7U +#define HCA_PKA_CR_NSRTM_Msk (1U << HCA_PKA_CR_NSRTM_Pos) + +/* HCA PKA control cegister: Operation Opcode */ +#define HCA_PKA_CR_OPCODE_Pos 8U +#define HCA_PKA_CR_OPCODE_Msk (7U << HCA_PKA_CR_OPCODE_Pos) + +/* HCA PKA control cegister: Interrupt Enable */ +#define HCA_PKA_CR_PKAIE_Pos 15U +#define HCA_PKA_CR_PKAIE_Msk (1U << HCA_PKA_CR_PKAIE_Pos) + +/* HCA PKA control cegister: Operation Width */ +#define HCA_PKA_CR_OPW_Pos 16U +#define HCA_PKA_CR_OPW_Msk (0x3FFU << HCA_PKA_CR_OPW_Pos) + +/* HCA PKA control cegister: Erase Memory */ +#define HCA_PKA_CR_PKAERMEM_Pos 31U +#define HCA_PKA_CR_PKAERMEM_Msk (1U << HCA_PKA_CR_PKAERMEM_Pos) + +/** + * Structure type to access PKA control (PKA_SR) + */ +typedef union _HCA_PKA_SR { + struct { + uint32_t BUSY:1; /**< bit: 0 PKA Busy */ + uint32_t ALUNEG:2; /**< bit: 1..2 PKA ALU1/2 SIGN */ + uint32_t _reserved0:29; /**< bit: 3..31 (reserved) */ + } b; /**< Structure used for bit access */ + uint32_t w; /**< Structure used for word access */ +} HCA_PKA_SR_Type; + +/* HCA PKA control: Busy */ +#define HCA_PKA_SR_BUSY_Pos 0U +#define HCA_PKA_SR_BUSY_Msk (1U << HCA_PKA_SR_BUSY_Pos) + +/* HCA PKA control: ALU1/2 SIGN */ +#define HCA_PKA_SR_ALUNEG_Pos 1U +#define HCA_PKA_SR_ALUNEG_Msk (3U << HCA_PKA_SR_ALUNEG_Pos) + +/* HCA PKA Operand A Address: Operand A Address */ +#define HCA_PKA_OPA_OPA_Pos 0U +#define HCA_PKA_OPA_OPA_Msk 0xFFFFFFFFU + +/* HCA PKA Operand B Address: Operand B Address */ +#define HCA_PKA_OPB_OPB_Pos 0U +#define HCA_PKA_OPB_OPB_Msk 0xFFFFFFFFU + +/* HCA PKA Result Address: Operation Result Address */ +#define HCA_PKA_RES_RES_Pos 0U +#define HCA_PKA_RES_RES_Msk 0xFFFFFFFFU + +/* HCA PKA Modulo Address: MODULO Address */ +#define HCA_PKA_MOD_MOD_Pos 0U +#define HCA_PKA_MOD_MOD_Msk 0xFFFFFFFFU + +typedef struct _HCA_TRNG { + __IM uint32_t REV; /**< Offset: 0x00 (R/ ) TRNG Revision */ + uint32_t _reserved0; + __IOM uint32_t CR; /**< Offset: 0x08 (R/W) TRNG control */ + uint32_t _reserved1; + __IOM uint32_t SR; /**< Offset: 0x10 (R/W) TRNG status */ + uint32_t _reserved2; + __IM uint32_t DATA; /**< Offset: 0x18 (R/ ) TRNG data */ + uint32_t _reserved3; + __IOM uint32_t TRIM; /**< Offset: 0x20 (R/W) TRNG trim */ + uint32_t _reserved4; + __IM uint32_t OSC1_CNT; /**< Offset: 0x28 (R/ ) TRNG osc1 counter */ + uint32_t _reserved5; + __IM uint32_t OSC2_CNT; /**< Offset: 0x30 (R/ ) TRNG osc1 counter */ +} HCA_TRNG_Type; + +/** + * Structure type to access TRNG Revision (TRNG_REV) + */ +typedef union _HCA_TRNG_REV { + struct { + uint32_t PATCHREV:4; /**< bit: 0..3 TRNG Patch Revision */ + uint32_t MINORREV:4; /**< bit: 4..7 TRNG Minor Revision */ + uint32_t MAJORREV:4; /**< bit: 8..11 TRNG Major Revision */ + uint32_t _reserved0:20; /**< bit: 12..31 (reserved) */ + } b; /**< Structure used for bit access */ + uint32_t w; /**< Structure used for word access */ +} HCA_TRNG_REV_Type; + +/* HCA TRNG Revision: Patch Revision */ +#define HCA_TRNG_REV_PATCHREV_Pos 0U +#define HCA_TRNG_REV_PATCHREV_Msk (0xFU << HCA_TRNG_REV_PATCHREV_Pos) + +/* HCA TRNG Revision: Minor Revision */ +#define HCA_TRNG_REV_MINORREV_Pos 4U +#define HCA_TRNG_REV_MINORREV_Msk (0xFU << HCA_TRNG_REV_MINORREV_Pos) + +/* HCA TRNG Revision: Major Revision */ +#define HCA_TRNG_REV_MAJORREV_Pos 8U +#define HCA_TRNG_REV_MAJORREV_Msk (0xFU << HCA_TRNG_REV_MAJORREV_Pos) + +/** + * Structure type to access TRNG control (TRNG_CR) + */ +typedef union _HCA_TRNG_CR { + struct { + uint32_t HTSTART:1; /**< bit: 0 Health tests start */ + uint32_t RNDIRQEN:1; /**< bit: 1 Random IRQ enable */ + uint32_t HTIRQEN:1; /**< bit: 2 Health Tests IRQ enable */ + uint32_t BURSTEN:1; /**< bit: 3 Burst mode enable */ + uint32_t _reserved0:4; /**< bit: 4..7 (reserved) */ + uint32_t STARTOSCTEST:1; /**< bit: 8 Oscillator test start */ + uint32_t TESTOUTSEL:2; /**< bit: 9..10 Test Oscillator in free running mode */ + uint32_t _reserved1:5; /**< bit: 11..15 (reserved) */ + uint32_t OSCTESTCNT:5; /**< bit: 16..20 Oscillator test counter */ + uint32_t _reserved2:11; /**< bit: 21..31 (reserved) */ + } b; /**< Structure used for bit access */ + uint32_t w; /**< Structure used for word access */ +} HCA_TRNG_CR_Type; + +/* HCA TRNG control: Health tests start */ +#define HCA_TRNG_CR_HTSTART_Pos 0U +#define HCA_TRNG_CR_HTSTART_Msk (1U << HCA_TRNG_CR_HTSTART_Pos) + +/* HCA TRNG control: Random IRQ enable */ +#define HCA_TRNG_CR_RNDIRQEN_Pos 1U +#define HCA_TRNG_CR_RNDIRQEN_Msk (1U << HCA_TRNG_CR_RNDIRQEN_Pos) + +/* HCA TRNG control: Health Tests IRQ enable */ +#define HCA_TRNG_CR_HTIRQEN_Pos 2U +#define HCA_TRNG_CR_HTIRQEN_Msk (1U << HCA_TRNG_CR_HTIRQEN_Pos) + +/* HCA TRNG control: Burst mode enable */ +#define HCA_TRNG_CR_BURSTEN_Pos 3U +#define HCA_TRNG_CR_BURSTEN_Msk (1U << HCA_TRNG_CR_BURSTEN_Pos) + +/* HCA TRNG control: Oscillator test start */ +#define HCA_TRNG_CR_STARTOSCTEST_Pos 8U +#define HCA_TRNG_CR_STARTOSCTEST_Msk (1U << HCA_TRNG_CR_STARTOSCTEST_Pos) + +/* HCA TRNG control: Test Oscillator in free running mode */ +#define HCA_TRNG_CR_TESTOUTSEL_Pos 9U +#define HCA_TRNG_CR_TESTOUTSEL_Msk (3U << HCA_TRNG_CR_TESTOUTSEL_Pos) + +/* HCA TRNG control: Oscillator test counter */ +#define HCA_TRNG_CR_OSCTESTCNT_Pos 16U +#define HCA_TRNG_CR_OSCTESTCNT_Msk (0x1FU << HCA_TRNG_CR_OSCTESTCNT_Pos) + +/** + * Structure type to access TRNG status (TRNG_SR) + */ +typedef union _HCA_TRNG_SR { + struct { + uint32_t RNDRDY:1; /**< bit: 0 32-bit random data is ready to read from TRNG_DATA register */ + uint32_t SRCS:1; /**< bit: 1 Entropy source status (0:running, 1:failed) */ + uint32_t HTR:1; /**< bit: 2 Health test ready (0:done, 1:on-going) */ + uint32_t HTS:1; /**< bit: 3 Health test status (0:pass, 1:fail) */ + uint32_t _reserved0:4; /**< bit: 4..7 (reserved) */ + uint32_t ADAPFAIL:1; /**< bit: 8 Adaptative test failing bit */ + uint32_t REPFAIL:1; /**< bit: 9 Repetition test failing bit */ + uint32_t OSCTESTDONE:1; /**< bit: 10 Osc Test Done */ + uint32_t _reserved1:21; /**< bit: 11..31 (reserved) */ + } b; /**< Structure used for bit access */ + uint32_t w; /**< Structure used for word access */ +} HCA_TRNG_SR_Type; + +/* HCA TRNG status: 32-bit random data is ready to read from TRNG_DATA register */ +#define HCA_TRNG_SR_RNDRDY_Pos 0U +#define HCA_TRNG_SR_RNDRDY_Msk (1U << HCA_TRNG_SR_RNDRDY_Pos) + +/* HCA TRNG status: Entropy source status (0:running, 1:failed) */ +#define HCA_TRNG_SR_SRCS_Pos 1U +#define HCA_TRNG_SR_SRCS_Msk (1U << HCA_TRNG_SR_SRCS_Pos) + +/* HCA TRNG status: Health test ready (0:done, 1:on-going) */ +#define HCA_TRNG_SR_HTR_Pos 2U +#define HCA_TRNG_SR_HTR_Msk (1U << HCA_TRNG_SR_HTR_Pos) + +/* HCA TRNG status: Health test status (0:pass, 1:fail) */ +#define HCA_TRNG_SR_HTS_Pos 3U +#define HCA_TRNG_SR_HTS_Msk (1U << HCA_TRNG_SR_HTS_Pos) + +/* HCA TRNG status: Adaptative test failing bit */ +#define HCA_TRNG_SR_ADAPFAIL_Pos 8U +#define HCA_TRNG_SR_ADAPFAIL_Msk (1U << HCA_TRNG_SR_ADAPFAIL_Pos) + +/* HCA TRNG status: Repetition test failing bit */ +#define HCA_TRNG_SR_REPFAIL_Pos 9U +#define HCA_TRNG_SR_REPFAIL_Msk (1U << HCA_TRNG_SR_REPFAIL_Pos) + +/* HCA TRNG status: Osc Test Done */ +#define HCA_TRNG_SR_OSCTESTDONE_Pos 10U +#define HCA_TRNG_SR_OSCTESTDONE_Msk (1U << HCA_TRNG_SR_OSCTESTDONE_Pos) + +/* HCA TRNG data: Data register */ +#define HCA_TRNG_DATA_DATA_Pos 0U +#define HCA_TRNG_DATA_DATA_Msk 0xFFFFFFFFU + +/** + * Structure type to access TRNG trim (TRNG_TRIM) + */ +typedef union _HCA_TRNG_TRIM { + struct { + uint32_t TRIM:28; /**< bit: 0..27 TRNG trim value */ + uint32_t _reserved0:3; /**< bit: 28..30 (reserved) */ + uint32_t LOCK:1; /**< bit: 31 TRNG trim lock (0:RW, 1:R) */ + } b; /**< Structure used for bit access */ + uint32_t w; /**< Structure used for word access */ +} HCA_TRNG_TRIM_Type; + +/* HCA TRNG trim: Trim value */ +#define HCA_TRNG_TRIM_TRIM_Pos 0U +#define HCA_TRNG_TRIM_TRIM_Msk (0xFFFFFFFU << HCA_TRNG_TRIM_TRIM_Pos) + +/* HCA TRNG trim: Trim lock (0:RW, 1:R) */ +#define HCA_TRNG_TRIM_LOCK_Pos 31U +#define HCA_TRNG_TRIM_LOCK_Msk (1U << HCA_TRNG_TRIM_LOCK_Pos) + +/* HCA TRNG osc1 counter: OSC1 counter register */ +#define HCA_TRNG_OSC1_CNT_OSC1CNT_Pos 0U +#define HCA_TRNG_OSC1_CNT_OSC1CNT_Msk 0xFFFFFFFFU + +/* HCA TRNG osc1 counter: OSC2 counter register */ +#define HCA_TRNG_OSC2_CNT_OSC2CNT_Pos 0U +#define HCA_TRNG_OSC2_CNT_OSC2CNT_Msk 0xFFFFFFFFU + + +#endif /* SIFIVE_HCA1_REGS_H_ */ diff --git a/arch/riscv32/fe310/src/freedom-metal/metal/drivers/sifive_i2c0.h b/arch/riscv32/fe310/src/freedom-metal/metal/drivers/sifive_i2c0.h new file mode 100644 index 00000000..8fbbe21e --- /dev/null +++ b/arch/riscv32/fe310/src/freedom-metal/metal/drivers/sifive_i2c0.h @@ -0,0 +1,24 @@ +/* Copyright 2019 SiFive, Inc */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#ifndef METAL__DRIVERS__SIFIVE_I2C0_H +#define METAL__DRIVERS__SIFIVE_I2C0_H + +#include +#include + +struct __metal_driver_vtable_sifive_i2c0 { + const struct metal_i2c_vtable i2c; +}; + +__METAL_DECLARE_VTABLE(__metal_driver_vtable_sifive_i2c0) + +struct __metal_driver_sifive_i2c0 { + struct metal_i2c i2c; + unsigned int init_done; + unsigned int baud_rate; + metal_clock_callback pre_rate_change_callback; + metal_clock_callback post_rate_change_callback; +}; + +#endif diff --git a/arch/riscv32/fe310/src/freedom-metal/metal/drivers/sifive_l2pf0.h b/arch/riscv32/fe310/src/freedom-metal/metal/drivers/sifive_l2pf0.h new file mode 100644 index 00000000..63c8e653 --- /dev/null +++ b/arch/riscv32/fe310/src/freedom-metal/metal/drivers/sifive_l2pf0.h @@ -0,0 +1,78 @@ +/* Copyright 2020 SiFive, Inc */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#ifndef METAL__DRIVERS__SIFIVE_L2PF0_H +#define METAL__DRIVERS__SIFIVE_L2PF0_H + +/*! + * @file sifive_l2pf0.h + * + * @brief API for configuring the SiFive L2 prefetcher. + */ + +#include + +/*! @brief L2 prefetcher configuration */ +typedef struct { + /* Enable L2 hardware prefetcher */ + uint8_t HwPrefetchEnable; + + /* Only works when CrossPageEn === 0. + Cross Page optimization disable: + 0 -> Entry goes into Pause state while crossing Page boundary. + Next time when the demand miss happens on the same page, it doesn’t need + to train again. 1 -> The entry is invalidated in case of a cross page. */ + uint8_t CrossPageOptmDisable; + + /* Enable prefetches to cross pages */ + uint8_t CrossPageEn; + + /* Age-out mechanism enable */ + uint8_t AgeOutEn; + + uint32_t PrefetchDistance; + + uint32_t MaxAllowedDistance; + + /* Linear to exponential threshold */ + uint32_t LinToExpThreshold; + + /* No. of non-matching loads to edge out an entry */ + uint32_t NumLdsToAgeOut; + + /* Threshold no. of Fullness (L2 MSHRs used/ total available) to stop + * sending hits */ + uint32_t QFullnessThreshold; + + /* Threshold no. of CacheHits for evicting SPF entry */ + uint32_t HitCacheThreshold; + + /* Threshold no. of MSHR hits for increasing SPF distance */ + uint32_t hitMSHRThreshold; + + /* Size of the comparison window for address matching */ + uint32_t Window; + +} sifive_l2pf0_config; + +/*! @brief Enable L2 hardware prefetcher unit. + * @param None. + * @return None.*/ +void sifive_l2pf0_enable(void); + +/*! @brief Disable L2 hardware prefetcher unit. + * @param None. + * @return None.*/ +void sifive_l2pf0_disable(void); + +/*! @brief Get currently active L2 prefetcher configuration. + * @param config Pointer to user specified configuration structure. + * @return None.*/ +void sifive_l2pf0_get_config(sifive_l2pf0_config *config); + +/*! @brief Enables fine grain access to L2 prefetcher configuration. + * @param config Pointer to user structure with values to be set. + * @return None.*/ +void sifive_l2pf0_set_config(sifive_l2pf0_config *config); + +#endif diff --git a/arch/riscv32/fe310/src/freedom-metal/metal/drivers/sifive_l2pf1.h b/arch/riscv32/fe310/src/freedom-metal/metal/drivers/sifive_l2pf1.h new file mode 100644 index 00000000..3e917699 --- /dev/null +++ b/arch/riscv32/fe310/src/freedom-metal/metal/drivers/sifive_l2pf1.h @@ -0,0 +1,94 @@ +/* Copyright 2021 SiFive, Inc */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#ifndef METAL__DRIVERS__SIFIVE_L2PF1_H +#define METAL__DRIVERS__SIFIVE_L2PF1_H + +/*! + * @file sifive_l2pf1.h + * + * @brief API for configuring the SiFive L2 stride prefetcher. + */ + +#include + +/*! @brief L2 stride prefetcher configuration. */ +typedef struct { + /* Enable hardware prefetcher support for scalar Loads. */ + uint8_t ScalarLoadSupportEn; + + /* Initial prefetch distance. */ + uint8_t Dist; + + /* Maximum allowed prefetch Distance. */ + uint32_t MaxAllowedDist; + + /* + * Threshold when SPF distance increase changes + * from linear (+1) to exponential (*2). + */ + uint8_t LinToExpThrd; + + /* Enable prefetches to cross pages. */ + uint8_t CrossPageEn; + + /* + * Threshold for forgiving loads with + * mismatching strides when SPF is in trained state. + */ + uint8_t ForgiveThrd; + + /* + * Threshold no. of Fullness (L2 MSHRs used/ total available) to stop + * sending hits. + */ + uint8_t QFullnessThrd; + + /* No. of CacheHits for evicting SPF entry. */ + uint8_t HitCacheThrd; + + /* Threshold no. of MSHR hits for increasing SPF distance. */ + uint8_t HitMSHRThrd; + + /* Size of the comparison window for address matching. */ + uint8_t Window; + + /* Prefetch-enable for Scalar Stores. */ + uint8_t ScalarStoreSupportEn; + + /* Prefetch-enable for Vector Loads (Gets). */ + uint8_t VectorLoadSupportEn; + + /* Prefetch-enable for Vector Stores (Puts). */ + uint8_t VectorStoreSupportEn; +} sifive_l2pf1_config; + +/* + * ! @brief Disable L2 hardware stride prefetcher unit. + * @param None. + * @return None. + */ +void sifive_l2pf1_disable(void); + +/* + * ! @brief Get currently active L2 stride prefetcher configuration. + * @param config Pointer to user specified configuration structure. + * @return None. + */ +void sifive_l2pf1_get_config(sifive_l2pf1_config *config); + +/* + * ! @brief Enables fine grain access to L2 stride prefetcher configuration. + * @param config Pointer to user structure with values to be set. + * @return None. + */ +void sifive_l2pf1_set_config(sifive_l2pf1_config *config); + +/* + * ! @brief Initialize L2 hardware stride prefetcher unit. + * @param None. + * @return None. + */ +void sifive_l2pf1_init(void); + +#endif diff --git a/arch/riscv32/fe310/src/freedom-metal/metal/drivers/sifive_local-external-interrupts0.h b/arch/riscv32/fe310/src/freedom-metal/metal/drivers/sifive_local-external-interrupts0.h new file mode 100644 index 00000000..320ab10d --- /dev/null +++ b/arch/riscv32/fe310/src/freedom-metal/metal/drivers/sifive_local-external-interrupts0.h @@ -0,0 +1,21 @@ +/* Copyright 2018 SiFive, Inc */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#ifndef METAL__DRIVERS__SIFIVE_EXTERNAL_INTERRUPTS0_H +#define METAL__DRIVERS__SIFIVE_EXTERNAL_INTERRUPTS0_H + +#include +#include + +struct __metal_driver_vtable_sifive_local_external_interrupts0 { + struct metal_interrupt_vtable local0_vtable; +}; + +__METAL_DECLARE_VTABLE(__metal_driver_vtable_sifive_local_external_interrupts0) + +struct __metal_driver_sifive_local_external_interrupts0 { + struct metal_interrupt irc; + int init_done; +}; + +#endif diff --git a/arch/riscv32/fe310/src/freedom-metal/metal/drivers/sifive_pl2cache0.h b/arch/riscv32/fe310/src/freedom-metal/metal/drivers/sifive_pl2cache0.h new file mode 100644 index 00000000..1fbe1a66 --- /dev/null +++ b/arch/riscv32/fe310/src/freedom-metal/metal/drivers/sifive_pl2cache0.h @@ -0,0 +1,137 @@ +/* Copyright 2021 SiFive, Inc */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#ifndef METAL__DRIVERS__SIFIVE_PL2CACHE0_H +#define METAL__DRIVERS__SIFIVE_PL2CACHE0_H + +/*! + * @file sifive_pl2cache0.h + * + * @brief API for configuring the SiFive private L2 cache controller + */ + +#include +#include +#include + +/*! @brief Cache configuration data */ +typedef struct { + uint32_t num_bank; + uint32_t num_ways; + uint32_t num_sets; + uint32_t block_size; +} sifive_pl2cache0_config; + +typedef union _sifive_pl2cache0_configbits { + struct { + uint32_t _reserved0 : 3; + uint32_t cleanEvictEnable : 1; + uint32_t _reserved1 : 5; + uint32_t l2AvoidL1LineDisable : 1; + uint32_t _reserved2 : 6; + uint32_t softwareEccInjectEnable : 1; + uint32_t errInjectOnWriteEnable : 1; + uint32_t errInjectOnReadEnable : 1; + uint32_t dataUceInjectEnable : 1; + uint32_t dirUceInjectEnable : 1; + uint32_t _reserved3 : 11; + } b; /**< Structure used for bit access */ + uint32_t w; /**< Structure used for 32bits access */ +} sifive_pl2cache0_configbits; + +/*! @brief Set of values for ECC error target */ +typedef enum { + SIFIVE_PL2CACHE0_ECC_ERROR_DATA = 0, + SIFIVE_PL2CACHE0_ECC_ERROR_DIR = 1, + SIFIVE_PL2CACHE0_ECC_ERROR_LRU = 2, +} sifive_pl2cache0_ecc_errtarget_t; + +/*! @brief Set of values for ECC error direction */ +typedef enum { + SIFIVE_PL2CACHE0_ECC_ERROR_READ = 0, + SIFIVE_PL2CACHE0_ECC_ERROR_WRITE = 1, +} sifive_pl2cache0_ecc_errdirection_t; + +/*! @brief Set of values for ECC error type */ +typedef enum { + SIFIVE_PL2CACHE0_ECC_ERROR_CORRECTABLE = 0, + SIFIVE_PL2CACHE0_ECC_ERROR_UNCORRECTABLE = 1, +} sifive_pl2cache0_ecc_errtype_t; + +/*! @brief Set theCleanEvicenable bit of the Private L2 cache controller. + * @param val boolean parameter true(enable) or false(desable). + * @return None.*/ +void sifive_pl2cache0_set_cleanEvictenale_bit(bool val); + +/*! @brief Initialize Private L2 cache controller. + * @param None. + * @return None.*/ +void sifive_pl2cache0_init(void); + +/*! @brief Get cache configuration data. + * @param config User specified data buffer. + * @return None.*/ +void sifive_pl2cache0_get_config(sifive_pl2cache0_config *config); + +/*! @brief Get currently active cache ways. + * @param None. + * @return Number of cache ways enabled.*/ +uint32_t sifive_pl2cache0_get_enabled_ways(void); + +/*! @brief Enable specified cache ways. + * @param ways Number of ways to be enabled. + * @return 0 If no error.*/ +int sifive_pl2cache0_set_enabled_ways(uint32_t ways); + +/*! @brief Inject ECC error into data or meta-data. + * @param bitindex Bit index to be corrupted on next cache operation. + * @param target ECC error target location. + * @param dir ECC error direction. + * @param type ECC error target type. + * @return None.*/ +void sifive_pl2cache0_inject_ecc_error(uint32_t bitindex, + sifive_pl2cache0_ecc_errtarget_t target, + sifive_pl2cache0_ecc_errdirection_t dir, + sifive_pl2cache0_ecc_errtype_t type); + +/*! @brief Flush out entire cache block containing given address. + * @param flush_addr Address for the cache block to be flushed. + * @return None.*/ +void sifive_pl2cache0_flush(uintptr_t flush_addr); + +/*! @brief Get the number of outstanding cache flush requests. + * @return Outstanding flush requests .*/ +uint32_t sifive_pl2cache0_get_flush_count(void); + +/*! @brief Select cache performance events to be counted. + * @param counter Cache performance monitor counter index. + * @param mask Event selection mask. + * @return None.*/ +void sifive_pl2cache0_set_pmevent_selector(uint32_t counter, uint64_t mask); + +/*! @brief Get currently set events for the given counter index. + * @param counter Cache performance monitor counter index. + * @return Event selection mask.*/ +uint64_t sifive_pl2cache0_get_pmevent_selector(uint32_t counter); + +/*! @brief Clears specified cache performance counter. + * @param counter Cache performance monitor counter index. + * @return None.*/ +void sifive_pl2cache0_clr_pmevent_counter(uint32_t counter); + +/*! @brief Reads specified cache performance counter. + * @param counter Cache performance monitor counter index. + * @return Counter value.*/ +uint64_t sifive_pl2cache0_get_pmevent_counter(uint32_t counter); + +/*! @brief Select cache clients to be excluded from performance monitoring. + * @param mask Client disable mask. + * @return None.*/ +void sifive_pl2cache0_set_client_filter(uint64_t mask); + +/*! @brief Get currently set cache client disable mask. + * @param None. + * @return Client disable mask.*/ +uint64_t sifive_pl2cache0_get_client_filter(void); + +#endif diff --git a/arch/riscv32/fe310/src/freedom-metal/metal/drivers/sifive_prci0.h b/arch/riscv32/fe310/src/freedom-metal/metal/drivers/sifive_prci0.h new file mode 100644 index 00000000..cd0df7b5 --- /dev/null +++ b/arch/riscv32/fe310/src/freedom-metal/metal/drivers/sifive_prci0.h @@ -0,0 +1,21 @@ +/* Copyright 2021 SiFive, Inc */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#ifndef METAL__DRIVERS__SIFIVE_PRCI0_H +#define METAL__DRIVERS__SIFIVE_PRCI0_H + +#include +#include + +struct __metal_driver_vtable_sifive_prci0 { + const struct metal_prci_vtable prci; +}; + +__METAL_DECLARE_VTABLE(__metal_driver_vtable_sifive_prci0) + +struct __metal_driver_sifive_prci0 { + struct metal_prci prci; + int init_done; +}; + +#endif /* METAL__DRIVERS__SIFIVE_PRCI0_H */ diff --git a/arch/riscv32/fe310/src/freedom-metal/metal/drivers/sifive_pwm0.h b/arch/riscv32/fe310/src/freedom-metal/metal/drivers/sifive_pwm0.h new file mode 100644 index 00000000..caa77440 --- /dev/null +++ b/arch/riscv32/fe310/src/freedom-metal/metal/drivers/sifive_pwm0.h @@ -0,0 +1,29 @@ +/* Copyright 2020 SiFive, Inc */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#ifndef METAL__DRIVERS__SIFIVE_PWM0_H +#define METAL__DRIVERS__SIFIVE_PWM0_H + +#include +#include + +struct __metal_driver_vtable_sifive_pwm0 { + const struct metal_pwm_vtable pwm; +}; + +/* Max possible PWM channel count */ +#define METAL_MAX_PWM_CHANNELS 16 + +__METAL_DECLARE_VTABLE(__metal_driver_vtable_sifive_pwm0) + +struct __metal_driver_sifive_pwm0 { + struct metal_pwm pwm; + unsigned int max_count; + unsigned int count_val; + unsigned int freq; + unsigned int duty[METAL_MAX_PWM_CHANNELS]; + metal_clock_callback pre_rate_change_callback; + metal_clock_callback post_rate_change_callback; +}; + +#endif diff --git a/arch/riscv32/fe310/src/freedom-metal/metal/drivers/sifive_remapper2.h b/arch/riscv32/fe310/src/freedom-metal/metal/drivers/sifive_remapper2.h new file mode 100644 index 00000000..834eb282 --- /dev/null +++ b/arch/riscv32/fe310/src/freedom-metal/metal/drivers/sifive_remapper2.h @@ -0,0 +1,20 @@ +/* Copyright 2021 SiFive, Inc */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#ifndef METAL__DRIVERS__SIFIVE_REMAPPER2_H +#define METAL__DRIVERS__SIFIVE_REMAPPER2_H + +#include +#include + +struct __metal_driver_vtable_sifive_remapper2 { + const struct metal_remapper_vtable remapper; +}; + +__METAL_DECLARE_VTABLE(__metal_driver_vtable_sifive_remapper2) + +struct __metal_driver_sifive_remapper2 { + struct metal_remapper remapper; +}; + +#endif /* METAL__DRIVERS__SIFIVE_REMAPPER2_H */ diff --git a/arch/riscv32/fe310/src/freedom-metal/metal/drivers/sifive_rtc0.h b/arch/riscv32/fe310/src/freedom-metal/metal/drivers/sifive_rtc0.h new file mode 100644 index 00000000..a35ab9a0 --- /dev/null +++ b/arch/riscv32/fe310/src/freedom-metal/metal/drivers/sifive_rtc0.h @@ -0,0 +1,26 @@ +/* Copyright 2019 SiFive, Inc */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#ifndef METAL__DRIVERS__SIFIVE_RTC0_H +#define METAL__DRIVERS__SIFIVE_RTC0_H + +#include +#include + +#include +#include +#include + +struct __metal_driver_vtable_sifive_rtc0 { + const struct metal_rtc_vtable rtc; +}; + +struct __metal_driver_sifive_rtc0; + +__METAL_DECLARE_VTABLE(__metal_driver_vtable_sifive_rtc0) + +struct __metal_driver_sifive_rtc0 { + const struct metal_rtc rtc; +}; + +#endif diff --git a/arch/riscv32/fe310/src/freedom-metal/metal/drivers/sifive_simuart0.h b/arch/riscv32/fe310/src/freedom-metal/metal/drivers/sifive_simuart0.h new file mode 100644 index 00000000..f6b73914 --- /dev/null +++ b/arch/riscv32/fe310/src/freedom-metal/metal/drivers/sifive_simuart0.h @@ -0,0 +1,29 @@ +/* Copyright 2020 SiFive, Inc */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#ifndef METAL__DRIVERS__SIFIVE_SIMUART0_H +#define METAL__DRIVERS__SIFIVE_SIMUART0_H + +#include +#include +#include +#include +#include +#include + +struct __metal_driver_vtable_sifive_simuart0 { + const struct metal_uart_vtable uart; +}; + +struct __metal_driver_sifive_simuart0; + +__METAL_DECLARE_VTABLE(__metal_driver_vtable_sifive_simuart0) + +struct __metal_driver_sifive_simuart0 { + struct metal_uart uart; + unsigned long baud_rate; + metal_clock_callback pre_rate_change_callback; + metal_clock_callback post_rate_change_callback; +}; + +#endif diff --git a/arch/riscv32/fe310/src/freedom-metal/metal/drivers/sifive_spi0.h b/arch/riscv32/fe310/src/freedom-metal/metal/drivers/sifive_spi0.h new file mode 100644 index 00000000..73527944 --- /dev/null +++ b/arch/riscv32/fe310/src/freedom-metal/metal/drivers/sifive_spi0.h @@ -0,0 +1,26 @@ +/* Copyright 2018 SiFive, Inc */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#ifndef METAL__DRIVERS__SIFIVE_SPI0_H +#define METAL__DRIVERS__SIFIVE_SPI0_H + +#include +#include +#include +#include +#include + +struct __metal_driver_vtable_sifive_spi0 { + const struct metal_spi_vtable spi; +}; + +__METAL_DECLARE_VTABLE(__metal_driver_vtable_sifive_spi0) + +struct __metal_driver_sifive_spi0 { + struct metal_spi spi; + unsigned long baud_rate; + metal_clock_callback pre_rate_change_callback; + metal_clock_callback post_rate_change_callback; +}; + +#endif diff --git a/arch/riscv32/fe310/src/freedom-metal/metal/drivers/sifive_test0.h b/arch/riscv32/fe310/src/freedom-metal/metal/drivers/sifive_test0.h new file mode 100644 index 00000000..debd3fb9 --- /dev/null +++ b/arch/riscv32/fe310/src/freedom-metal/metal/drivers/sifive_test0.h @@ -0,0 +1,20 @@ +/* Copyright 2018 SiFive, Inc */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#ifndef METAL__DRIVERS__SIFIVE_TEST0_H +#define METAL__DRIVERS__SIFIVE_TEST0_H + +#include +#include + +struct __metal_driver_vtable_sifive_test0 { + const struct __metal_shutdown_vtable shutdown; +}; + +__METAL_DECLARE_VTABLE(__metal_driver_vtable_sifive_test0) + +struct __metal_driver_sifive_test0 { + struct __metal_shutdown shutdown; +}; + +#endif diff --git a/arch/riscv32/fe310/src/freedom-metal/metal/drivers/sifive_trace.h b/arch/riscv32/fe310/src/freedom-metal/metal/drivers/sifive_trace.h new file mode 100644 index 00000000..3c67522f --- /dev/null +++ b/arch/riscv32/fe310/src/freedom-metal/metal/drivers/sifive_trace.h @@ -0,0 +1,23 @@ +/* Copyright 2019 SiFive, Inc */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#ifndef METAL__DRIVERS__SIFIVE_TRACE_H +#define METAL__DRIVERS__SIFIVE_TRACE_H + +#include +#include +#include + +struct __metal_driver_vtable_sifive_trace { + const struct metal_uart_vtable uart; +}; + +struct __metal_driver_sifive_trace; + +__METAL_DECLARE_VTABLE(__metal_driver_vtable_sifive_trace) + +struct __metal_driver_sifive_trace { + struct metal_uart uart; +}; + +#endif /* METAL__DRIVERS__SIFIVE_TRACE_H */ diff --git a/arch/riscv32/fe310/src/freedom-metal/metal/drivers/sifive_uart0.h b/arch/riscv32/fe310/src/freedom-metal/metal/drivers/sifive_uart0.h new file mode 100644 index 00000000..2b38e463 --- /dev/null +++ b/arch/riscv32/fe310/src/freedom-metal/metal/drivers/sifive_uart0.h @@ -0,0 +1,29 @@ +/* Copyright 2018 SiFive, Inc */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#ifndef METAL__DRIVERS__SIFIVE_UART0_H +#define METAL__DRIVERS__SIFIVE_UART0_H + +#include +#include +#include +#include +#include +#include + +struct __metal_driver_vtable_sifive_uart0 { + const struct metal_uart_vtable uart; +}; + +struct __metal_driver_sifive_uart0; + +__METAL_DECLARE_VTABLE(__metal_driver_vtable_sifive_uart0) + +struct __metal_driver_sifive_uart0 { + struct metal_uart uart; + unsigned long baud_rate; + metal_clock_callback pre_rate_change_callback; + metal_clock_callback post_rate_change_callback; +}; + +#endif diff --git a/arch/riscv32/fe310/src/freedom-metal/metal/drivers/sifive_wdog0.h b/arch/riscv32/fe310/src/freedom-metal/metal/drivers/sifive_wdog0.h new file mode 100644 index 00000000..bb342458 --- /dev/null +++ b/arch/riscv32/fe310/src/freedom-metal/metal/drivers/sifive_wdog0.h @@ -0,0 +1,26 @@ +/* Copyright 2018 SiFive, Inc */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#ifndef METAL__DRIVERS__SIFIVE_WDOG0_H +#define METAL__DRIVERS__SIFIVE_WDOG0_H + +#include +#include + +#include +#include +#include + +struct __metal_driver_vtable_sifive_wdog0 { + const struct metal_watchdog_vtable watchdog; +}; + +struct __metal_driver_sifive_wdog0; + +__METAL_DECLARE_VTABLE(__metal_driver_vtable_sifive_wdog0) + +struct __metal_driver_sifive_wdog0 { + const struct metal_watchdog watchdog; +}; + +#endif diff --git a/arch/riscv32/fe310/src/freedom-metal/metal/drivers/ucb_htif0.h b/arch/riscv32/fe310/src/freedom-metal/metal/drivers/ucb_htif0.h new file mode 100644 index 00000000..210d0819 --- /dev/null +++ b/arch/riscv32/fe310/src/freedom-metal/metal/drivers/ucb_htif0.h @@ -0,0 +1,48 @@ +/* Copyright 2018 SiFive, Inc */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#ifndef METAL__DRIVERS__UCB_HTIF0_H +#define METAL__DRIVERS__UCB_HTIF0_H + +#include +#include +#include + +struct __metal_driver_vtable_ucb_htif0_shutdown { + const struct __metal_shutdown_vtable shutdown; +}; + +struct __metal_driver_vtable_ucb_htif0_uart { + const struct metal_uart_vtable uart; +}; + +struct __metal_driver_ucb_htif0; + +void __metal_driver_ucb_htif0_exit(const struct __metal_shutdown *test, + int code) __attribute__((noreturn)); + +void __metal_driver_ucb_htif0_init(struct metal_uart *uart, int baud_rate); +int __metal_driver_ucb_htif0_putc(struct metal_uart *uart, int c); +int __metal_driver_ucb_htif0_getc(struct metal_uart *uart, int *c); +int __metal_driver_ucb_htif0_get_baud_rate(struct metal_uart *guart); +int __metal_driver_ucb_htif0_set_baud_rate(struct metal_uart *guart, + int baud_rate); +struct metal_interrupt * +__metal_driver_ucb_htif0_interrupt_controller(struct metal_uart *uart); +int __metal_driver_ucb_htif0_get_interrupt_id(struct metal_uart *uart); + +__METAL_DECLARE_VTABLE(__metal_driver_vtable_ucb_htif0_shutdown) + +__METAL_DECLARE_VTABLE(__metal_driver_vtable_ucb_htif0_uart) + +struct __metal_driver_ucb_htif0_shutdown { + struct __metal_shutdown shutdown; + const struct __metal_driver_vtable_ucb_htif0_shutdown *vtable; +}; + +struct __metal_driver_ucb_htif0_uart { + struct metal_uart uart; + const struct __metal_driver_vtable_ucb_htif0_uart *vtable; +}; + +#endif diff --git a/arch/riscv32/fe310/src/freedom-metal/metal/gpio.h b/arch/riscv32/fe310/src/freedom-metal/metal/gpio.h new file mode 100644 index 00000000..df9adb45 --- /dev/null +++ b/arch/riscv32/fe310/src/freedom-metal/metal/gpio.h @@ -0,0 +1,287 @@ +/* Copyright 2019 SiFive, Inc */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#ifndef METAL__GPIO_H +#define METAL__GPIO_H + +#include +#include + +/*! + * @file gpio.h + * @brief API for manipulating general-purpose input/output + */ + +struct metal_gpio; + +struct __metal_gpio_vtable { + int (*disable_input)(struct metal_gpio *, long pins); + int (*enable_input)(struct metal_gpio *, long pins); + long (*input)(struct metal_gpio *); + long (*output)(struct metal_gpio *); + int (*disable_output)(struct metal_gpio *, long pins); + int (*enable_output)(struct metal_gpio *, long pins); + int (*output_set)(struct metal_gpio *, long value); + int (*output_clear)(struct metal_gpio *, long value); + int (*output_toggle)(struct metal_gpio *, long value); + int (*enable_io)(struct metal_gpio *, long pins, long dest); + int (*disable_io)(struct metal_gpio *, long pins); + int (*config_int)(struct metal_gpio *, long pins, int intr_type); + int (*clear_int)(struct metal_gpio *, long pins, int intr_type); + struct metal_interrupt *(*interrupt_controller)(struct metal_gpio *gpio); + int (*get_interrupt_id)(struct metal_gpio *gpio, int pin); +}; + +#define METAL_GPIO_INT_DISABLE 0 +#define METAL_GPIO_INT_RISING 1 +#define METAL_GPIO_INT_FALLING 2 +#define METAL_GPIO_INT_BOTH_EDGE 3 +#define METAL_GPIO_INT_LOW 4 +#define METAL_GPIO_INT_HIGH 5 +#define METAL_GPIO_INT_BOTH_LEVEL 6 +#define METAL_GPIO_INT_MAX 7 + +/*! + * @struct metal_gpio + * @brief The handle for a GPIO interface + */ +struct metal_gpio { + const struct __metal_gpio_vtable *vtable; +}; + +/*! + * @brief Get a GPIO device handle + * @param device_num The GPIO device index + * @return The GPIO device handle, or NULL if there is no device at that index + */ +struct metal_gpio *metal_gpio_get_device(unsigned int device_num); + +/*! + * @brief enable input on a pin + * @param gpio The handle for the GPIO interface + * @param pin The pin number indexed from 0 + * @return 0 if the input is successfully enabled + */ +__inline__ int metal_gpio_enable_input(struct metal_gpio *gpio, int pin) { + if (!gpio) { + return 1; + } + + return gpio->vtable->enable_input(gpio, (1 << pin)); +} + +/*! + * @brief Disable input on a pin + * @param gpio The handle for the GPIO interface + * @param pin The pin number indexed from 0 + * @return 0 if the input is successfully disabled + */ +__inline__ int metal_gpio_disable_input(struct metal_gpio *gpio, int pin) { + if (!gpio) { + return 1; + } + + return gpio->vtable->disable_input(gpio, (1 << pin)); +} + +/*! + * @brief Enable output on a pin + * @param gpio The handle for the GPIO interface + * @param pin The pin number indexed from 0 + * @return 0 if the output is successfully enabled + */ +__inline__ int metal_gpio_enable_output(struct metal_gpio *gpio, int pin) { + if (!gpio) { + return 1; + } + + return gpio->vtable->enable_output(gpio, (1 << pin)); +} + +/*! + * @brief Disable output on a pin + * @param gpio The handle for the GPIO interface + * @param pin The pin number indexed from 0 + * @return 0 if the output is successfully disabled + */ +__inline__ int metal_gpio_disable_output(struct metal_gpio *gpio, int pin) { + if (!gpio) { + return 1; + } + + return gpio->vtable->disable_output(gpio, (1 << pin)); +} + +/*! + * @brief Set the output value of a GPIO pin + * @param gpio The handle for the GPIO interface + * @param pin The pin number indexed from 0 + * @param value The value to set the pin to + * @return 0 if the output is successfully set + */ +__inline__ int metal_gpio_set_pin(struct metal_gpio *gpio, int pin, int value) { + if (!gpio) { + return 1; + } + + if (value == 0) { + return gpio->vtable->output_clear(gpio, (1 << pin)); + } else { + return gpio->vtable->output_set(gpio, (1 << pin)); + } +} + +/*! + * @brief Get the value of the GPIO pin + * @param gpio The handle for the GPIO interface + * @param pin The pin number indexed from 0 + * @return The value of the GPIO pin + */ +__inline__ int metal_gpio_get_input_pin(struct metal_gpio *gpio, int pin) { + if (!gpio) { + return 0; + } + + long value = gpio->vtable->input(gpio); + + if (value & (1 << pin)) { + return 1; + } else { + return 0; + } +} + +/*! + * @brief Get the value of the GPIO pin + * @param gpio The handle for the GPIO interface + * @param pin The pin number indexed from 0 + * @return The value of the GPIO pin + */ +__inline__ int metal_gpio_get_output_pin(struct metal_gpio *gpio, int pin) { + if (!gpio) { + return 0; + } + + long value = gpio->vtable->output(gpio); + + if (value & (1 << pin)) { + return 1; + } else { + return 0; + } +} + +/*! + * @brief Clears the value of the GPIO pin + * @param gpio The handle for the GPIO interface + * @param pin The pin number indexed from 0 + * @return 0 if the pin is successfully cleared + */ +__inline__ int metal_gpio_clear_pin(struct metal_gpio *gpio, int pin) { + if (!gpio) { + return 1; + } + + return gpio->vtable->output_clear(gpio, (1 << pin)); +} + +/*! + * @brief Toggles the value of the GPIO pin + * @param gpio The handle for the GPIO interface + * @param pin The pin number indexed from 0 + * @return 0 if the pin is successfully toggled + */ +__inline__ int metal_gpio_toggle_pin(struct metal_gpio *gpio, int pin) { + if (!gpio) { + return 1; + } + + return gpio->vtable->output_toggle(gpio, (1 << pin)); +} + +/*! + * @brief Enables and sets the pinmux for a GPIO pin + * @param gpio The handle for the GPIO interface + * @param pin The bitmask for the pin to enable pinmux on + * @param io_function The IO function to set + * @return 0 if the pinmux is successfully set + */ +__inline__ int metal_gpio_enable_pinmux(struct metal_gpio *gpio, int pin, + int io_function) { + if (!gpio) { + return 1; + } + + return gpio->vtable->enable_io(gpio, (1 << pin), (io_function << pin)); +} + +/*! + * @brief Disables the pinmux for a GPIO pin + * @param gpio The handle for the GPIO interface + * @param pin The bitmask for the pin to disable pinmux on + * @return 0 if the pinmux is successfully set + */ +__inline__ int metal_gpio_disable_pinmux(struct metal_gpio *gpio, int pin) { + if (!gpio) { + return 1; + } + + return gpio->vtable->disable_io(gpio, (1 << pin)); +} + +/*! + * @brief Config gpio interrupt type + * @param gpio The handle for the GPIO interface + * @param pin The bitmask for the pin to enable gpio interrupt + * @param intr_type The interrupt type + * @return 0 if the interrupt mode is setup properly + */ +__inline__ int metal_gpio_config_interrupt(struct metal_gpio *gpio, int pin, + int intr_type) { + if (!gpio) { + return 1; + } + + return gpio->vtable->config_int(gpio, (1 << pin), intr_type); +} + +/*! + * @brief Clear gpio interrupt status + * @param gpio The handle for the GPIO interface + * @param pin The bitmask for the pin to clear gpio interrupt + * @param intr_type The interrupt type to be clear + * @return 0 if the interrupt is cleared + */ +__inline__ int metal_gpio_clear_interrupt(struct metal_gpio *gpio, int pin, + int intr_type) { + if (!gpio) { + return 1; + } + + return gpio->vtable->clear_int(gpio, (1 << pin), intr_type); +} + +/*! + * @brief Get the interrupt controller for a gpio + * + * @param gpio The handle for the gpio + * @return A pointer to the interrupt controller responsible for handling + * gpio interrupts. + */ +__inline__ struct metal_interrupt * +metal_gpio_interrupt_controller(struct metal_gpio *gpio) { + return gpio->vtable->interrupt_controller(gpio); +} + +/*! + * @brief Get the interrupt id for a gpio + * + * @param gpio The handle for the gpio + * @param pin The bitmask for the pin to get gpio interrupt id + * @return The interrupt id corresponding to a gpio. + */ +__inline__ int metal_gpio_get_interrupt_id(struct metal_gpio *gpio, int pin) { + return gpio->vtable->get_interrupt_id(gpio, pin); +} + +#endif diff --git a/arch/riscv32/fe310/src/freedom-metal/metal/hpm.h b/arch/riscv32/fe310/src/freedom-metal/metal/hpm.h new file mode 100644 index 00000000..290f7ec3 --- /dev/null +++ b/arch/riscv32/fe310/src/freedom-metal/metal/hpm.h @@ -0,0 +1,146 @@ +/* Copyright 2020 SiFive, Inc */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#ifndef METAL__HPM_H +#define METAL__HPM_H + +#include + +/*! @brief Macros for valid Event IDs */ +#define METAL_HPM_EVENTID_8 (1UL << 8) +#define METAL_HPM_EVENTID_9 (1UL << 9) +#define METAL_HPM_EVENTID_10 (1UL << 10) +#define METAL_HPM_EVENTID_11 (1UL << 11) +#define METAL_HPM_EVENTID_12 (1UL << 12) +#define METAL_HPM_EVENTID_13 (1UL << 13) +#define METAL_HPM_EVENTID_14 (1UL << 14) +#define METAL_HPM_EVENTID_15 (1UL << 15) +#define METAL_HPM_EVENTID_16 (1UL << 16) +#define METAL_HPM_EVENTID_17 (1UL << 17) +#define METAL_HPM_EVENTID_18 (1UL << 18) +#define METAL_HPM_EVENTID_19 (1UL << 19) +#define METAL_HPM_EVENTID_20 (1UL << 20) +#define METAL_HPM_EVENTID_21 (1UL << 21) +#define METAL_HPM_EVENTID_22 (1UL << 22) +#define METAL_HPM_EVENTID_23 (1UL << 23) +#define METAL_HPM_EVENTID_24 (1UL << 24) +#define METAL_HPM_EVENTID_25 (1UL << 25) +#define METAL_HPM_EVENTID_26 (1UL << 26) +#define METAL_HPM_EVENTID_27 (1UL << 27) +#define METAL_HPM_EVENTID_28 (1UL << 28) +#define METAL_HPM_EVENTID_29 (1UL << 29) +#define METAL_HPM_EVENTID_30 (1UL << 30) +#define METAL_HPM_EVENTID_31 (1UL << 31) + +/*! @brief Macros for valid Event Class */ +#define METAL_HPM_EVENTCLASS_0 (0UL) +#define METAL_HPM_EVENTCLASS_1 (1UL) +#define METAL_HPM_EVENTCLASS_2 (2UL) +#define METAL_HPM_EVENTCLASS_3 (3UL) +#define METAL_HPM_EVENTCLASS_4 (4UL) +#define METAL_HPM_EVENTCLASS_5 (5UL) +#define METAL_HPM_EVENTCLASS_6 (6UL) +#define METAL_HPM_EVENTCLASS_7 (7UL) +#define METAL_HPM_EVENTCLASS_8 (8UL) + +/*! @brief Enums for available HPM counters */ +typedef enum { + METAL_HPM_CYCLE = 0, + METAL_HPM_TIME = 1, + METAL_HPM_INSTRET = 2, + METAL_HPM_COUNTER_3 = 3, + METAL_HPM_COUNTER_4 = 4, + METAL_HPM_COUNTER_5 = 5, + METAL_HPM_COUNTER_6 = 6, + METAL_HPM_COUNTER_7 = 7, + METAL_HPM_COUNTER_8 = 8, + METAL_HPM_COUNTER_9 = 9, + METAL_HPM_COUNTER_10 = 10, + METAL_HPM_COUNTER_11 = 11, + METAL_HPM_COUNTER_12 = 12, + METAL_HPM_COUNTER_13 = 13, + METAL_HPM_COUNTER_14 = 14, + METAL_HPM_COUNTER_15 = 15, + METAL_HPM_COUNTER_16 = 16, + METAL_HPM_COUNTER_17 = 17, + METAL_HPM_COUNTER_18 = 18, + METAL_HPM_COUNTER_19 = 19, + METAL_HPM_COUNTER_20 = 20, + METAL_HPM_COUNTER_21 = 21, + METAL_HPM_COUNTER_22 = 22, + METAL_HPM_COUNTER_23 = 23, + METAL_HPM_COUNTER_24 = 24, + METAL_HPM_COUNTER_25 = 25, + METAL_HPM_COUNTER_26 = 26, + METAL_HPM_COUNTER_27 = 27, + METAL_HPM_COUNTER_28 = 28, + METAL_HPM_COUNTER_29 = 29, + METAL_HPM_COUNTER_30 = 30, + METAL_HPM_COUNTER_31 = 31 +} metal_hpm_counter; + +/*! @brief Initialize hardware performance monitor counters. + * @param cpu The CPU device handle. + * @return 0 If no error.*/ +int metal_hpm_init(struct metal_cpu *cpu); + +/*! @brief Disables hardware performance monitor counters. + * Note - Disabled HPM counters may reduce power consumption. + * @param cpu The CPU device handle. + * @return 0 If no error.*/ +int metal_hpm_disable(struct metal_cpu *cpu); + +/*! @brief Set events which will cause the specified counter to increment. + * Counter will start incrementing from the moment events are set. + * @param cpu The CPU device handle. + * @param counter Hardware counter to be incremented by selected events. + * @param bitmask Bit-mask to select events for a particular counter, + * refer core reference manual for selection of events. + * Event bit mask is partitioned as follows: + * [XLEN-1:8] - Event selection mask [7:0] - Event class + * @return 0 If no error.*/ +int metal_hpm_set_event(struct metal_cpu *cpu, metal_hpm_counter counter, + unsigned int bitmask); + +/*! @brief Get events selection mask set for specified counter. + * @param cpu The CPU device handle. + * @param counter Hardware counter. + * @return Event selection bit mask. refer core reference manual for details.*/ +unsigned int metal_hpm_get_event(struct metal_cpu *cpu, + metal_hpm_counter counter); + +/*! @brief Clear event selector bits as per specified bit-mask. + * @param cpu The CPU device handle. + * @param counter Hardware counter. + * @return 0 If no error.*/ +int metal_hpm_clr_event(struct metal_cpu *cpu, metal_hpm_counter counter, + unsigned int bitmask); + +/*! @brief Enable counter access to next lower privilege mode. + * @param cpu The CPU device handle. + * @param counter Hardware counter. + * @return 0 If no error.*/ +int metal_hpm_enable_access(struct metal_cpu *cpu, metal_hpm_counter counter); + +/*! @brief Disable counter access to next lower privilege mode. + * @param cpu The CPU device handle. + * @param counter Hardware counter. + * @return 0 If no error.*/ +int metal_hpm_disable_access(struct metal_cpu *cpu, metal_hpm_counter counter); + +/*! @brief Reads current value of specified hardware counter. + * Note: 'mtime' register is memory mapped into CLINT block. + * Use CLINT APIs to access this register. + * @param cpu The CPU device handle. + * @param counter Hardware counter. + * @return Current value of hardware counter on success, 0 on failure.*/ +unsigned long long metal_hpm_read_counter(struct metal_cpu *cpu, + metal_hpm_counter counter); + +/*! @brief Clears off specified counter. + * @param cpu The CPU device handle. + * @param counter Hardware counter. + * @return 0 If no error.*/ +int metal_hpm_clear_counter(struct metal_cpu *cpu, metal_hpm_counter counter); + +#endif diff --git a/arch/riscv32/fe310/src/freedom-metal/metal/i2c.h b/arch/riscv32/fe310/src/freedom-metal/metal/i2c.h new file mode 100644 index 00000000..baf62e5d --- /dev/null +++ b/arch/riscv32/fe310/src/freedom-metal/metal/i2c.h @@ -0,0 +1,112 @@ +/* Copyright 2019 SiFive, Inc */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#ifndef METAL__I2C_H +#define METAL__I2C_H + +/*! @brief Enums to enable/disable stop condition. */ +typedef enum { + METAL_I2C_STOP_DISABLE = 0, + METAL_I2C_STOP_ENABLE = 1 +} metal_i2c_stop_bit_t; + +/*! @brief Enums to set up I2C device modes. */ +typedef enum { METAL_I2C_SLAVE = 0, METAL_I2C_MASTER = 1 } metal_i2c_mode_t; + +struct metal_i2c; + +struct metal_i2c_vtable { + void (*init)(struct metal_i2c *i2c, unsigned int baud_rate, + metal_i2c_mode_t mode); + int (*write)(struct metal_i2c *i2c, unsigned int addr, unsigned int len, + unsigned char buf[], metal_i2c_stop_bit_t stop_bit); + int (*read)(struct metal_i2c *i2c, unsigned int addr, unsigned int len, + unsigned char buf[], metal_i2c_stop_bit_t stop_bit); + int (*transfer)(struct metal_i2c *i2c, unsigned int addr, + unsigned char txbuf[], unsigned int txlen, + unsigned char rxbuf[], unsigned int rxlen); + int (*get_baud_rate)(struct metal_i2c *i2c); + int (*set_baud_rate)(struct metal_i2c *i2c, unsigned int baud_rate); +}; + +/*! @brief A handle for a I2C device. */ +struct metal_i2c { + const struct metal_i2c_vtable *vtable; +}; + +/*! @brief Get a handle for a I2C device. + * @param device_num The index of the desired I2C device. + * @return A handle to the I2C device, or NULL if the device does not exist.*/ +struct metal_i2c *metal_i2c_get_device(unsigned int device_num); + +/*! @brief Initialize a I2C device with a certain baud rate. + * @param i2c The handle for the I2C device to initialize. + * @param baud_rate The baud rate for the I2C device to operate at. + * @param mode I2C operation mode. + */ +inline void metal_i2c_init(struct metal_i2c *i2c, unsigned int baud_rate, + metal_i2c_mode_t mode) { + i2c->vtable->init(i2c, baud_rate, mode); +} + +/*! @brief Perform a I2C write. + * @param i2c The handle for the I2C device to perform the write operation. + * @param addr The I2C slave address for the write operation. + * @param len The number of bytes to transfer. + * @param buf The buffer to send over the I2C bus. Must be len bytes long. + * @param stop_bit Enable / Disable STOP condition. + * @return 0 if the write succeeds. + */ +inline int metal_i2c_write(struct metal_i2c *i2c, unsigned int addr, + unsigned int len, unsigned char buf[], + metal_i2c_stop_bit_t stop_bit) { + return i2c->vtable->write(i2c, addr, len, buf, stop_bit); +} + +/*! @brief Perform a I2C read. + * @param i2c The handle for the I2C device to perform the read operation. + * @param addr The I2C slave address for the read operation. + * @param len The number of bytes to transfer. + * @param buf The buffer to store data from I2C bus. Must be len bytes long. + * @param stop_bit Enable / Disable STOP condition. + * @return 0 if the read succeeds. + */ +inline int metal_i2c_read(struct metal_i2c *i2c, unsigned int addr, + unsigned int len, unsigned char buf[], + metal_i2c_stop_bit_t stop_bit) { + return i2c->vtable->read(i2c, addr, len, buf, stop_bit); +} + +/*! @brief Performs back to back I2C write and read operations. + * @param i2c The handle for the I2C device to perform the transfer operation. + * @param addr The I2C slave address for the transfer operation. + * @param txbuf The data buffer to be transmitted over I2C bus. + * @param txlen The number of bytes to write over I2C. + * @param rxbuf The buffer to store data received over I2C bus. + * @param rxlen The number of bytes to read over I2C. + * @return 0 if the transfer succeeds. + */ +inline int metal_i2c_transfer(struct metal_i2c *i2c, unsigned int addr, + unsigned char txbuf[], unsigned int txlen, + unsigned char rxbuf[], unsigned int rxlen) { + return i2c->vtable->transfer(i2c, addr, txbuf, txlen, rxbuf, rxlen); +} + +/*! @brief Get the current baud rate of the I2C device. + * @param i2c The handle for the I2C device. + * @return The baud rate in Hz. + */ +inline int metal_i2c_get_baud_rate(struct metal_i2c *i2c) { + return i2c->vtable->get_baud_rate(i2c); +} + +/*! @brief Set the current baud rate of the I2C device. + * @param i2c The handle for the I2C device. + * @param baud_rate The desired baud rate of the I2C device. + * @return 0 If the baud rate is successfully changed. + */ +inline int metal_i2c_set_baud_rate(struct metal_i2c *i2c, int baud_rate) { + return i2c->vtable->set_baud_rate(i2c, baud_rate); +} + +#endif diff --git a/arch/riscv32/fe310/src/freedom-metal/metal/init.h b/arch/riscv32/fe310/src/freedom-metal/metal/init.h new file mode 100644 index 00000000..0214d0ad --- /dev/null +++ b/arch/riscv32/fe310/src/freedom-metal/metal/init.h @@ -0,0 +1,130 @@ +/* Copyright 2019 SiFive Inc. */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#ifndef METAL_INIT +#define METAL_INIT + +/*! + * @file init.h + * API for Metal constructors and destructors + */ + +typedef void (*metal_constructor_t)(void); +typedef void (*metal_destructor_t)(void); + +#define METAL_INIT_HIGHEST_PRIORITY 0 +#define METAL_INIT_DEFAULT_PRIORITY 5000 +#define METAL_INIT_LOWEST_PRIORITY 9999 + +/*! @def METAL_CONSTRUCTOR + * @brief Define a Metal constructor + * + * Functions defined with METAL_CONSTRUCTOR will be added to the list of + * Metal constructors. By default, these functions are called before main by + * the metal_init() function. + */ +#define METAL_CONSTRUCTOR(function_name) \ + METAL_CONSTRUCTOR_PRIO(function_name, METAL_INIT_DEFAULT_PRIORITY) + +/*! @def METAL_CONSTRUCTOR_PRIO + * @brief Define a Metal constructor with a given priority + * + * The priority argument should be an integer between 0 and 9999, where 0 + * is the highest priority (runs first) and 9999 is the lowest priority + * (runs last). + * + * Functions defined with METAL_CONSTRUCTOR_PRIO will be added to the list of + * Metal constructors. By default, these functions are called before main by + * the metal_init() function. + */ +#define METAL_CONSTRUCTOR_PRIO(function_name, priority) \ + __METAL_CONSTRUCTOR_PRIO(function_name, priority) + +/* We use this wrapper for METAL_CONSTRUCTOR_PRIORITY so that macros passed + * as 'priority' are expanded before being stringified by the # operator. + * If we don't do this, then + * METAL_CONSTRUCTOR(my_fn_name, METAL_INIT_DEFAULT_PRIORITY) + * results in .metal.init_array.METAL_INIT_DEFAULT_PRIORITY instead of + * .metal.init_array.5000 */ +#define __METAL_CONSTRUCTOR_PRIO(function_name, priority) \ + __attribute__((section(".metal.ctors"))) void function_name(void); \ + __attribute__((section(".metal.init_array." #priority))) \ + metal_constructor_t _##function_name##_ptr = &function_name; \ + void function_name(void) + +/*! @def METAL_DESTRUCTOR + * @brief Define a Metal destructor + * + * Functions defined with METAL_DESTRUCTOR will be added to the list of + * Metal destructors. By default, these functions are called on exit by + * the metal_fini() function. + */ +#define METAL_DESTRUCTOR(function_name) \ + METAL_DESTRUCTOR_PRIO(function_name, METAL_INIT_DEFAULT_PRIORITY) + +/*! @def METAL_DESTRUCTOR_PRIO + * @brief Define a Metal destructor with a given priority + * + * The priority argument should be an integer between 0 and 9999, where 0 + * is the highest priority (runs first) and 9999 is the lowest priority + * (runs last). + * + * Functions defined with METAL_DESTRUCTOR_PRIO will be added to the list of + * Metal destructors. By default, these functions are called on exit by + * the metal_fini() function. + */ +#define METAL_DESTRUCTOR_PRIO(function_name, priority) \ + __METAL_DESTRUCTOR_PRIO(function_name, priority) +#define __METAL_DESTRUCTOR_PRIO(function_name, priority) \ + __attribute__((section(".metal.dtors"))) void function_name(void); \ + __attribute__((section(".metal.fini_array." #priority))) \ + metal_destructor_t _##function_name##_ptr = &function_name; \ + void function_name(void) + +/*! + * @brief Call all Metal constructors + * + * Devices supported by Metal may define Metal constructors to perform + * initialization before main. This function iterates over the constructors + * and calls them in turn. + * + * You can add your own constructors to the functions called by metal_init() + * by defining functions with the METAL_CONSTRUCTOR() macro. + * + * This function is called before main by default by metal_init_run(). + */ +void metal_init(void); + +/*! + * @brief Call all Metal destructors + * + * Devices supported by Metal may define Metal destructors to perform + * initialization on exit. This function iterates over the destructors + * and calls them in turn. + * + * You can add your own destructors to the functions called by metal_fini() + * by defining functions with the METAL_DESTRUCTOR() macro. + * + * This function is called on exit by default by metal_fini_run(). + */ +void metal_fini(void); + +/*! + * @brief Weak function to call metal_init() before main + * + * This function calls metal_init() before main by default. If you wish to + * replace or augment this call to the Metal constructors, you can redefine + * metal_init_run() + */ +void metal_init_run(void); + +/*! + * @brief Weak function to call metal_fini() before main + * + * This function calls metal_fini() at exit by default. If you wish to + * replace or augment this call to the Metal destructors, you can redefine + * metal_fini_run() + */ +void metal_fini_run(void); + +#endif /* METAL_INIT */ diff --git a/arch/riscv32/fe310/src/freedom-metal/metal/interrupt.h b/arch/riscv32/fe310/src/freedom-metal/metal/interrupt.h new file mode 100644 index 00000000..11df019d --- /dev/null +++ b/arch/riscv32/fe310/src/freedom-metal/metal/interrupt.h @@ -0,0 +1,590 @@ +/* Copyright 2018 SiFive, Inc */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#ifndef METAL__INTERRUPT_H +#define METAL__INTERRUPT_H + +/*! @file interrupt.h + * @brief API for registering and manipulating interrupts + */ + +#include + +/*! + * @brief Possible interrupt controllers + */ +typedef enum metal_interrupt_controller_ { + METAL_CPU_CONTROLLER = 0, + METAL_CLINT_CONTROLLER = 1, + METAL_CLIC_CONTROLLER = 2, + METAL_PLIC_CONTROLLER = 3 +} metal_intr_cntrl_type; + +/*! + * @brief Possible mode of interrupts to operate + */ +typedef enum metal_vector_mode_ { + METAL_DIRECT_MODE = 0, + METAL_VECTOR_MODE = 1, + METAL_SELECTIVE_NONVECTOR_MODE = 2, + METAL_SELECTIVE_VECTOR_MODE = 3, + METAL_HARDWARE_VECTOR_MODE = 4 +} metal_vector_mode; + +/*! + * @brief Possible mode of privilege interrupts to operate + */ +typedef enum metal_intr_priv_mode_ { + METAL_INTR_PRIV_M_MODE = 0, + METAL_INTR_PRIV_MU_MODE = 1, + METAL_INTR_PRIV_MSU_MODE = 2 +} metal_intr_priv_mode; + +/*! + * @brief The bitmask of hart context + */ +typedef struct metal_affinity_ { + unsigned long bitmask; +} metal_affinity; + +#define for_each_metal_affinity(bit, metal_affinity) \ + for (bit = 0; metal_affinity.bitmask; bit++, metal_affinity.bitmask >>= 1) + +#define metal_affinity_set_val(metal_affinity, val) \ + metal_affinity.bitmask = val; + +#define metal_affinity_set_bit(metal_affinity, bit, val) \ + metal_affinity.bitmask |= ((val & 0x1) << bit); + +/*! + * @brief Function signature for interrupt callback handlers + */ +typedef void (*metal_interrupt_handler_t)(int, void *); +typedef void (*metal_interrupt_vector_handler_t)(void); + +struct metal_interrupt; + +struct metal_interrupt_vtable { + void (*interrupt_init)(struct metal_interrupt *controller); + int (*interrupt_set_vector_mode)(struct metal_interrupt *controller, + metal_vector_mode mode); + metal_vector_mode (*interrupt_get_vector_mode)( + struct metal_interrupt *controller); + int (*interrupt_set_privilege)(struct metal_interrupt *controller, + metal_intr_priv_mode priv); + metal_intr_priv_mode (*interrupt_get_privilege)( + struct metal_interrupt *controller); + int (*interrupt_clear)(struct metal_interrupt *controller, int id); + int (*interrupt_set)(struct metal_interrupt *controller, int id); + int (*interrupt_register)(struct metal_interrupt *controller, int id, + metal_interrupt_handler_t isr, void *priv_data); + int (*interrupt_vector_register)(struct metal_interrupt *controller, int id, + metal_interrupt_vector_handler_t isr, + void *priv_data); + int (*interrupt_enable)(struct metal_interrupt *controller, int id); + int (*interrupt_disable)(struct metal_interrupt *controller, int id); + int (*interrupt_vector_enable)(struct metal_interrupt *controller, int id); + int (*interrupt_vector_disable)(struct metal_interrupt *controller, int id); + unsigned int (*interrupt_get_threshold)(struct metal_interrupt *controller); + int (*interrupt_set_threshold)(struct metal_interrupt *controller, + unsigned int threshold); + unsigned int (*interrupt_get_priority)(struct metal_interrupt *controller, + int id); + int (*interrupt_set_priority)(struct metal_interrupt *controller, int id, + unsigned int priority); + unsigned int (*interrupt_get_preemptive_level)( + struct metal_interrupt *controller, int id); + int (*interrupt_set_preemptive_level)(struct metal_interrupt *controller, + int id, unsigned int level); + int (*command_request)(struct metal_interrupt *controller, int cmd, + void *data); + int (*mtimecmp_set)(struct metal_interrupt *controller, int hartid, + unsigned long long time); + metal_affinity (*interrupt_affinity_enable)( + struct metal_interrupt *controller, metal_affinity bitmask, int id); + metal_affinity (*interrupt_affinity_disable)( + struct metal_interrupt *controller, metal_affinity bitmask, int id); + metal_affinity (*interrupt_affinity_set_threshold)( + struct metal_interrupt *controller, metal_affinity bitmask, + unsigned int threshold); + unsigned int (*interrupt_affinity_get_threshold)( + struct metal_interrupt *controller, int context_id); +}; + +/*! + * @brief A handle for an interrupt + */ +struct metal_interrupt { + const struct metal_interrupt_vtable *vtable; +}; + +/*! + * @brief Initialize a given interrupt controller + * + * Initialize a given interrupt controller. This function must be called + * before any interrupts are registered or enabled with the handler. It + * is invalid to initialize an interrupt controller more than once. + * + * @param controller The handle for the interrupt controller + */ +__inline__ void metal_interrupt_init(struct metal_interrupt *controller) { + controller->vtable->interrupt_init(controller); +} + +/*! + * @brief Get the handle for an given interrupt controller type + * @param cntrl The type ofinterrupt controller + * @param id The instance of the interrupt controller + * @return A handle to the interrupt controller (CLINT, CLIC, PLIC), or + * NULL if none is found for the requested label + */ +struct metal_interrupt * +metal_interrupt_get_controller(metal_intr_cntrl_type cntrl, int id); + +/*! + * @brief Configure vector mode for an interrupt controller + * + * Configure vector mode for an interrupt controller. + * This function must be called after initialization and before + * configuring individual interrupts, registering ISR. + * + * @param controller The handle for the interrupt controller + * @param mode The vector mode of the interrupt controller. + * @return 0 upon success + */ +__inline__ int +metal_interrupt_set_vector_mode(struct metal_interrupt *controller, + metal_vector_mode mode) { + return controller->vtable->interrupt_set_vector_mode(controller, mode); +} + +/*! + * @brief Get vector mode of a given an interrupt controller + * + * Configure vector mode for an interrupt controller. + * This function must be called after initialization and before + * configuring individual interrupts, registering ISR. + * + * @param controller The handle for the interrupt controller + * @param mode The vector mode of the interrupt controller. + * @return The interrupt vector mode + */ +__inline__ metal_vector_mode +metal_interrupt_get_vector_mode(struct metal_interrupt *controller) { + return controller->vtable->interrupt_get_vector_mode(controller); +} + +/*! + * @brief Configure privilege mode a of given interrupt controller + * + * Configure privilege mode for a given interrupt controller. + * This function must be called after initialization and before + * configuring individual interrupts, registering ISR. + * + * @param controller The handle for the interrupt controller + * @param privilege The privilege mode of the interrupt controller. + * @return 0 upon success + */ +__inline__ int metal_interrupt_set_privilege(struct metal_interrupt *controller, + metal_intr_priv_mode privilege) { + return controller->vtable->interrupt_set_privilege(controller, privilege); +} + +/*! + * @brief Get privilege mode a of given interrupt controller + * + * Get privilege mode for a given interrupt controller. + * This function must be called after initialization and before + * configuring individual interrupts, registering ISR. + * + * @param controller The handle for the interrupt controller + * @return The interrupt privilege mode + */ +__inline__ metal_intr_priv_mode +metal_interrupt_get_privilege(struct metal_interrupt *controller) { + return controller->vtable->interrupt_get_privilege(controller); +} + +/*! + * @brief clear an interrupt + * @param controller The handle for the interrupt controller + * @param id The interrupt ID to trigger + * @return 0 upon success + */ +__inline__ int metal_interrupt_clear(struct metal_interrupt *controller, + int id) { + return controller->vtable->interrupt_clear(controller, id); +} + +/*! + * @brief Set an interrupt + * @param controller The handle for the interrupt controller + * @param id The interrupt ID to trigger + * @return 0 upon success + */ +__inline__ int metal_interrupt_set(struct metal_interrupt *controller, int id) { + return controller->vtable->interrupt_set(controller, id); +} + +/*! + * @brief Register an interrupt handler + * @param controller The handle for the interrupt controller + * @param id The interrupt ID to register + * @param handler The interrupt handler callback + * @param priv_data Private data for the interrupt handler + * @return 0 upon success + */ +__inline__ int +metal_interrupt_register_handler(struct metal_interrupt *controller, int id, + metal_interrupt_handler_t handler, + void *priv_data) { + return controller->vtable->interrupt_register(controller, id, handler, + priv_data); +} + +/*! + * @brief Register an interrupt vector handler + * @param controller The handle for the interrupt controller + * @param id The interrupt ID to register + * @param handler The interrupt vector handler callback + * @param priv_data Private data for the interrupt handler + * @return 0 upon success + */ +__inline__ int metal_interrupt_register_vector_handler( + struct metal_interrupt *controller, int id, + metal_interrupt_vector_handler_t handler, void *priv_data) { + return controller->vtable->interrupt_vector_register(controller, id, + handler, priv_data); +} + +/*! + * @brief Enable an interrupt + * @param controller The handle for the interrupt controller + * @param id The interrupt ID to enable + * @return 0 upon success + */ +__inline__ int metal_interrupt_enable(struct metal_interrupt *controller, + int id) { + return controller->vtable->interrupt_enable(controller, id); +} + +/*! + * @brief Disable an interrupt + * @param controller The handle for the interrupt controller + * @param id The interrupt ID to disable + * @return 0 upon success + */ +__inline__ int metal_interrupt_disable(struct metal_interrupt *controller, + int id) { + return controller->vtable->interrupt_disable(controller, id); +} + +/*! + * @brief Set interrupt threshold level + * @param controller The handle for the interrupt controller + * @param threshold The interrupt threshold level + * @return 0 upon success + */ +__inline__ int metal_interrupt_set_threshold(struct metal_interrupt *controller, + unsigned int level) { + return controller->vtable->interrupt_set_threshold(controller, level); +} + +/*! + * @brief Get an interrupt threshold level + * @param controller The handle for the interrupt controller + * @return The interrupt threshold level + */ +__inline__ unsigned int +metal_interrupt_get_threshold(struct metal_interrupt *controller) { + return controller->vtable->interrupt_get_threshold(controller); +} + +/*! + * @brief Set an interrupt priority level + * @param controller The handle for the interrupt controller + * @param id The interrupt ID to enable + * @param priority The interrupt priority level + * @return 0 upon success + */ +__inline__ int metal_interrupt_set_priority(struct metal_interrupt *controller, + int id, unsigned int priority) { + return controller->vtable->interrupt_set_priority(controller, id, priority); +} + +/*! + * @brief Get an interrupt priority level + * @param controller The handle for the interrupt controller + * @param id The interrupt ID to enable + * @return The interrupt priority level + */ +__inline__ unsigned int +metal_interrupt_get_priority(struct metal_interrupt *controller, int id) { + return controller->vtable->interrupt_get_priority(controller, id); +} + +/*! + * @brief Set preemptive level and priority for a given interrupt ID + * + * Set the preemptive level and priority for a given interrupt ID. + * + * @param controller The handle for the interrupt controller + * @param id The interrupt ID to enable + * @param level The interrupt level and priority are encoded together + * @return 0 upon success + */ +__inline__ int +metal_interrupt_set_preemptive_level(struct metal_interrupt *controller, int id, + unsigned int level) { + if (controller->vtable->interrupt_set_preemptive_level) + return controller->vtable->interrupt_set_preemptive_level(controller, + id, level); + else + return 0; +} + +/*! + * @brief Get an interrupt preemptive level + * @param controller The handle for the interrupt controller + * @param id The interrupt ID to enable + * @return The interrupt level + */ +__inline__ unsigned int +metal_interrupt_get_preemptive_level(struct metal_interrupt *controller, + int id) { + if (controller->vtable->interrupt_get_preemptive_level) + return controller->vtable->interrupt_get_preemptive_level(controller, + id); + else + return 0; +} + +/*! + * @brief Enable an interrupt vector + * @param controller The handle for the interrupt controller + * @param id The interrupt ID to enable + * @return 0 upon success + */ +__inline__ int metal_interrupt_vector_enable(struct metal_interrupt *controller, + int id) { + return controller->vtable->interrupt_vector_enable(controller, id); +} + +/*! + * @brief Disable an interrupt vector + * @param controller The handle for the interrupt controller + * @param id The interrupt ID to disable + * @return 0 upon success + */ +__inline__ int +metal_interrupt_vector_disable(struct metal_interrupt *controller, int id) { + return controller->vtable->interrupt_vector_disable(controller, id); +} + +/*! + * @brief Default interrupt vector handler, that can be overriden by user + * @param None + * @return None + */ +void __attribute__((weak, interrupt)) metal_interrupt_vector_handler(void); + +/*! + * @brief Metal Software interrupt vector handler, that can be overriden by user + * @param None + * @return None + */ +void __attribute__((weak, interrupt)) +metal_software_interrupt_vector_handler(void); + +/*! + * @brief Metal Timer interrupt vector handler, that can be overriden by user + * @param None + * @return None + */ +void __attribute__((weak, interrupt)) +metal_timer_interrupt_vector_handler(void); + +/*! + * @brief Metal External interrupt vector handler, that can be overriden by user + * @param None + * @return None + */ +void __attribute__((weak, interrupt)) +metal_external_interrupt_vector_handler(void); + +/*! + * @brief Metal Local 0 interrupt vector handler, that can be overriden by user + * @param None + * @return None + */ +void __attribute__((weak, interrupt)) metal_lc0_interrupt_vector_handler(void); + +/*! + * @brief Metal Local 1 interrupt vector handler, that can be overriden by user + * @param None + * @return None + */ +void __attribute__((weak, interrupt)) metal_lc1_interrupt_vector_handler(void); + +/*! + * @brief Metal Local 2 interrupt vector handler, that can be overriden by user + * @param None + * @return None + */ +void __attribute__((weak, interrupt)) metal_lc2_interrupt_vector_handler(void); + +/*! + * @brief Metal Local 3 interrupt vector handler, that can be overriden by user + * @param None + * @return None + */ +void __attribute__((weak, interrupt)) metal_lc3_interrupt_vector_handler(void); + +/*! + * @brief Metal Local 4 interrupt vector handler, that can be overriden by user + * @param None + * @return None + */ +void __attribute__((weak, interrupt)) metal_lc4_interrupt_vector_handler(void); + +/*! + * @brief Metal Local 5 interrupt vector handler, that can be overriden by user + * @param None + * @return None + */ +void __attribute__((weak, interrupt)) metal_lc5_interrupt_vector_handler(void); + +/*! + * @brief Metal Local 6 interrupt vector handler, that can be overriden by user + * @param None + * @return None + */ +void __attribute__((weak, interrupt)) metal_lc6_interrupt_vector_handler(void); + +/*! + * @brief Metal Local 7 interrupt vector handler, that can be overriden by user + * @param None + * @return None + */ +void __attribute__((weak, interrupt)) metal_lc7_interrupt_vector_handler(void); + +/*! + * @brief Metal Local 8 interrupt vector handler, that can be overriden by user + * @param None + * @return None + */ +void __attribute__((weak, interrupt)) metal_lc8_interrupt_vector_handler(void); + +/*! + * @brief Metal Local 9 interrupt vector handler, that can be overriden by user + * @param None + * @return None + */ +void __attribute__((weak, interrupt)) metal_lc9_interrupt_vector_handler(void); + +/*! + * @brief Metal Local 10 interrupt vector handler, that can be overriden by user + * @param None + * @return None + */ +void __attribute__((weak, interrupt)) metal_lc10_interrupt_vector_handler(void); + +/*! + * @brief Metal Local 11 interrupt vector handler, that can be overriden by user + * @param None + * @return None + */ +void __attribute__((weak, interrupt)) metal_lc11_interrupt_vector_handler(void); + +/*! + * @brief Metal Local 12 interrupt vector handler, that can be overriden by user + * @param None + * @return None + */ +void __attribute__((weak, interrupt)) metal_lc12_interrupt_vector_handler(void); + +/*! + * @brief Metal Local 13 interrupt vector handler, that can be overriden by user + * @param None + * @return None + */ +void __attribute__((weak, interrupt)) metal_lc13_interrupt_vector_handler(void); + +/*! + * @brief Metal Local 14 interrupt vector handler, that can be overriden by user + * @param None + * @return None + */ +void __attribute__((weak, interrupt)) metal_lc14_interrupt_vector_handler(void); + +/*! + * @brief Metal Local 15 interrupt vector handler, that can be overriden by user + * @param None + * @return None + */ +void __attribute__((weak, interrupt)) metal_lc15_interrupt_vector_handler(void); + +/* Utilities function to controll, manages devices via a given interrupt + * controller */ +__inline__ int +_metal_interrupt_command_request(struct metal_interrupt *controller, int cmd, + void *data) { + return controller->vtable->command_request(controller, cmd, data); +} + +/*! + * @brief Enable an interrupt for the hart contexts + * @param controller The handle for the interrupt controller + * @param bitmask The bit mask of hart contexts to enable + * @param id The interrupt ID to enable + * @return The result of each hart context. 0 upon success at relevant bit. + */ +__inline__ metal_affinity +metal_interrupt_affinity_enable(struct metal_interrupt *controller, + metal_affinity bitmask, int id) { + return controller->vtable->interrupt_affinity_enable(controller, bitmask, + id); +} + +/*! + * @brief Disable an interrupt for the hart contexts + * @param controller The handle for the interrupt controller + * @param bitmask The bit mask of hart contexts to disable + * @param id The interrupt ID to disable + * @return The result of each hart context. 0 upon success at relevant bit. + */ +__inline__ metal_affinity +metal_interrupt_affinity_disable(struct metal_interrupt *controller, + metal_affinity bitmask, int id) { + return controller->vtable->interrupt_affinity_disable(controller, bitmask, + id); +} + +/*! + * @brief Set interrupt threshold level for the hart contexts + * @param controller The handle for the interrupt controller + * @param bitmask The bit mask of hart contexts to set threshold + * @param threshold The interrupt threshold level + * @return The result of each hart context. 0 upon success at relevant bit. + */ +__inline__ metal_affinity +metal_interrupt_affinity_set_threshold(struct metal_interrupt *controller, + metal_affinity bitmask, + unsigned int level) { + return controller->vtable->interrupt_affinity_set_threshold(controller, + bitmask, level); +} + +/*! + * @brief Get an interrupt threshold level from the hart context + * @param controller The handle for the interrupt controller + * @param context_id The hart context ID to get threshold + * @return The interrupt threshold level + */ +__inline__ unsigned int +metal_interrupt_affinity_get_threshold(struct metal_interrupt *controller, + int context_id) { + return controller->vtable->interrupt_affinity_get_threshold(controller, + context_id); +} +#endif diff --git a/arch/riscv32/fe310/src/freedom-metal/metal/io.h b/arch/riscv32/fe310/src/freedom-metal/metal/io.h new file mode 100644 index 00000000..f1df8551 --- /dev/null +++ b/arch/riscv32/fe310/src/freedom-metal/metal/io.h @@ -0,0 +1,23 @@ +/* Copyright 2018 SiFive, Inc */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#ifndef METAL__IO_H +#define METAL__IO_H + +/* This macro enforces that the compiler will not elide the given access. */ +#define __METAL_ACCESS_ONCE(x) (*(__typeof__(*x) volatile *)(x)) + +/* Allows users to specify arbitrary fences. */ +#define __METAL_IO_FENCE(pred, succ) \ + __asm__ volatile("fence " #pred "," #succ ::: "memory"); + +/* Types that explicitly describe an address as being used for memory-mapped + * IO. These should only be accessed via __METAL_ACCESS_ONCE. */ +typedef unsigned char __metal_io_u8; +typedef unsigned short __metal_io_u16; +typedef unsigned int __metal_io_u32; +#if __riscv_xlen >= 64 +typedef unsigned long __metal_io_u64; +#endif + +#endif diff --git a/arch/riscv32/fe310/src/freedom-metal/metal/itim.h b/arch/riscv32/fe310/src/freedom-metal/metal/itim.h new file mode 100644 index 00000000..3decefff --- /dev/null +++ b/arch/riscv32/fe310/src/freedom-metal/metal/itim.h @@ -0,0 +1,20 @@ +/* Copyright 2018 SiFive, Inc */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#ifndef METAL__ITIM_H +#define METAL__ITIM_H + +/*! @file itim.h + * + * API for manipulating ITIM allocation + */ + +/*! @def METAL_PLACE_IN_ITIM + * @brief Link a function into the ITIM + * + * Link a function into the ITIM (Instruction Tightly Integrated + * Memory) if the ITIM is present on the target device. + */ +#define METAL_PLACE_IN_ITIM __attribute__((section(".itim"))) + +#endif diff --git a/arch/riscv32/fe310/src/freedom-metal/metal/led.h b/arch/riscv32/fe310/src/freedom-metal/metal/led.h new file mode 100644 index 00000000..da2555fb --- /dev/null +++ b/arch/riscv32/fe310/src/freedom-metal/metal/led.h @@ -0,0 +1,77 @@ +/* Copyright 2018 SiFive, Inc */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#ifndef METAL__LED_H +#define METAL__LED_H + +/*! + * @file led.h + * @brief API for manipulating LEDs + */ + +struct metal_led; + +struct metal_led_vtable { + int (*led_exist)(struct metal_led *led, char *label); + void (*led_enable)(struct metal_led *led); + void (*led_on)(struct metal_led *led); + void (*led_off)(struct metal_led *led); + void (*led_toggle)(struct metal_led *led); +}; + +/*! + * @brief A handle for an LED + */ +struct metal_led { + const struct metal_led_vtable *vtable; +}; + +/*! + * @brief Get a handle for an LED + * @param label The DeviceTree label for the desired LED + * @return A handle to the LED, or NULL if none is found for the requested label + */ +struct metal_led *metal_led_get(char *label); + +/*! + * @brief Get a handle for a channel of an RGB LED + * @param label The DeviceTree label for the desired LED + * @param color The color for the LED in the DeviceTree + * @return A handle to the LED, or NULL if none is found for the requested label + * and color + */ +struct metal_led *metal_led_get_rgb(char *label, char *color); + +/*! + * @brief Enable an LED + * @param led The handle for the LED + */ +__inline__ void metal_led_enable(struct metal_led *led) { + led->vtable->led_enable(led); +} + +/*! + * @brief Turn an LED on + * @param led The handle for the LED + */ +__inline__ void metal_led_on(struct metal_led *led) { + led->vtable->led_on(led); +} + +/*! + * @brief Turn an LED off + * @param led The handle for the LED + */ +__inline__ void metal_led_off(struct metal_led *led) { + led->vtable->led_off(led); +} + +/*! + * @brief Toggle the on/off state of an LED + * @param led The handle for the LED + */ +__inline__ void metal_led_toggle(struct metal_led *led) { + led->vtable->led_toggle(led); +} + +#endif diff --git a/arch/riscv32/fe310/src/freedom-metal/metal/lim.h b/arch/riscv32/fe310/src/freedom-metal/metal/lim.h new file mode 100644 index 00000000..1e573cad --- /dev/null +++ b/arch/riscv32/fe310/src/freedom-metal/metal/lim.h @@ -0,0 +1,20 @@ +/* Copyright 2020 SiFive, Inc */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#ifndef METAL__LIM_H +#define METAL__LIM_H + +/*! @file lim.h + * + * API for manipulating LIM allocation + */ + +/*! @def METAL_PLACE_IN_LIM + * @brief Link a function into the LIM + * + * Link a function into the LIM (Loosely Integrated + * Memory) if the LIM is present on the target device. + */ +#define METAL_PLACE_IN_LIM __attribute__((section(".lim"))) + +#endif diff --git a/arch/riscv32/fe310/src/freedom-metal/metal/lock.h b/arch/riscv32/fe310/src/freedom-metal/metal/lock.h new file mode 100644 index 00000000..e591eaef --- /dev/null +++ b/arch/riscv32/fe310/src/freedom-metal/metal/lock.h @@ -0,0 +1,145 @@ +/* Copyright 2019 SiFive, Inc */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#ifndef METAL__LOCK_H +#define METAL__LOCK_H + +#include +#include +#include + +/*! + * @file lock.h + * @brief An API for creating and using a software lock/mutex + */ + +/* TODO: How can we make the exception code platform-independant? */ +#define _METAL_STORE_AMO_ACCESS_FAULT 7 + +#define METAL_LOCK_BACKOFF_CYCLES 32 +#define METAL_LOCK_BACKOFF_EXPONENT 2 + +/*! + * @def METAL_LOCK_DECLARE + * @brief Declare a lock + * + * Locks must be declared with METAL_LOCK_DECLARE to ensure that the lock + * is linked into a memory region which supports atomic memory operations. + */ +#define METAL_LOCK_DECLARE(name) \ + __attribute__((section(".data.locks"))) struct metal_lock name + +/*! + * @brief A handle for a lock + */ +struct metal_lock { + int _state; +}; + +/*! + * @brief Initialize a lock + * @param lock The handle for a lock + * @return 0 if the lock is successfully initialized. A non-zero code indicates + * failure. + * + * If the lock cannot be initialized, attempts to take or give the lock + * will result in a Store/AMO access fault. + */ +__inline__ int metal_lock_init(struct metal_lock *lock) { +#ifdef __riscv_atomic + /* Get a handle for the memory which holds the lock state */ + struct metal_memory *lock_mem = + metal_get_memory_from_address((uintptr_t) & (lock->_state)); + if (!lock_mem) { + return 1; + } + + /* If the memory doesn't support atomics, report an error */ + if (!metal_memory_supports_atomics(lock_mem)) { + return 2; + } + + lock->_state = 0; + + return 0; +#else + return 3; +#endif +} + +/*! + * @brief Take a lock + * @param lock The handle for a lock + * @return 0 if the lock is successfully taken + * + * If the lock initialization failed, attempts to take a lock will result in + * a Store/AMO access fault. + */ +__inline__ int metal_lock_take(struct metal_lock *lock) { +#ifdef __riscv_atomic + int old = 1; + int new = 1; + + int backoff = 1; + const int max_backoff = METAL_LOCK_BACKOFF_CYCLES * METAL_MAX_CORES; + + while (1) { + __asm__ volatile("amoswap.w.aq %[old], %[new], (%[state])" + : [old] "=r"(old) + : [new] "r"(new), [state] "r"(&(lock->_state)) + : "memory"); + + if (old == 0) { + break; + } + + for (int i = 0; i < backoff; i++) { + __asm__ volatile(""); + } + + if (backoff < max_backoff) { + backoff *= METAL_LOCK_BACKOFF_EXPONENT; + } + } + + return 0; +#else + /* Store the memory address in mtval like a normal store/amo access fault */ + __asm__("csrw mtval, %[state]" ::[state] "r"(&(lock->_state))); + + /* Trigger a Store/AMO access fault */ + _metal_trap(_METAL_STORE_AMO_ACCESS_FAULT); + + /* If execution returns, indicate failure */ + return 1; +#endif +} + +/*! + * @brief Give back a held lock + * @param lock The handle for a lock + * @return 0 if the lock is successfully given + * + * If the lock initialization failed, attempts to give a lock will result in + * a Store/AMO access fault. + */ +__inline__ int metal_lock_give(struct metal_lock *lock) { +#ifdef __riscv_atomic + __asm__ volatile( + "amoswap.w.rl x0, x0, (%[state])" ::[state] "r"(&(lock->_state)) + : "memory"); + + return 0; +#else + /* Store the memory address in mtval like a normal store/amo access fault */ + __asm__("csrw mtval, %[state]" ::[state] "r"(&(lock->_state))); + + /* Trigger a Store/AMO access fault */ + _metal_trap(_METAL_STORE_AMO_ACCESS_FAULT); + + /* If execution returns, indicate failure */ + return 1; +#endif +} + +#endif /* METAL__LOCK_H */ diff --git a/arch/riscv32/fe310/src/freedom-metal/metal/memory.h b/arch/riscv32/fe310/src/freedom-metal/metal/memory.h new file mode 100644 index 00000000..f009e9ec --- /dev/null +++ b/arch/riscv32/fe310/src/freedom-metal/metal/memory.h @@ -0,0 +1,83 @@ +/* Copyright 2019 SiFive, Inc */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#ifndef METAL__MEMORY_H +#define METAL__MEMORY_H + +#include +#include + +/*! + * @file memory.h + * + * @brief API for enumerating memory blocks + */ + +struct _metal_memory_attributes { + unsigned int R : 1; + unsigned int W : 1; + unsigned int X : 1; + unsigned int C : 1; + unsigned int A : 1; +}; + +/*! + * @brief A handle for a memory block + */ +struct metal_memory { + const uintptr_t _base_address; + const size_t _size; + const struct _metal_memory_attributes _attrs; +}; + +/*! + * @brief Get the memory block which services the given address + * + * Given a physical memory address, get a handle for the memory block to which + * that address is mapped. + * + * @param address The address to query + * @return The memory block handle, or NULL if the address is not mapped to a + * memory block + */ +struct metal_memory *metal_get_memory_from_address(const uintptr_t address); + +/*! + * @brief Get the base address for a memory block + * @param memory The handle for the memory block + * @return The base address of the memory block + */ +__inline__ uintptr_t +metal_memory_get_base_address(const struct metal_memory *memory) { + return memory->_base_address; +} + +/*! + * @brief Get the size of a memory block + * @param memory The handle for the memory block + * @return The size of the memory block + */ +__inline__ size_t metal_memory_get_size(const struct metal_memory *memory) { + return memory->_size; +} + +/*! + * @brief Query if a memory block supports atomic operations + * @param memory The handle for the memory block + * @return nonzero if the memory block supports atomic operations + */ +__inline__ int +metal_memory_supports_atomics(const struct metal_memory *memory) { + return memory->_attrs.A; +} + +/*! + * @brief Query if a memory block is cacheable + * @param memory The handle for the memory block + * @return nonzero if the memory block is cachable + */ +__inline__ int metal_memory_is_cachable(const struct metal_memory *memory) { + return memory->_attrs.C; +} + +#endif /* METAL__MEMORY_H */ diff --git a/arch/riscv32/fe310/src/freedom-metal/metal/nmi.h b/arch/riscv32/fe310/src/freedom-metal/metal/nmi.h new file mode 100644 index 00000000..2e9919db --- /dev/null +++ b/arch/riscv32/fe310/src/freedom-metal/metal/nmi.h @@ -0,0 +1,32 @@ +/* Copyright 2018 SiFive, Inc */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#ifndef METAL__NMI_H +#define METAL__NMI_H + +/*! @file nmi.h + * + * API for manipulating NMI allocation + */ + +#define _STR(x) #x + +#define _RNMI_IRQ(ID) _STR(.rnmi_irq_##ID) +#define _RNMI_EXCP(ID) _STR(.rnmi_excp_##ID) + +#define METAL_NMI_MNRET __asm__ volatile(".word 0x70200073") + +/*! @def METAL_PLACE_IN_RNMI_IRQ + * @brief Link a function into the RNMI interrupt handler. + */ +#define METAL_PLACE_IN_RNMI_IRQ(ID) \ + __attribute__((section(_RNMI_IRQ(ID)), naked)) + +/*! @def METAL_PLACE_IN_RNMI_EXCP + * @brief Link a function into the RNMI exception handler. + */ + +#define METAL_PLACE_IN_RNMI_EXCP(ID) \ + __attribute__((section(_RNMI_EXCP(ID)), interrupt)) + +#endif diff --git a/arch/riscv32/fe310/src/freedom-metal/metal/pmp.h b/arch/riscv32/fe310/src/freedom-metal/metal/pmp.h new file mode 100644 index 00000000..38ab1b9a --- /dev/null +++ b/arch/riscv32/fe310/src/freedom-metal/metal/pmp.h @@ -0,0 +1,214 @@ +/* Copyright 2018 SiFive, Inc */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#ifndef METAL__PMP_H +#define METAL__PMP_H + +/*! + * @file metal/pmp.h + * + * @brief API for Configuring Physical Memory Protection on RISC-V Cores + * + * The Physical Memory Protection (PMP) interface on RISC-V cores + * is a form of memory protection unit which allows for a finite number + * of physical memory regions to be configured with certain access + * permissions. + * + * Additional information about the use and configuration rules for PMPs + * can be found by reading the RISC-V Privileged Architecture Specification. + */ + +#include +#include + +struct metal_pmp; + +/*! + * @brief Set of available PMP addressing modes + */ +enum metal_pmp_address_mode { + /*! @brief Disable the PMP region */ + METAL_PMP_OFF = 0, + /*! @brief Use Top-of-Range mode */ + METAL_PMP_TOR = 1, + /*! @brief Use naturally-aligned 4-byte region mode */ + METAL_PMP_NA4 = 2, + /*! @brief Use naturally-aligned power-of-two mode */ + METAL_PMP_NAPOT = 3 +}; + +/*! + * @brief Configuration for a PMP region + */ +struct metal_pmp_config { + /*! @brief Sets whether reads to the PMP region succeed */ + unsigned int R : 1; + /*! @brief Sets whether writes to the PMP region succeed */ + unsigned int W : 1; + /*! @brief Sets whether the PMP region is executable */ + unsigned int X : 1; + + /*! @brief Sets the addressing mode of the PMP region */ + enum metal_pmp_address_mode A : 2; + + int _pad : 2; + + /*! @brief Sets whether the PMP region is locked */ + enum metal_pmp_locked { + METAL_PMP_UNLOCKED = 0, + METAL_PMP_LOCKED = 1 + } L : 1; +}; + +/*! + * @brief A handle for the PMP device + */ +struct metal_pmp { + /* The minimum granularity of the PMP region. Set by metal_pmp_init */ + uintptr_t _granularity[METAL_MAX_CORES]; +}; + +/*! + * @brief Get the PMP device handle + */ +struct metal_pmp *metal_pmp_get_device(void); + +/*! + * @brief Get the number of pmp regions for the hartid + */ +int metal_pmp_num_regions(int hartid); + +/*! + * @brief Initialize the PMP + * @param pmp The PMP device handle to be initialized + * + * The PMP initialization routine is optional and may be called as many times + * as is desired. The effect of the initialization routine is to attempt to set + * all regions to unlocked and disabled, as well as to clear the X, W, and R + * bits. Only the pmp configuration of the hart which executes the routine will + * be affected. + * + * If any regions are fused to preset values by the implementation or locked, + * those PMP regions will silently remain uninitialized. + */ +void metal_pmp_init(struct metal_pmp *pmp); + +/*! + * @brief Configure a PMP region + * @param pmp The PMP device handle + * @param region The PMP region to configure + * @param config The desired configuration of the PMP region + * @param address The desired address of the PMP region + * @return 0 upon success + */ +int metal_pmp_set_region(struct metal_pmp *pmp, unsigned int region, + struct metal_pmp_config config, size_t address); + +/*! + * @brief Get the configuration for a PMP region + * @param pmp The PMP device handle + * @param region The PMP region to read + * @param config Variable to store the PMP region configuration + * @param address Variable to store the PMP region address + * @return 0 if the region is read successfully + */ +int metal_pmp_get_region(struct metal_pmp *pmp, unsigned int region, + struct metal_pmp_config *config, size_t *address); + +/*! + * @brief Lock a PMP region + * @param pmp The PMP device handle + * @param region The PMP region to lock + * @return 0 if the region is successfully locked + */ +int metal_pmp_lock(struct metal_pmp *pmp, unsigned int region); + +/*! + * @brief Set the address for a PMP region + * @param pmp The PMP device handle + * @param region The PMP region to set + * @param address The desired address of the PMP region + * @return 0 if the address is successfully set + */ +int metal_pmp_set_address(struct metal_pmp *pmp, unsigned int region, + size_t address); + +/*! + * @brief Get the address of a PMP region + * @param pmp The PMP device handle + * @param region The PMP region to read + * @return The address of the PMP region, or 0 if the region could not be read + */ +size_t metal_pmp_get_address(struct metal_pmp *pmp, unsigned int region); + +/*! + * @brief Set the addressing mode of a PMP region + * @param pmp The PMP device handle + * @param region The PMP region to set + * @param mode The PMP addressing mode to set + * @return 0 if the addressing mode is successfully set + */ +int metal_pmp_set_address_mode(struct metal_pmp *pmp, unsigned int region, + enum metal_pmp_address_mode mode); + +/*! + * @brief Get the addressing mode of a PMP region + * @param pmp The PMP device handle + * @param region The PMP region to read + * @return The address mode of the PMP region + */ +enum metal_pmp_address_mode metal_pmp_get_address_mode(struct metal_pmp *pmp, + unsigned int region); + +/*! + * @brief Set the executable bit for a PMP region + * @param pmp The PMP device handle + * @param region The PMP region to set + * @param X The desired value of the executable bit + * @return 0 if the executable bit is successfully set + */ +int metal_pmp_set_executable(struct metal_pmp *pmp, unsigned int region, int X); + +/*! + * @brief Get the executable bit for a PMP region + * @param pmp The PMP device handle + * @param region The PMP region to read + * @return the value of the executable bit + */ +int metal_pmp_get_executable(struct metal_pmp *pmp, unsigned int region); + +/*! + * @brief Set the writable bit for a PMP region + * @param pmp The PMP device handle + * @param region The PMP region to set + * @param W The desired value of the writable bit + * @return 0 if the writable bit is successfully set + */ +int metal_pmp_set_writeable(struct metal_pmp *pmp, unsigned int region, int W); + +/*! + * @brief Get the writable bit for a PMP region + * @param pmp The PMP device handle + * @param region The PMP region to read + * @return the value of the writable bit + */ +int metal_pmp_get_writeable(struct metal_pmp *pmp, unsigned int region); + +/*! + * @brief Set the readable bit for a PMP region + * @param pmp The PMP device handle + * @param region The PMP region to set + * @param R The desired value of the readable bit + * @return 0 if the readable bit is successfully set + */ +int metal_pmp_set_readable(struct metal_pmp *pmp, unsigned int region, int R); + +/*! + * @brief Set the readable bit for a PMP region + * @param pmp The PMP device handle + * @param region The PMP region to read + * @return the value of the readable bit + */ +int metal_pmp_get_readable(struct metal_pmp *pmp, unsigned int region); + +#endif diff --git a/arch/riscv32/fe310/src/freedom-metal/metal/prci.h b/arch/riscv32/fe310/src/freedom-metal/metal/prci.h new file mode 100644 index 00000000..edcf07d0 --- /dev/null +++ b/arch/riscv32/fe310/src/freedom-metal/metal/prci.h @@ -0,0 +1,32 @@ +/* Copyright 2021 SiFive, Inc */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#ifndef METAL__PRCI_H +#define METAL__PRCI_H + +struct metal_prci; + +struct metal_prci_vtable { + unsigned long (*get_reg)(struct metal_prci *prci, unsigned long offset); + unsigned long (*set_reg)(struct metal_prci *prci, unsigned long offset, + unsigned long value); +}; + +struct metal_prci { + const struct metal_prci_vtable *vtable; +}; + +__inline__ unsigned long metal_prci_get_reg(struct metal_prci *prci, + unsigned long offset) { + return prci->vtable->get_reg(prci, offset); +} + +__inline__ unsigned long metal_prci_set_reg(struct metal_prci *prci, + unsigned long offset, + unsigned long value) { + return prci->vtable->set_reg(prci, offset, value); +} + +struct metal_prci *metal_prci_get_device(void); + +#endif /* METAL__PRCI_H */ diff --git a/arch/riscv32/fe310/src/freedom-metal/metal/privilege.h b/arch/riscv32/fe310/src/freedom-metal/metal/privilege.h new file mode 100644 index 00000000..522e7efe --- /dev/null +++ b/arch/riscv32/fe310/src/freedom-metal/metal/privilege.h @@ -0,0 +1,122 @@ +/* Copyright 2019 SiFive, Inc */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#ifndef METAL__PRIVILEGE_H +#define METAL__PRIVILEGE_H + +/*! + * @file metal/privilege.h + * + * @brief API for manipulating the privilege mode of a RISC-V system + * + * Additional information about privilege modes on RISC-V systems can be found + * by reading the RISC-V Privileged Architecture Specification v1.10. + */ + +#include + +enum metal_privilege_mode { + METAL_PRIVILEGE_USER = 0, + METAL_PRIVILEGE_SUPERVISOR = 1, + METAL_PRIVILEGE_MACHINE = 3, +}; + +#if __riscv_xlen == 32 +typedef uint32_t metal_xreg_t; +#elif __riscv_xlen == 64 +typedef uint64_t metal_xreg_t; +#endif + +#if __riscv_flen == 32 +typedef uint32_t metal_freg_t; +#elif __riscv_flen == 64 +typedef uint64_t metal_freg_t; +#endif + +struct metal_register_file { + metal_xreg_t ra; + metal_xreg_t sp; + metal_xreg_t gp; + metal_xreg_t tp; + + metal_xreg_t t0; + metal_xreg_t t1; + metal_xreg_t t2; + + metal_xreg_t s0; + metal_xreg_t s1; + + metal_xreg_t a0; + metal_xreg_t a1; + metal_xreg_t a2; + metal_xreg_t a3; + metal_xreg_t a4; + metal_xreg_t a5; +#ifndef __riscv_32e + metal_xreg_t a6; + metal_xreg_t a7; + + metal_xreg_t s2; + metal_xreg_t s3; + metal_xreg_t s4; + metal_xreg_t s5; + metal_xreg_t s6; + metal_xreg_t s7; + metal_xreg_t s8; + metal_xreg_t s9; + metal_xreg_t s10; + metal_xreg_t s11; + + metal_xreg_t t3; + metal_xreg_t t4; + metal_xreg_t t5; + metal_xreg_t t6; +#endif /* __riscv_32e */ + +#ifdef __riscv_flen + metal_freg_t ft0; + metal_freg_t ft1; + metal_freg_t ft2; + metal_freg_t ft3; + metal_freg_t ft4; + metal_freg_t ft5; + metal_freg_t ft6; + metal_freg_t ft7; + + metal_freg_t fs0; + metal_freg_t fs1; + + metal_freg_t fa0; + metal_freg_t fa1; + metal_freg_t fa2; + metal_freg_t fa3; + metal_freg_t fa4; + metal_freg_t fa5; + metal_freg_t fa6; + metal_freg_t fa7; + + metal_freg_t fs2; + metal_freg_t fs3; + metal_freg_t fs4; + metal_freg_t fs5; + metal_freg_t fs6; + metal_freg_t fs7; + metal_freg_t fs8; + metal_freg_t fs9; + metal_freg_t fs10; + metal_freg_t fs11; + + metal_freg_t ft8; + metal_freg_t ft9; + metal_freg_t ft10; + metal_freg_t ft11; +#endif /* __riscv_flen */ +}; + +typedef void (*metal_privilege_entry_point_t)(void); + +void metal_privilege_drop_to_mode(enum metal_privilege_mode mode, + struct metal_register_file regfile, + metal_privilege_entry_point_t entry_point); + +#endif diff --git a/arch/riscv32/fe310/src/freedom-metal/metal/pwm.h b/arch/riscv32/fe310/src/freedom-metal/metal/pwm.h new file mode 100644 index 00000000..600d5a02 --- /dev/null +++ b/arch/riscv32/fe310/src/freedom-metal/metal/pwm.h @@ -0,0 +1,162 @@ +/* Copyright 2020 SiFive, Inc */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#ifndef METAL__PWM_H +#define METAL__PWM_H + +/*! @brief Enums for PWM running modes. */ +typedef enum { + METAL_PWM_CONTINUOUS = 0, + METAL_PWM_ONE_SHOT = 1 +} metal_pwm_run_mode_t; + +/*! @brief Enums for Phase correct PWM. */ +typedef enum { + METAL_PWM_PHASE_CORRECT_DISABLE = 0, + METAL_PWM_PHASE_CORRECT_ENABLE = 1, +} metal_pwm_phase_correct_t; + +/*! @brief Enums for Interrupts enable/disable. */ +typedef enum { + METAL_PWM_INTERRUPT_DISABLE = 0, + METAL_PWM_INTERRUPT_ENABLE = 1, +} metal_pwm_interrupt_t; + +struct metal_pwm; + +/*! @brief vtable for PWM. */ +struct metal_pwm_vtable { + int (*enable)(struct metal_pwm *pwm); + int (*disable)(struct metal_pwm *pwm); + int (*set_freq)(struct metal_pwm *pwm, unsigned int idx, unsigned int freq); + int (*set_duty)(struct metal_pwm *pwm, unsigned int idx, unsigned int duty, + metal_pwm_phase_correct_t phase_corr); + unsigned int (*get_duty)(struct metal_pwm *pwm, unsigned int idx); + unsigned int (*get_freq)(struct metal_pwm *pwm, unsigned int idx); + int (*trigger)(struct metal_pwm *pwm, unsigned int idx, + metal_pwm_run_mode_t mode); + int (*stop)(struct metal_pwm *pwm, unsigned int idx); + int (*cfg_interrupt)(struct metal_pwm *pwm, metal_pwm_interrupt_t flag); + int (*clr_interrupt)(struct metal_pwm *pwm, unsigned int idx); + struct metal_interrupt *(*get_interrupt_controller)(struct metal_pwm *pwm); + int (*get_interrupt_id)(struct metal_pwm *pwm, unsigned int idx); +}; + +/*! @brief A handle for a PWM device. */ +struct metal_pwm { + const struct metal_pwm_vtable *vtable; +}; + +/*! @brief Gets a PWM device handle. + * @param device_num The index of the desired PWM device. + * @return A handle to the PWM device, or NULL if the device does not exist.*/ +struct metal_pwm *metal_pwm_get_device(unsigned int device_num); + +/*! @brief Enables PWM operation. + * @param pwm The handle for the PWM device to initialize. + * @return 0 If no error.*/ +inline int metal_pwm_enable(struct metal_pwm *pwm) { + return pwm->vtable->enable(pwm); +} + +/*! @brief Disables PWM operation. + * @param pwm The handle for the PWM device to be disabled. + * @return 0 If no error.*/ +inline int metal_pwm_disable(struct metal_pwm *pwm) { + return pwm->vtable->disable(pwm); +} + +/*! @brief Sets frequency in Hz for a given PWM instance. + * @param pwm PWM device handle. + * @param idx PWM channel id. + * @param freq PWM frequency in Hz. + * @return 0 If no error.*/ +inline int metal_pwm_set_freq(struct metal_pwm *pwm, unsigned int idx, + unsigned int freq) { + return pwm->vtable->set_freq(pwm, idx, freq); +} + +/*! @brief Sets duty cycle in percent values [0 - 100] for a given PWM instance. + * Phase correct mode provides center aligned PWM waveform output. + * @param pwm PWM device handle. + * @param idx PWM channel id. + * @param duty PWM duty cycle value. + * @param phase_corr Enable / Disable phase correct mode. + * @return 0 If no error.*/ +inline int metal_pwm_set_duty(struct metal_pwm *pwm, unsigned int idx, + unsigned int duty, + metal_pwm_phase_correct_t phase_corr) { + return pwm->vtable->set_duty(pwm, idx, duty, phase_corr); +} + +/*! @brief Gets duty cycle in percent values [0 - 100] for a given PWM instance. + * @param pwm PWM device handle. + * @param idx PWM channel id. + * @return PWM duty cycle value.*/ +inline unsigned int metal_pwm_get_duty(struct metal_pwm *pwm, + unsigned int idx) { + return pwm->vtable->get_duty(pwm, idx); +} + +/*! @brief Gets frequency in Hz for a given PWM instance. + * @param pwm PWM device handle. + * @param idx PWM channel id. + * @return PWM frequency in Hz.*/ +inline unsigned int metal_pwm_get_freq(struct metal_pwm *pwm, + unsigned int idx) { + return pwm->vtable->get_freq(pwm, idx); +} + +/*! @brief Starts a PWM instance in selected run mode (continuous/one shot). + * @param pwm PWM device handle. + * @param idx PWM channel id. + * @return 0 If no error.*/ +inline int metal_pwm_trigger(struct metal_pwm *pwm, unsigned int idx, + metal_pwm_run_mode_t mode) { + return pwm->vtable->trigger(pwm, idx, mode); +} + +/*! @brief Stops a running PWM instance in continuous mode. + * @param pwm PWM device handle. + * @param idx PWM channel id. + * @return 0 If no error.*/ +inline int metal_pwm_stop(struct metal_pwm *pwm, unsigned int idx) { + return pwm->vtable->stop(pwm, idx); +} + +/*! @brief Enable or Disable PWM interrupts. + * @param pwm PWM device handle. + * @param flag PWM interrupt enable flag. + * @return 0 If no error.*/ +inline int metal_pwm_cfg_interrupt(struct metal_pwm *pwm, + metal_pwm_interrupt_t flag) { + return pwm->vtable->cfg_interrupt(pwm, flag); +} + +/*! @brief Clears pending interrupt flags. + * @param pwm PWM device handle. + * @param idx PWM channel id. + * @return 0 If no error.*/ +inline int metal_pwm_clr_interrupt(struct metal_pwm *pwm, unsigned int idx) { + return pwm->vtable->clr_interrupt(pwm, idx); +} + +/*! @brief Get the interrupt controller of the PWM peripheral. + * The interrupt controller must be initialized before any interrupts can be + * registered or enabled with it. + * @param pwm PWM device handle. + * @return The handle for the PWM interrupt controller.*/ +inline struct metal_interrupt * +metal_pwm_interrupt_controller(struct metal_pwm *pwm) { + return pwm->vtable->get_interrupt_controller(pwm); +} + +/*! @brief Get the interrupt ID of the PWM peripheral. + * @param pwm PWM device handle. + * @param idx PWM channel id. + * @return The PWM interrupt id.*/ +inline int metal_pwm_get_interrupt_id(struct metal_pwm *pwm, unsigned int idx) { + return pwm->vtable->get_interrupt_id(pwm, idx); +} + +#endif diff --git a/arch/riscv32/fe310/src/freedom-metal/metal/remapper.h b/arch/riscv32/fe310/src/freedom-metal/metal/remapper.h new file mode 100644 index 00000000..cca1d8ea --- /dev/null +++ b/arch/riscv32/fe310/src/freedom-metal/metal/remapper.h @@ -0,0 +1,246 @@ +/* Copyright 2021 SiFive, Inc */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#ifndef METAL__REMAPPER_H +#define METAL__REMAPPER_H + +#include + +struct metal_remapper; +struct metal_remapper_entry; + +/*! @brief vtable for address remapper. */ +struct metal_remapper_vtable { + int (*enable_remap)(struct metal_remapper *remapper, int idx); + int (*disable_remap)(struct metal_remapper *remapper, int idx); + int (*enable_remaps)(struct metal_remapper *remapper, int idxs[], + int num_idxs); + int (*disable_remaps)(struct metal_remapper *remapper, int idxs[], + int num_idxs); + uint32_t (*get_valid)(struct metal_remapper *remapper, int idx); + int (*set_valid)(struct metal_remapper *remapper, int idx, uint32_t val); + int (*flush)(struct metal_remapper *remapper); + uint64_t (*get_from_region_base)(struct metal_remapper *remapper); + uint64_t (*get_from_region_size)(struct metal_remapper *remapper); + uint64_t (*get_to_region_base)(struct metal_remapper *remapper); + uint64_t (*get_to_region_size)(struct metal_remapper *remapper); + uint64_t (*get_max_from_entry_region_size)(struct metal_remapper *remapper); + uint32_t (*get_version)(struct metal_remapper *remapper); + uint32_t (*get_entries)(struct metal_remapper *remapper); + int (*set_remap)(struct metal_remapper *remapper, + struct metal_remapper_entry *entry); + int (*set_remaps)(struct metal_remapper *remapper, + struct metal_remapper_entry *entries[], int num_entries); + uint64_t (*get_from)(struct metal_remapper *remapper, int idx); + uint64_t (*get_to)(struct metal_remapper *remapper, int idx); +}; + +/*! @brief A handle for a address remapper device. */ +struct metal_remapper { + const struct metal_remapper_vtable *vtable; +}; + +/*! @brief Remap entry descriptor. */ +struct metal_remapper_entry { + int idx; /*!< Index of remap entry. */ + uint64_t from_addr; /*!< From[] entry address to be remapped. */ + uint64_t to_addr; /*!< To[] entry address to be remapped. */ +}; + +/*! @brief Enables an address remapper entry. + * @param remapper Address remapper device handle. + * @param idx Index of remap entry to enable. + * @return 0 If no error.*/ +inline int __metal_remapper_enable_remap(struct metal_remapper *remapper, + int idx) { + return remapper->vtable->enable_remap(remapper, idx); +} + +/*! @brief Disables an address remapper entry. + * @param remapper Address remapper device handle. + * @param idx Index of remap entry to disable. + * @return 0 If no error. + */ +inline int __metal_remapper_disable_remap(struct metal_remapper *remapper, + int idx) { + return remapper->vtable->disable_remap(remapper, idx); +} + +/*! @brief Batch enables address remapper entries. + * @param remapper Address remapper device handle. + * @param idxs[] Array of indexes of remap entries to enable. + * @param num_idxs Number of indexes passed to idxs[] array. + * @return 0 If no error.*/ +inline int __metal_remapper_enable_remaps(struct metal_remapper *remapper, + int idxs[], int num_idxs) { + return remapper->vtable->enable_remaps(remapper, idxs, num_idxs); +} + +/*! @brief Batch disables an address remapper entries. + * @param remapper Address remapper device handle. + * @param idxs[] Array of indexes of remap entries to disable. + * @param num_idxs Number of indexes passed to idxs[] array. + * @return 0 If no error. + */ +inline int __metal_remapper_disable_remaps(struct metal_remapper *remapper, + int idxs[], int num_idxs) { + return remapper->vtable->disable_remaps(remapper, idxs, num_idxs); +} + +/*! @brief Get remappervalid[] register content. + * @param remapper Address remapper device handle. + * @param idx Index of remappervalid[] register. + * @return 0 If no error. + */ +inline uint32_t __metal_remapper_get_valid(struct metal_remapper *remapper, + int idx) { + return remapper->vtable->get_valid(remapper, idx); +} + +/*! @brief Set remappervalid[] register with given value. + * @param remapper Address remapper device handle. + * @param idx Index of remappervalid[] register. + * @param val Value to be set. + * @return 0 If no error. + */ +inline int __metal_remapper_set_valid(struct metal_remapper *remapper, int idx, + uint32_t val) { + return remapper->vtable->set_valid(remapper, idx, val); +} + +/*! @brief Disable all address remapper entries. + * @param remapper Address remapper device handle. + * @return 0 If no error. + */ +inline int __metal_remapper_flush(struct metal_remapper *remapper) { + return remapper->vtable->flush(remapper); +} + +/*! @brief Get hardware configured from region base address. + * @param remapper Address remapper device handle. + */ +inline uint64_t +__metal_remapper_get_from_region_base(struct metal_remapper *remapper) { + return remapper->vtable->get_from_region_base(remapper); +} + +/*! @brief Get hardware configured from region size. + * @param remapper Address remapper device handle. + */ +inline uint64_t +__metal_remapper_get_from_region_size(struct metal_remapper *remapper) { + return remapper->vtable->get_from_region_size(remapper); +} + +/*! @brief Get hardware configured to region base address. + * @param remapper Address remapper device handle. + */ +inline uint64_t +__metal_remapper_get_to_region_base(struct metal_remapper *remapper) { + return remapper->vtable->get_to_region_base(remapper); +} + +/*! @brief Get hardware configured to region size. + * @param remapper Address remapper device handle. + */ +inline uint64_t +__metal_remapper_get_to_region_size(struct metal_remapper *remapper) { + return remapper->vtable->get_to_region_size(remapper); +} + +/*! @brief Get hardware configured maximum from entry region size. + * @param remapper Address remapper device handle. + */ +inline uint64_t __metal_remapper_get_max_from_entry_region_size( + struct metal_remapper *remapper) { + return remapper->vtable->get_max_from_entry_region_size(remapper); +} + +/*! @brief Get hardware version of address remapper. + * @param remapper Address remapper device handle. + * @return current hardware version of address remapper. + */ +inline uint32_t __metal_remapper_get_version(struct metal_remapper *remapper) { + return remapper->vtable->get_version(remapper); +} + +/*! @brief Get number of entries in address remapper. + * @param remapper Address remapper device handle. + * @return number of entries in address remapper. + */ +inline uint32_t __metal_remapper_get_entries(struct metal_remapper *remapper) { + return remapper->vtable->get_entries(remapper); +} + +/*! @brief Set from/to remap entry. + * @param remapper Address remapper device handle. + * @param entry Pointer to the remap entry descriptor. + * @return 0 If no error. + */ +inline int __metal_remapper_set_remap(struct metal_remapper *remapper, + struct metal_remapper_entry *entry) { + return remapper->vtable->set_remap(remapper, entry); +} + +/*! @brief Batch set from/to remap entries. + * @param remapper Address remapper device handle. + * @param entries Array of pointers to the remap entry descriptors. + * @return 0 If no error. + */ +inline int __metal_remapper_set_remaps(struct metal_remapper *remapper, + struct metal_remapper_entry *entries[], + int num_entries) { + return remapper->vtable->set_remaps(remapper, entries, num_entries); +} + +/*! @brief Get the from address of a remap entry. + * @param remapper Address remapper device handle. + * @param idx Index of remap entry. + * @return From address of the remap entry. + */ +inline uint64_t __metal_remapper_get_from(struct metal_remapper *remapper, + int idx) { + return remapper->vtable->get_from(remapper, idx); +} + +/*! @brief Get the to address of a remap entry. + * @param remapper Address remapper device handle. + * @param idx Index of remap entry. + * @return To address of the remap entry. + */ +inline uint64_t __metal_remapper_get_to(struct metal_remapper *remapper, + int idx) { + return remapper->vtable->get_to(remapper, idx); +} + +/*! + * @brief The public METAL address remapper interfaces. + */ +struct metal_remapper *metal_remapper_get_device(void); +int metal_remapper_enable_remap(struct metal_remapper *remapper, int idx); +int metal_remapper_disable_remap(struct metal_remapper *remapper, int idx); +int metal_remapper_enable_remaps(struct metal_remapper *remapper, int idx[], + int num_idxs); +int metal_remapper_disable_remaps(struct metal_remapper *remapper, int idx[], + int num_idxs); +uint32_t metal_remapper_get_valid(struct metal_remapper *remapper, int idx); +int metal_remapper_set_valid(struct metal_remapper *remapper, int idx, + uint32_t val); +int metal_remapper_flush(struct metal_remapper *remapper); +uint64_t metal_remapper_get_from_region_base(struct metal_remapper *remapper); +uint64_t metal_remapper_get_from_region_size(struct metal_remapper *remapper); +uint64_t metal_remapper_get_to_region_base(struct metal_remapper *remapper); +uint64_t metal_remapper_get_to_region_size(struct metal_remapper *remapper); +uint64_t +metal_remapper_get_max_from_entry_region_size(struct metal_remapper *remapper); +uint32_t metal_remapper_get_version(struct metal_remapper *remapper); +uint32_t metal_remapper_get_entries(struct metal_remapper *remapper); +int metal_remapper_set_remap(struct metal_remapper *remapper, + struct metal_remapper_entry *entry); +int metal_remapper_set_remaps(struct metal_remapper *remapper, + struct metal_remapper_entry *entries[], + int num_entries); +uint64_t metal_remapper_get_from(struct metal_remapper *remapper, int idx); +uint64_t metal_remapper_get_to(struct metal_remapper *remapper, int idx); + +#endif /*METAL__REMAPPER_H */ diff --git a/arch/riscv32/fe310/src/freedom-metal/metal/rtc.h b/arch/riscv32/fe310/src/freedom-metal/metal/rtc.h new file mode 100644 index 00000000..e1b79826 --- /dev/null +++ b/arch/riscv32/fe310/src/freedom-metal/metal/rtc.h @@ -0,0 +1,137 @@ +/* Copyright 2019 SiFive, Inc. */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#ifndef METAL__RTC_H +#define METAL__RTC_H + +#include + +/*! + * @file rtc.h + * @brief API for Real-Time Clocks + */ + +struct metal_rtc; + +/*! + * @brief List of RTC run behaviors + */ +enum metal_rtc_run_option { + METAL_RTC_STOP = 0, + METAL_RTC_RUN, +}; + +struct metal_rtc_vtable { + uint64_t (*get_rate)(const struct metal_rtc *const rtc); + uint64_t (*set_rate)(const struct metal_rtc *const rtc, + const uint64_t rate); + uint64_t (*get_compare)(const struct metal_rtc *const rtc); + uint64_t (*set_compare)(const struct metal_rtc *const rtc, + const uint64_t compare); + uint64_t (*get_count)(const struct metal_rtc *const rtc); + uint64_t (*set_count)(const struct metal_rtc *const rtc, + const uint64_t count); + int (*run)(const struct metal_rtc *const rtc, + const enum metal_rtc_run_option option); + struct metal_interrupt *(*get_interrupt)(const struct metal_rtc *const rtc); + int (*get_interrupt_id)(const struct metal_rtc *const rtc); +}; + +/*! + * @brief Handle for a Real-Time Clock + */ +struct metal_rtc { + const struct metal_rtc_vtable *vtable; +}; + +/*! + * @brief Get the rate of the RTC + * @return The rate in Hz + */ +inline uint64_t metal_rtc_get_rate(const struct metal_rtc *const rtc) { + return rtc->vtable->get_rate(rtc); +} + +/*! + * @brief Set (if possible) the rate of the RTC + * @return The new rate of the RTC (not guaranteed to be the same as requested) + */ +inline uint64_t metal_rtc_set_rate(const struct metal_rtc *const rtc, + const uint64_t rate) { + return rtc->vtable->set_rate(rtc, rate); +} + +/*! + * @brief Get the compare value of the RTC + * @return The compare value + */ +inline uint64_t metal_rtc_get_compare(const struct metal_rtc *const rtc) { + return rtc->vtable->get_compare(rtc); +} + +/*! + * @brief Set the compare value of the RTC + * @return The set compare value (not guaranteed to be exactly the requested + * value) + * + * The RTC device might impose limits on the maximum compare value or the + * granularity of the compare value. + */ +inline uint64_t metal_rtc_set_compare(const struct metal_rtc *const rtc, + const uint64_t compare) { + return rtc->vtable->set_compare(rtc, compare); +} + +/*! + * @brief Get the current count of the RTC + * @return The count + */ +inline uint64_t metal_rtc_get_count(const struct metal_rtc *const rtc) { + return rtc->vtable->get_count(rtc); +} + +/*! + * @brief Set the current count of the RTC + * @return The set value of the count (not guaranteed to be exactly the + * requested value) + * + * The RTC device might impose limits on the maximum value of the count + */ +inline uint64_t metal_rtc_set_count(const struct metal_rtc *const rtc, + const uint64_t count) { + return rtc->vtable->set_count(rtc, count); +} + +/*! + * @brief Start or stop the RTC + * @return 0 if the RTC was successfully started/stopped + */ +inline int metal_rtc_run(const struct metal_rtc *const rtc, + const enum metal_rtc_run_option option) { + return rtc->vtable->run(rtc, option); +} + +/*! + * @brief Get the interrupt handle for the RTC compare + * @return The interrupt handle + */ +inline struct metal_interrupt * +metal_rtc_get_interrupt(const struct metal_rtc *const rtc) { + return rtc->vtable->get_interrupt(rtc); +} + +/*! + * @brief Get the interrupt ID for the RTC compare + * @return The interrupt ID + */ +inline int metal_rtc_get_interrupt_id(const struct metal_rtc *const rtc) { + return rtc->vtable->get_interrupt_id(rtc); +} + +/*! + * @brief Get the handle for an RTC by index + * @return The RTC handle, or NULL if none is available at that index + */ +struct metal_rtc *metal_rtc_get_device(int index); + +#endif diff --git a/arch/riscv32/fe310/src/freedom-metal/metal/scrub.h b/arch/riscv32/fe310/src/freedom-metal/metal/scrub.h new file mode 100644 index 00000000..51683cc7 --- /dev/null +++ b/arch/riscv32/fe310/src/freedom-metal/metal/scrub.h @@ -0,0 +1,13 @@ +/* Copyright 2020 SiFive, Inc */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#ifndef METAL__SCRUB_H +#define METAL__SCRUB_H + +/*! @brief Writes specified memory region with zeros. + * @param address Start memory address for zero-scrub. + * @param size Memory region size in bytes. + * @return None.*/ +void metal_mem_scrub(void *address, int size); + +#endif diff --git a/arch/riscv32/fe310/src/freedom-metal/metal/shutdown.h b/arch/riscv32/fe310/src/freedom-metal/metal/shutdown.h new file mode 100644 index 00000000..7a43437b --- /dev/null +++ b/arch/riscv32/fe310/src/freedom-metal/metal/shutdown.h @@ -0,0 +1,41 @@ +/* Copyright 2018 SiFive, Inc */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#ifndef METAL__SHUTDOWN_H +#define METAL__SHUTDOWN_H + +/*! + * @file shutdown.h + * @brief API for shutting down a machine + */ + +struct __metal_shutdown; + +struct __metal_shutdown_vtable { + void (*exit)(const struct __metal_shutdown *sd, int code) + __attribute__((noreturn)); +}; + +struct __metal_shutdown { + const struct __metal_shutdown_vtable *vtable; +}; + +__inline__ void __metal_shutdown_exit(const struct __metal_shutdown *sd, + int code) __attribute__((noreturn)); +__inline__ void __metal_shutdown_exit(const struct __metal_shutdown *sd, + int code) { + sd->vtable->exit(sd, code); +} + +/*! + * @brief The public METAL shutdown interface + * + * Shuts down the machine, if the machine enables an interface for + * shutting down. When no interface is provided, will cause the machine + * to spin indefinitely. + * + * @param code The return code to set. 0 indicates program success. + */ +void metal_shutdown(int code) __attribute__((noreturn)); + +#endif diff --git a/arch/riscv32/fe310/src/freedom-metal/metal/spi.h b/arch/riscv32/fe310/src/freedom-metal/metal/spi.h new file mode 100644 index 00000000..7e4b04ae --- /dev/null +++ b/arch/riscv32/fe310/src/freedom-metal/metal/spi.h @@ -0,0 +1,97 @@ +/* Copyright 2018 SiFive, Inc */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#ifndef METAL__SPI_H +#define METAL__SPI_H + +struct metal_spi; + +/*! @brief The configuration for a SPI transfer */ +struct metal_spi_config { + /*! @brief The protocol for the SPI transfer */ + enum { METAL_SPI_SINGLE, METAL_SPI_DUAL, METAL_SPI_QUAD } protocol; + + /*! @brief The polarity of the SPI transfer, equivalent to CPOL */ + unsigned int polarity : 1; + /*! @brief The phase of the SPI transfer, equivalent to CPHA */ + unsigned int phase : 1; + /*! @brief The endianness of the SPI transfer */ + unsigned int little_endian : 1; + /*! @brief The active state of the chip select line */ + unsigned int cs_active_high : 1; + /*! @brief The chip select ID to activate for the SPI transfer */ + unsigned int csid; + /*! @brief The spi command frame number (cycles = num * frame_len) */ + unsigned int cmd_num; + /*! @brief The spi address frame number */ + unsigned int addr_num; + /*! @brief The spi dummy frame number */ + unsigned int dummy_num; + /*! @brief The Dual/Quad spi mode selection.*/ + enum { + MULTI_WIRE_ALL, + MULTI_WIRE_DATA_ONLY, + MULTI_WIRE_ADDR_DATA + } multi_wire; +}; + +struct metal_spi_vtable { + void (*init)(struct metal_spi *spi, int baud_rate); + int (*transfer)(struct metal_spi *spi, struct metal_spi_config *config, + size_t len, char *tx_buf, char *rx_buf); + int (*get_baud_rate)(struct metal_spi *spi); + int (*set_baud_rate)(struct metal_spi *spi, int baud_rate); +}; + +/*! @brief A handle for a SPI device */ +struct metal_spi { + const struct metal_spi_vtable *vtable; +}; + +/*! @brief Get a handle for a SPI device + * @param device_num The index of the desired SPI device + * @return A handle to the SPI device, or NULL if the device does not exist*/ +struct metal_spi *metal_spi_get_device(unsigned int device_num); + +/*! @brief Initialize a SPI device with a certain baud rate + * @param spi The handle for the SPI device to initialize + * @param baud_rate The baud rate to set the SPI device to + */ +__inline__ void metal_spi_init(struct metal_spi *spi, int baud_rate) { + spi->vtable->init(spi, baud_rate); +} + +/*! @brief Perform a SPI transfer + * @param spi The handle for the SPI device to perform the transfer + * @param config The configuration for the SPI transfer. + * @param len The number of bytes to transfer + * @param tx_buf The buffer to send over the SPI bus. Must be len bytes long. If + * NULL, the SPI will transfer the value 0. + * @param rx_buf The buffer to receive data into. Must be len bytes long. If + * NULL, the SPI will ignore received bytes. + * @return 0 if the transfer succeeds + */ +__inline__ int metal_spi_transfer(struct metal_spi *spi, + struct metal_spi_config *config, size_t len, + char *tx_buf, char *rx_buf) { + return spi->vtable->transfer(spi, config, len, tx_buf, rx_buf); +} + +/*! @brief Get the current baud rate of the SPI device + * @param spi The handle for the SPI device + * @return The baud rate in Hz + */ +__inline__ int metal_spi_get_baud_rate(struct metal_spi *spi) { + return spi->vtable->get_baud_rate(spi); +} + +/*! @brief Set the current baud rate of the SPI device + * @param spi The handle for the SPI device + * @param baud_rate The desired baud rate of the SPI device + * @return 0 if the baud rate is successfully changed + */ +__inline__ int metal_spi_set_baud_rate(struct metal_spi *spi, int baud_rate) { + return spi->vtable->set_baud_rate(spi, baud_rate); +} + +#endif diff --git a/arch/riscv32/fe310/src/freedom-metal/metal/switch.h b/arch/riscv32/fe310/src/freedom-metal/metal/switch.h new file mode 100644 index 00000000..695b21ae --- /dev/null +++ b/arch/riscv32/fe310/src/freedom-metal/metal/switch.h @@ -0,0 +1,56 @@ +/* Copyright 2018 SiFive, Inc */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#ifndef METAL__SWITCH_H +#define METAL__SWITCH_H + +/*! + * @file switch.h + * @brief API for reading toggle switches + */ + +#include + +struct metal_switch; + +struct metal_switch_vtable { + int (*switch_exist)(struct metal_switch *sw, char *label); + struct metal_interrupt *(*interrupt_controller)(struct metal_switch *sw); + int (*get_interrupt_id)(struct metal_switch *sw); +}; + +/*! + * @brief A handle for a switch + */ +struct metal_switch { + const struct metal_switch_vtable *vtable; +}; + +/*! + * @brief Get a handle for a switch + * @param label The DeviceTree label for the desired switch + * @return A handle to the switch, or NULL if none is found for the requested + * label + */ +struct metal_switch *metal_switch_get(char *label); + +/*! + * @brief Get the interrupt controller for a switch + * @param sw The handle for the switch + * @return The interrupt controller handle + */ +__inline__ struct metal_interrupt * +metal_switch_interrupt_controller(struct metal_switch *sw) { + return sw->vtable->interrupt_controller(sw); +} + +/*! + * @brief Get the interrupt id for a switch + * @param sw The handle for the switch + * @return The interrupt ID for the switch + */ +__inline__ int metal_switch_get_interrupt_id(struct metal_switch *sw) { + return sw->vtable->get_interrupt_id(sw); +} + +#endif diff --git a/arch/riscv32/fe310/src/freedom-metal/metal/time.h b/arch/riscv32/fe310/src/freedom-metal/metal/time.h new file mode 100644 index 00000000..a5a880f0 --- /dev/null +++ b/arch/riscv32/fe310/src/freedom-metal/metal/time.h @@ -0,0 +1,21 @@ +/* Copyright 2019 SiFive, Inc */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#ifndef METAL__TIME_H +#define METAL__TIME_H + +#include +#ifndef __SEGGER_LIBC__ +#include +#endif + +/*! + * @file time.h + * @brief API for dealing with time + */ + +int metal_gettimeofday(struct timeval *tp, void *tzp); + +time_t metal_time(void); + +#endif diff --git a/arch/riscv32/fe310/src/freedom-metal/metal/timer.h b/arch/riscv32/fe310/src/freedom-metal/metal/timer.h new file mode 100644 index 00000000..5d5132de --- /dev/null +++ b/arch/riscv32/fe310/src/freedom-metal/metal/timer.h @@ -0,0 +1,37 @@ +/* Copyright 2018 SiFive, Inc */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#ifndef METAL__TIMER_H +#define METAL__TIMER_H + +/*! + * @file timer.h + * @brief API for reading and manipulating the machine timer + */ + +/*! + * @brief Read the machine cycle count + * @param hartid The hart ID to read the cycle count of + * @param cyclecount The variable to hold the value + * @return 0 upon success + */ +int metal_timer_get_cyclecount(int hartid, unsigned long long *cyclecount); + +/*! + * @brief Get the machine timebase frequency + * @param hartid The hart ID to read the timebase of + * @param timebase The variable to hold the value + * @return 0 upon success + */ +int metal_timer_get_timebase_frequency(int hartid, + unsigned long long *timebase); + +/*! + * @brief Set the machine timer tick interval in seconds + * @param hartid The hart ID to read the timebase of + * @param second The number of seconds to set the tick interval to + * @return 0 upon success + */ +int metal_timer_set_tick(int hartid, int second); + +#endif diff --git a/arch/riscv32/fe310/src/freedom-metal/metal/tty.h b/arch/riscv32/fe310/src/freedom-metal/metal/tty.h new file mode 100644 index 00000000..5d41783a --- /dev/null +++ b/arch/riscv32/fe310/src/freedom-metal/metal/tty.h @@ -0,0 +1,35 @@ +/* Copyright 2018 SiFive, Inc */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#ifndef METAL__TTY_H +#define METAL__TTY_H + +/*! + * @file tty.h + * @brief API for emulated serial teriminals + */ + +/*! + * @brief Write a character to the default output device + * + * Write a character to the default output device, which for most + * targets is the UART serial port. + * + * @param c The character to write to the terminal + * @return 0 on success, or -1 on failure. + */ +int metal_tty_putc(int c); + +/*! + * @brief Get a byte from the default output device + * + * The default output device, is typically the UART serial port. + * + * This call is non-blocking, if nothing is ready c==-1 + * if something is ready, then c=[0x00 to 0xff] byte value. + * + * @return 0 on success, or -1 on failure. + */ +int metal_tty_getc(int *c); + +#endif diff --git a/arch/riscv32/fe310/src/freedom-metal/metal/uart.h b/arch/riscv32/fe310/src/freedom-metal/metal/uart.h new file mode 100644 index 00000000..856970ac --- /dev/null +++ b/arch/riscv32/fe310/src/freedom-metal/metal/uart.h @@ -0,0 +1,215 @@ +/* Copyright 2018 SiFive, Inc */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#ifndef METAL__UART_H +#define METAL__UART_H + +/*! + * @file uart.h + * @brief API for UART serial ports + */ + +#include + +struct metal_uart; +#undef getc +#undef putc +struct metal_uart_vtable { + void (*init)(struct metal_uart *uart, int baud_rate); + int (*putc)(struct metal_uart *uart, int c); + int (*txready)(struct metal_uart *uart); + int (*getc)(struct metal_uart *uart, int *c); + int (*get_baud_rate)(struct metal_uart *uart); + int (*set_baud_rate)(struct metal_uart *uart, int baud_rate); + struct metal_interrupt *(*controller_interrupt)(struct metal_uart *uart); + int (*get_interrupt_id)(struct metal_uart *uart); + int (*tx_interrupt_enable)(struct metal_uart *uart); + int (*tx_interrupt_disable)(struct metal_uart *uart); + int (*rx_interrupt_enable)(struct metal_uart *uart); + int (*rx_interrupt_disable)(struct metal_uart *uart); + int (*set_tx_watermark)(struct metal_uart *uart, size_t length); + size_t (*get_tx_watermark)(struct metal_uart *uart); + int (*set_rx_watermark)(struct metal_uart *uart, size_t length); + size_t (*get_rx_watermark)(struct metal_uart *uart); +}; + +/*! + * @brief Handle for a UART serial device + */ +struct metal_uart { + const struct metal_uart_vtable *vtable; +}; + +/*! @brief Get a handle for a UART device + * @param device_num The index of the desired UART device + * @return A handle to the UART device, or NULL if the device does not exist*/ +struct metal_uart *metal_uart_get_device(unsigned int device_num); + +/*! + * @brief Initialize UART device + + * Initialize the UART device described by the UART handle. This function must + be called before any + * other method on the UART can be invoked. It is invalid to initialize a UART + more than once. + * + * @param uart The UART device handle + * @param baud_rate the baud rate to set the UART to + */ +__inline__ void metal_uart_init(struct metal_uart *uart, int baud_rate) { + uart->vtable->init(uart, baud_rate); +} + +/*! + * @brief Output a character over the UART + * @param uart The UART device handle + * @param c The character to send over the UART + * @return 0 upon success + */ +__inline__ int metal_uart_putc(struct metal_uart *uart, int c) { + return uart->vtable->putc(uart, c); +} + +/*! + * @brief Test, determine if tx output is blocked(full/busy) + * @param uart The UART device handle + * @return 0 not blocked + */ +__inline__ int metal_uart_txready(struct metal_uart *uart) { + return uart->vtable->txready(uart); +} + +/*! + * @brief Read a character sent over the UART + * @param uart The UART device handle + * @param c The varible to hold the read character + * @return 0 upon success + * + * If "c == -1" no char was ready. + * If "c != -1" then C == byte value (0x00 to 0xff) + */ +__inline__ int metal_uart_getc(struct metal_uart *uart, int *c) { + return uart->vtable->getc(uart, c); +} + +/*! + * @brief Get the baud rate of the UART peripheral + * @param uart The UART device handle + * @return The current baud rate of the UART + */ +__inline__ int metal_uart_get_baud_rate(struct metal_uart *uart) { + return uart->vtable->get_baud_rate(uart); +} + +/*! + * @brief Set the baud rate of the UART peripheral + * @param uart The UART device handle + * @param baud_rate The baud rate to configure + * @return the new baud rate of the UART + */ +__inline__ int metal_uart_set_baud_rate(struct metal_uart *uart, + int baud_rate) { + return uart->vtable->set_baud_rate(uart, baud_rate); +} + +/*! + * @brief Get the interrupt controller of the UART peripheral + * + * Get the interrupt controller for the UART peripheral. The interrupt + * controller must be initialized before any interrupts can be registered + * or enabled with it. + * + * @param uart The UART device handle + * @return The handle for the UART interrupt controller + */ +__inline__ struct metal_interrupt * +metal_uart_interrupt_controller(struct metal_uart *uart) { + return uart->vtable->controller_interrupt(uart); +} + +/*! + * @brief Get the interrupt ID of the UART controller + * @param uart The UART device handle + * @return The UART interrupt id + */ +__inline__ int metal_uart_get_interrupt_id(struct metal_uart *uart) { + return uart->vtable->get_interrupt_id(uart); +} + +/*! + * @brief Enable the UART transmit interrupt + * @param uart The UART device handle + * @return 0 upon success + */ +__inline__ int metal_uart_transmit_interrupt_enable(struct metal_uart *uart) { + return uart->vtable->tx_interrupt_enable(uart); +} + +/*! + * @brief Disable the UART transmit interrupt + * @param uart The UART device handle + * @return 0 upon success + */ +__inline__ int metal_uart_transmit_interrupt_disable(struct metal_uart *uart) { + return uart->vtable->tx_interrupt_disable(uart); +} + +/*! + * @brief Enable the UART receive interrupt + * @param uart The UART device handle + * @return 0 upon success + */ +__inline__ int metal_uart_receive_interrupt_enable(struct metal_uart *uart) { + return uart->vtable->rx_interrupt_enable(uart); +} + +/*! + * @brief Disable the UART receive interrupt + * @param uart The UART device handle + * @return 0 upon success + */ +__inline__ int metal_uart_receive_interrupt_disable(struct metal_uart *uart) { + return uart->vtable->rx_interrupt_disable(uart); +} + +/*! + * @brief Set the transmit watermark level of the UART controller + * @param uart The UART device handle + * @param level The UART transmit watermark level + * @return 0 upon success + */ +__inline__ int metal_uart_set_transmit_watermark(struct metal_uart *uart, + size_t level) { + return uart->vtable->set_tx_watermark(uart, level); +} + +/*! + * @brief Get the transmit watermark level of the UART controller + * @param uart The UART device handle + * @return The UART transmit watermark level + */ +__inline__ size_t metal_uart_get_transmit_watermark(struct metal_uart *uart) { + return uart->vtable->get_tx_watermark(uart); +} + +/*! + * @brief Set the receive watermark level of the UART controller + * @param uart The UART device handle + * @param level The UART transmit watermark level + * @return 0 upon success + */ +__inline__ int metal_uart_set_receive_watermark(struct metal_uart *uart, + size_t level) { + return uart->vtable->set_rx_watermark(uart, level); +} + +/*! + * @brief Get the receive watermark level of the UART controller + * @param uart The UART device handle + * @return The UART transmit watermark level + */ +__inline__ size_t metal_uart_get_receive_watermark(struct metal_uart *uart) { + return uart->vtable->get_rx_watermark(uart); +} + +#endif diff --git a/arch/riscv32/fe310/src/freedom-metal/metal/watchdog.h b/arch/riscv32/fe310/src/freedom-metal/metal/watchdog.h new file mode 100644 index 00000000..2f84d3b4 --- /dev/null +++ b/arch/riscv32/fe310/src/freedom-metal/metal/watchdog.h @@ -0,0 +1,168 @@ +/* Copyright 2019 SiFive, Inc */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#ifndef METAL__WATCHDOG_H +#define METAL__WATCHDOG_H + +/*! + * @file watchdog.h + * + * @brief API for configuring watchdog timers + */ + +#include + +struct metal_watchdog; + +/*! + * @brief List of watchdog timer count behaviors + */ +enum metal_watchdog_run_option { + METAL_WATCHDOG_STOP = 0, /*!< Stop the watchdog */ + METAL_WATCHDOG_RUN_ALWAYS, /*!< Run the watchdog continuously, even during + sleep */ + METAL_WATCHDOG_RUN_AWAKE, /*!< Run the watchdog only while the CPU is awake + */ +}; + +/*! + * @brief List of behaviors when a watchdog triggers + */ +enum metal_watchdog_result { + METAL_WATCHDOG_NO_RESULT = 0, /*!< When the watchdog triggers, do nothing */ + METAL_WATCHDOG_INTERRUPT, /*!< When the watchdog triggers, fire an interrupt + */ + METAL_WATCHDOG_FULL_RESET, /*!< When the watchdog triggers, cause a full + system reset */ +}; + +struct metal_watchdog_vtable { + int (*feed)(const struct metal_watchdog *const wdog); + long int (*get_rate)(const struct metal_watchdog *const wdog); + long int (*set_rate)(const struct metal_watchdog *const wdog, + const long int rate); + long int (*get_timeout)(const struct metal_watchdog *const wdog); + long int (*set_timeout)(const struct metal_watchdog *const wdog, + const long int timeout); + int (*set_result)(const struct metal_watchdog *const wdog, + const enum metal_watchdog_result result); + int (*run)(const struct metal_watchdog *const wdog, + const enum metal_watchdog_run_option option); + struct metal_interrupt *(*get_interrupt)( + const struct metal_watchdog *const wdog); + int (*get_interrupt_id)(const struct metal_watchdog *const wdog); + int (*clear_interrupt)(const struct metal_watchdog *const wdog); +}; + +/*! + * @brief Handle for a Watchdog Timer + */ +struct metal_watchdog { + const struct metal_watchdog_vtable *vtable; +}; + +/*! + * @brief Feed the watchdog timer + */ +inline int metal_watchdog_feed(const struct metal_watchdog *const wdog) { + return wdog->vtable->feed(wdog); +} + +/*! + * @brief Get the rate of the watchdog timer in Hz + * + * @return the rate of the watchdog timer + */ +inline long int +metal_watchdog_get_rate(const struct metal_watchdog *const wdog) { + return wdog->vtable->get_rate(wdog); +} + +/*! + * @brief Set the rate of the watchdog timer in Hz + * + * There is no guarantee that the new rate will match the requested rate. + * + * @return the new rate of the watchdog timer + */ +inline long int metal_watchdog_set_rate(const struct metal_watchdog *const wdog, + const long int rate) { + return wdog->vtable->set_rate(wdog, rate); +} + +/*! + * @brief Get the timeout of the watchdog timer + * + * @return the watchdog timeout value + */ +inline long int +metal_watchdog_get_timeout(const struct metal_watchdog *const wdog) { + return wdog->vtable->get_timeout(wdog); +} + +/*! + * @brief Set the timeout of the watchdog timer + * + * The set rate will be the minimimum of the requested and maximum supported + * rates. + * + * @return the new watchdog timeout value + */ +inline long int +metal_watchdog_set_timeout(const struct metal_watchdog *const wdog, + const long int timeout) { + return wdog->vtable->set_timeout(wdog, timeout); +} + +/*! + * @brief Sets the result behavior of a watchdog timer timeout + * + * @return 0 if the requested result behavior is supported + */ +inline int metal_watchdog_set_result(const struct metal_watchdog *const wdog, + const enum metal_watchdog_result result) { + return wdog->vtable->set_result(wdog, result); +} + +/*! + * @brief Set the run behavior of the watchdog + * + * Used to enable/disable the watchdog timer + * + * @return 0 if the watchdog was successfully started/stopped + */ +inline int metal_watchdog_run(const struct metal_watchdog *const wdog, + const enum metal_watchdog_run_option option) { + return wdog->vtable->run(wdog, option); +} + +/*! + * @brief Get the interrupt controller for the watchdog interrupt + */ +inline struct metal_interrupt * +metal_watchdog_get_interrupt(const struct metal_watchdog *const wdog) { + return wdog->vtable->get_interrupt(wdog); +} + +/*! + * @Brief Get the interrupt id for the watchdog interrupt + */ +inline int +metal_watchdog_get_interrupt_id(const struct metal_watchdog *const wdog) { + return wdog->vtable->get_interrupt_id(wdog); +} + +/*! + * @brief Clear the watchdog interrupt + */ +inline int +metal_watchdog_clear_interrupt(const struct metal_watchdog *const wdog) { + return wdog->vtable->clear_interrupt(wdog); +} + +/*! + * @brief Get a watchdog handle + */ +struct metal_watchdog *metal_watchdog_get_device(const int index); + +#endif /* METAL__WATCHDOG_H */ diff --git a/arch/riscv32/fe310/src/freedom-metal/src/atomic.c b/arch/riscv32/fe310/src/freedom-metal/src/atomic.c new file mode 100644 index 00000000..326568e3 --- /dev/null +++ b/arch/riscv32/fe310/src/freedom-metal/src/atomic.c @@ -0,0 +1,19 @@ +/* Copyright 2019 SiFive, Inc */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include + +extern __inline__ int32_t metal_atomic_available(void); +extern __inline__ int32_t metal_atomic_add(metal_atomic_t *a, + int32_t increment); +extern __inline__ int32_t metal_atomic_and(metal_atomic_t *a, int32_t mask); +extern __inline__ int32_t metal_atomic_or(metal_atomic_t *a, int32_t mask); +extern __inline__ int32_t metal_atomic_swap(metal_atomic_t *a, + int32_t new_value); +extern __inline__ int32_t metal_atomic_xor(metal_atomic_t *a, int32_t mask); +extern __inline__ int32_t metal_atomic_max(metal_atomic_t *a, int32_t compare); +extern __inline__ uint32_t metal_atomic_max_u(metal_atomic_t *a, + uint32_t compare); +extern __inline__ int32_t metal_atomic_min(metal_atomic_t *a, int32_t compare); +extern __inline__ uint32_t metal_atomic_min_u(metal_atomic_t *a, + uint32_t compare); diff --git a/arch/riscv32/fe310/src/freedom-metal/src/button.c b/arch/riscv32/fe310/src/freedom-metal/src/button.c new file mode 100644 index 00000000..5831945c --- /dev/null +++ b/arch/riscv32/fe310/src/freedom-metal/src/button.c @@ -0,0 +1,27 @@ +/* Copyright 2018 SiFive, Inc */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include +#include + +struct metal_button *metal_button_get(char *label) { + int i; + struct metal_button *button; + + if ((__METAL_DT_MAX_BUTTONS == 0) || (label == NULL)) { + return NULL; + } + + for (i = 0; i < __METAL_DT_MAX_BUTTONS; i++) { + button = (struct metal_button *)__metal_button_table[i]; + if (button->vtable->button_exist(button, label)) { + return button; + } + } + return NULL; +} + +extern __inline__ struct metal_interrupt * +metal_button_interrupt_controller(struct metal_button *button); +extern __inline__ int +metal_button_get_interrupt_id(struct metal_button *button); diff --git a/arch/riscv32/fe310/src/freedom-metal/src/cache.c b/arch/riscv32/fe310/src/freedom-metal/src/cache.c new file mode 100644 index 00000000..6a45036b --- /dev/null +++ b/arch/riscv32/fe310/src/freedom-metal/src/cache.c @@ -0,0 +1,234 @@ +/* Copyright 2020 SiFive, Inc */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include +#include + +/* Macros to generate driver prefix string */ +#ifdef METAL_CACHE_DRIVER_PREFIX +#define METAL_CACHE_FUNC_STR(a, b) a##_##b +#define METAL_CACHE_FUNC_STR_(a, b) METAL_CACHE_FUNC_STR(a, b) +#define METAL_CACHE_FUNC(x) METAL_CACHE_FUNC_STR_(METAL_CACHE_DRIVER_PREFIX, x) +#endif + +#ifdef METAL_PL2CACHE_DRIVER_PREFIX +#define METAL_PL2CACHE_FUNC_STR(a, b) a##_##b +#define METAL_PL2CACHE_FUNC_STR_(a, b) METAL_PL2CACHE_FUNC_STR(a, b) +#define METAL_PL2CACHE_FUNC(x) \ + METAL_PL2CACHE_FUNC_STR_(METAL_PL2CACHE_DRIVER_PREFIX, x) +#endif + +extern __inline__ void metal_cache_init(struct metal_cache *cache, int ways); +extern __inline__ int metal_cache_get_enabled_ways(struct metal_cache *cache); +extern __inline__ int metal_cache_set_enabled_ways(struct metal_cache *cache, + int ways); + +int metal_l2cache_init(void) { +#if defined(METAL_PL2CACHE_DRIVER_PREFIX) + return 0; +#elif defined(METAL_CACHE_DRIVER_PREFIX) + return METAL_CACHE_FUNC(init)(); +#else + return -1; +#endif +} + +int metal_l2cache_get_enabled_ways(void) { +#if defined(METAL_PL2CACHE_DRIVER_PREFIX) + return METAL_PL2CACHE_FUNC(get_enabled_ways)(); +#elif defined(METAL_CACHE_DRIVER_PREFIX) + return METAL_CACHE_FUNC(get_enabled_ways)(); +#else + return -1; +#endif +} + +int metal_l2cache_set_enabled_ways(int ways) { +#if defined(METAL_PL2CACHE_DRIVER_PREFIX) + return METAL_PL2CACHE_FUNC(set_enabled_ways)(ways); +#elif defined(METAL_CACHE_DRIVER_PREFIX) + return METAL_CACHE_FUNC(set_enabled_ways)(ways); +#else + return -1; +#endif +} + +int metal_dcache_l1_available(int hartid) { + switch (hartid) { + case 0: +#ifdef __METAL_CPU_0_DCACHE_HANDLE + return __METAL_CPU_0_DCACHE_HANDLE; +#endif + break; + case 1: +#ifdef __METAL_CPU_1_DCACHE_HANDLE + return __METAL_CPU_1_DCACHE_HANDLE; +#endif + break; + case 2: +#ifdef __METAL_CPU_2_DCACHE_HANDLE + return __METAL_CPU_2_DCACHE_HANDLE; +#endif + break; + case 3: +#ifdef __METAL_CPU_3_DCACHE_HANDLE + return __METAL_CPU_3_DCACHE_HANDLE; +#endif + break; + case 4: +#ifdef __METAL_CPU_4_DCACHE_HANDLE + return __METAL_CPU_4_DCACHE_HANDLE; +#endif + break; + case 5: +#ifdef __METAL_CPU_5_DCACHE_HANDLE + return __METAL_CPU_5_DCACHE_HANDLE; +#endif + break; + case 6: +#ifdef __METAL_CPU_6_DCACHE_HANDLE + return __METAL_CPU_6_DCACHE_HANDLE; +#endif + break; + case 7: +#ifdef __METAL_CPU_7_DCACHE_HANDLE + return __METAL_CPU_7_DCACHE_HANDLE; +#endif + break; + case 8: +#ifdef __METAL_CPU_8_DCACHE_HANDLE + return __METAL_CPU_8_DCACHE_HANDLE; +#endif + break; + } + return 0; +} + +int metal_icache_l1_available(int hartid) { + switch (hartid) { + case 0: +#ifdef __METAL_CPU_0_ICACHE_HANDLE + return __METAL_CPU_0_ICACHE_HANDLE; +#endif + break; + case 1: +#ifdef __METAL_CPU_1_ICACHE_HANDLE + return __METAL_CPU_1_ICACHE_HANDLE; +#endif + break; + case 2: +#ifdef __METAL_CPU_2_ICACHE_HANDLE + return __METAL_CPU_2_ICACHE_HANDLE; +#endif + break; + case 3: +#ifdef __METAL_CPU_3_ICACHE_HANDLE + return __METAL_CPU_3_ICACHE_HANDLE; +#endif + break; + case 4: +#ifdef __METAL_CPU_4_ICACHE_HANDLE + return __METAL_CPU_4_ICACHE_HANDLE; +#endif + break; + case 5: +#ifdef __METAL_CPU_5_ICACHE_HANDLE + return __METAL_CPU_5_ICACHE_HANDLE; +#endif + break; + case 6: +#ifdef __METAL_CPU_6_ICACHE_HANDLE + return __METAL_CPU_6_ICACHE_HANDLE; +#endif + break; + case 7: +#ifdef __METAL_CPU_7_ICACHE_HANDLE + return __METAL_CPU_7_ICACHE_HANDLE; +#endif + break; + case 8: +#ifdef __METAL_CPU_8_ICACHE_HANDLE + return __METAL_CPU_8_ICACHE_HANDLE; +#endif + break; + } + return 0; +} + +/*! + * @brief CFlush.D.L1 instruction is a custom instruction implemented as a + * state machine in L1 Data Cache (D$) with funct3=0, (for core with data + * caches) It is an I type: .insn i opcode, func3, rd, rs1, simm12(signed + * immediate 12bs) + * 31 28 27 24 23 20 19 16 15 12 11 8 7 4 3 0 + * |--------|--------|--------|--------|--------|--------|--------|--------| + * +-------------+------------+----------+------+--------+-----------------+ + * |sign immediate12b (simm12)| rs1 | func3| rd | opcode | + * |-1-1-1-1 -1-1-0-0 -0-0-0-0|-x-x-x-x-x|0-0-0-|-0-0-0-0|-0-1-1-1 -0-0-1-1| + * +--------------------------+----------+------+--------+-----------------+ + * 31 -0x40 20 15 0 12 x0 7 0x73 0 + * +--------+--------+--------+----------+------+--------+--------+--------+ + * where, + * rs1 = x0, CFLUSH.D.L1 writes back and invalidates all lines in the L1 D$ + * rs1 != x0, CFLUSH.D.L1 writes back and invalidates the L1 D$ line containing + * the virtual address in integer register rs1. + */ +void metal_dcache_l1_flush(int hartid, uintptr_t address) { + if (metal_dcache_l1_available(hartid)) { + if (address) { + uintptr_t ms1 = 0, ms2 = 0; + __asm__ __volatile__("csrr %0, mtvec \n\t" + "la %1, 1f \n\t" + "csrw mtvec, %1 \n\t" + ".insn i 0x73, 0, x0, %2, -0x40 \n\t" + ".align 2\n\t" + "1: \n\t" + "csrw mtvec, %0 \n\t" + : "+r"(ms1), "+r"(ms2) + : "r"(address)); + // Using ‘.insn’ pseudo directive: + // '.insn i opcode, func3, rd, rs1, simm12' + } else { + __asm__ __volatile__(".word 0xfc000073" : : : "memory"); + } + } +} + +/*! + * @brief CDiscard.D.L1 instruction is a custom instruction implemented as a + * state machine in L1 Data Cache (D$) with funct3=0, (for core with data + * caches) It is an I type: .insn i opcode, func3, rd, rs1, simm12(signed + * immediate 12bs) + * 31 28 27 24 23 20 19 16 15 12 11 8 7 4 3 0 + * |--------|--------|--------|--------|--------|--------|--------|--------| + * +-------------+------------+----------+------+--------+-----------------+ + * |sign immediate12b (simm12)| rs1 | func3| rd | opcode | + * |-1-1-1-1 -1-1-0-0 -0-0-1-0|-x-x-x-x-x|0-0-0-|-0-0-0-0|-0-1-1-1 -0-0-1-1| + * +--------------------------+----------+------+--------+-----------------+ + * 31 -0x3E 20 15 0 12 x0 7 0x73 0 + * +--------+--------+--------+----------+------+--------+--------+--------+ + * where, + * rs1 = x0, CDISCARD.D.L1 invalidates all lines in the L1 D$ with no writes + * back. rs1 != x0, CDISCARD.D.L1 invalidates the L1 D$ line containing the + * virtual address in integer register rs1, with no writes back. + */ +void metal_dcache_l1_discard(int hartid, uintptr_t address) { + if (metal_dcache_l1_available(hartid)) { + if (address) { + uintptr_t ms1 = 0, ms2 = 0; + __asm__ __volatile__("csrr %0, mtvec \n\t" + "la %1, 1f \n\t" + "csrw mtvec, %1 \n\t" + ".insn i 0x73, 0, x0, %2, -0x3E \n\t" + ".align 2\n\t" + "1: \n\t" + "csrw mtvec, %0 \n\t" + : "+r"(ms1), "+r"(ms2) + : "r"(address)); + // Using ‘.insn’ pseudo directive: + // '.insn i opcode, func3, rd, rs1, simm12' + } else { + __asm__ __volatile__(".word 0xfc200073" : : : "memory"); + } + } +} diff --git a/arch/riscv32/fe310/src/freedom-metal/src/clock.c b/arch/riscv32/fe310/src/freedom-metal/src/clock.c new file mode 100644 index 00000000..a2a11231 --- /dev/null +++ b/arch/riscv32/fe310/src/freedom-metal/src/clock.c @@ -0,0 +1,20 @@ +/* Copyright 2018 SiFive, Inc */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include + +extern __inline__ void +_metal_clock_call_all_callbacks(const metal_clock_callback *const list); +extern __inline__ metal_clock_callback * +_metal_clock_append_to_callbacks(metal_clock_callback *list, + metal_clock_callback *const cb); + +extern __inline__ long metal_clock_get_rate_hz(const struct metal_clock *clk); +extern __inline__ long metal_clock_set_rate_hz(struct metal_clock *clk, + long hz); +extern __inline__ void +metal_clock_register_post_rate_change_callback(struct metal_clock *clk, + metal_clock_callback *cb); +extern __inline__ void +metal_clock_register_pre_rate_change_callback(struct metal_clock *clk, + metal_clock_callback *cb); diff --git a/arch/riscv32/fe310/src/freedom-metal/src/cpu.c b/arch/riscv32/fe310/src/freedom-metal/src/cpu.c new file mode 100644 index 00000000..8e28c897 --- /dev/null +++ b/arch/riscv32/fe310/src/freedom-metal/src/cpu.c @@ -0,0 +1,69 @@ +/* Copyright 2018 SiFive, Inc */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include +#include + +struct metal_cpu *metal_cpu_get(unsigned int hartid) { + if (hartid < __METAL_DT_MAX_HARTS) { + return (struct metal_cpu *)__metal_cpu_table[hartid]; + } + return NULL; +} + +int metal_cpu_get_current_hartid() { +#ifdef __riscv + int mhartid; + __asm__ volatile("csrr %0, mhartid" : "=r"(mhartid)); + return mhartid; +#endif +} + +int metal_cpu_get_num_harts() { return __METAL_DT_MAX_HARTS; } + +extern __inline__ unsigned long long metal_cpu_get_timer(struct metal_cpu *cpu); + +extern __inline__ unsigned long long +metal_cpu_get_timebase(struct metal_cpu *cpu); + +extern __inline__ unsigned long long metal_cpu_get_mtime(struct metal_cpu *cpu); + +extern __inline__ int metal_cpu_set_mtimecmp(struct metal_cpu *cpu, + unsigned long long time); + +extern __inline__ struct metal_interrupt * +metal_cpu_timer_interrupt_controller(struct metal_cpu *cpu); + +extern __inline__ int metal_cpu_timer_get_interrupt_id(struct metal_cpu *cpu); + +extern __inline__ struct metal_interrupt * +metal_cpu_software_interrupt_controller(struct metal_cpu *cpu); + +extern __inline__ int +metal_cpu_software_get_interrupt_id(struct metal_cpu *cpu); + +extern __inline__ int metal_cpu_software_set_ipi(struct metal_cpu *cpu, + int hartid); + +extern __inline__ int metal_cpu_software_clear_ipi(struct metal_cpu *cpu, + int hartid); + +extern __inline__ int metal_cpu_get_msip(struct metal_cpu *cpu, int hartid); + +extern __inline__ struct metal_interrupt * +metal_cpu_interrupt_controller(struct metal_cpu *cpu); + +extern __inline__ int +metal_cpu_exception_register(struct metal_cpu *cpu, int ecode, + metal_exception_handler_t handler); + +extern __inline__ int metal_cpu_get_instruction_length(struct metal_cpu *cpu, + uintptr_t epc); + +extern __inline__ uintptr_t metal_cpu_get_exception_pc(struct metal_cpu *cpu); + +extern __inline__ int metal_cpu_set_exception_pc(struct metal_cpu *cpu, + uintptr_t epc); + +extern __inline__ struct metal_buserror * +metal_cpu_get_buserror(struct metal_cpu *cpu); diff --git a/arch/riscv32/fe310/src/freedom-metal/src/crt0.S b/arch/riscv32/fe310/src/freedom-metal/src/crt0.S new file mode 100644 index 00000000..62c84f61 --- /dev/null +++ b/arch/riscv32/fe310/src/freedom-metal/src/crt0.S @@ -0,0 +1,351 @@ +/* Copyright (c) 2017-2018 SiFive Inc. All rights reserved. + + This copyrighted material is made available to anyone wishing to use, + modify, copy, or redistribute it subject to the terms and conditions + of the FreeBSD License. This program is distributed in the hope that + it will be useful, but WITHOUT ANY WARRANTY expressed or implied, + including the implied warranties of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. A copy of this license is available at + http://www.opensource.org/licenses. +*/ + +/* crt0.S: Entry point for RISC-V METAL programs. */ + +.section .text.libgloss.start +.global _start +.type _start, @function + + /* _start is defined by the METAL to have been called with the following + * arguments: + * a0: the hart ID of the currently executing hart. Harts can start at + * any arbitrary point, it's the C library's job to ensure the code is + * safe. + * a1: a pointer to a description of the machine on which this code is + * currently executing. This is probably 0 on an embedded system + * because they tend to not be dynamically portable. As such, newlib + * ignores this argument. + * a2: a pointer to a function that must be run after the envirnoment has + * been initialized, but before user code can be expected to be run. + * If this is 0 then there is no function to be run. */ +_start: +.cfi_startproc +.cfi_undefined ra + + /* This is a bit funky: it's not usually sane for _start to return, but in + * this case we actually want to in order to signal an error to the METAL. */ + mv s0, ra + + /* Before doing anything we must initialize the global pointer, as we cannot + * safely perform any access that may be relaxed without GP being set. This + * is done with relaxation disabled to avoid relaxing the address calculation + * to just "addi gp, gp, 0". */ +.option push +.option norelax +#ifdef __riscv_cmodel_compact +1: + auipc a0, %pcrel_hi(__global_pointer__) + addi a0, a0, %pcrel_lo(1b) + ld gp, 0(a0) + add gp, gp, a0 +#else + la gp, __global_pointer$ +#endif +.option pop + + /* Stack pointer is expected to be initialized before _start */ + + /* If we're not hart 0, skip the initialization work */ + la t0, __metal_boot_hart + bne a0, t0, _skip_init + + /* Embedded systems frequently require relocating the data segment before C + * code can be run -- for example, the data segment may exist in flash upon + * boot and then need to get relocated into a non-persistant writable memory + * before C code can execute. If this is the case we do so here. This step + * is optional: if the METAL provides an environment in which this relocation + * is not necessary then it must simply set metal_segment_data_source_start to + * be equal to metal_segment_data_target_start. */ +#ifdef __riscv_cmodel_compact + la.got.gprel t0, metal_segment_data_source_start + lla.gprel t1, metal_segment_data_target_start + lla.gprel t2, metal_segment_data_target_end +#else + la t0, metal_segment_data_source_start + la t1, metal_segment_data_target_start + la t2, metal_segment_data_target_end +#endif + + beq t0, t1, 2f + bge t1, t2, 2f + +1: +#if __riscv_xlen == 32 + lw a0, 0(t0) + addi t0, t0, 4 + sw a0, 0(t1) + addi t1, t1, 4 + blt t1, t2, 1b +#else + ld a0, 0(t0) + addi t0, t0, 8 + sd a0, 0(t1) + addi t1, t1, 8 + blt t1, t2, 1b +#endif +2: + + /* Copy the ITIM section */ +#ifdef __riscv_cmodel_compact + la.got.gprel t0, metal_segment_itim_source_start + la.got.gprel t1, metal_segment_itim_target_start + la.got.gprel t2, metal_segment_itim_target_end +#else + la t0, metal_segment_itim_source_start + la t1, metal_segment_itim_target_start + la t2, metal_segment_itim_target_end +#endif + + beq t0, t1, 2f + bge t1, t2, 2f + +1: +#if __riscv_xlen == 32 + lw a0, 0(t0) + addi t0, t0, 4 + sw a0, 0(t1) + addi t1, t1, 4 + blt t1, t2, 1b +#else + ld a0, 0(t0) + addi t0, t0, 8 + sd a0, 0(t1) + addi t1, t1, 8 + blt t1, t2, 1b +#endif +2: + + /* Fence all subsequent instruction fetches until after the ITIM writes + complete */ + fence.i + +2: + + /* Copy the LIM section */ +#ifdef __riscv_cmodel_compact + la.got.gprel t0, metal_segment_lim_source_start + lla.gprel t1, metal_segment_lim_target_start + lla.gprel t2, metal_segment_lim_target_end +#else + la t0, metal_segment_lim_source_start + la t1, metal_segment_lim_target_start + la t2, metal_segment_lim_target_end +#endif + + beq t0, t1, 2f + bge t1, t2, 2f + +1: +#if __riscv_xlen == 32 + lw a0, 0(t0) + addi t0, t0, 4 + sw a0, 0(t1) + addi t1, t1, 4 + blt t1, t2, 1b +#else + ld a0, 0(t0) + addi t0, t0, 8 + sd a0, 0(t1) + addi t1, t1, 8 + blt t1, t2, 1b +#endif +2: + + /* Fence all subsequent instruction fetches until after the LIM writes + complete */ + fence.i + + /* Zero the BSS segment. */ +#ifdef __riscv_cmodel_compact + lla.gprel t1, metal_segment_bss_target_start + lla.gprel t2, metal_segment_bss_target_end +#else + la t1, metal_segment_bss_target_start + la t2, metal_segment_bss_target_end +#endif + + bge t1, t2, 2f + +1: +#if __riscv_xlen == 32 + sw x0, 0(t1) + addi t1, t1, 4 + blt t1, t2, 1b +#else + sd x0, 0(t1) + addi t1, t1, 8 + blt t1, t2, 1b +#endif +2: + + /* Set TLS pointer */ + .weak __tls_base +#ifdef __riscv_cmodel_compact + lla.gprel tp, __tls_base +#else + la tp, __tls_base +#endif + + /* At this point we're in an environment that can execute C code. The first + * thing to do is to make the callback to the parent environment if it's been + * requested to do so. */ + beqz a2, 1f + jalr a2 +1: + + /* The RISC-V port only uses new-style constructors and destructors. */ + la a0, __libc_fini_array + call atexit + call __libc_init_array + + /* Register metal_fini_run as a destructor and call metal_init_run to + * run and setup Metal constructors */ + la a0, metal_fini_run + call atexit + call metal_init_run + +_skip_init: + + /* Synchronize harts so that secondary harts wait until hart 0 finishes + initializing */ + call __metal_synchronize_harts + + /* Disable and clear all interrupt sources */ + li a3, -1 + csrc mie, a3 + csrc mip, a3 + + /* The delegation CSRs exist if user mode interrupts (N extension) or + * supervisor mode (S extension) are supported */ + csrr a5, misa + lui a4, 0x42 + and a4, a4, a5 + beqz a4, 1f + csrc mideleg, a3 + csrc medeleg, a3 +1: + + /* The satp CSR exists if supervisor mode (S extension) is supported */ + lui a4, 0x40 + and a4, a4, a5 + beqz a4, 1f + csrc satp, a3 +1: + + /* Check RISC-V isa and enable FS bits if Floating Point architecture. */ + li a4, 0x10028 + and a5, a5, a4 + beqz a5, 1f + csrr a5, mstatus + lui a4, 0x2 + or a5, a5, a4 + csrw mstatus, a5 + csrwi fcsr, 0 +1: + + /* Check for vector extension support and enable it if found. + * Omit if toolchain doesn't support the vector extension. */ +#ifdef __riscv_v + csrr a5, misa + li a4, 0x200000 + and a5, a5, a4 + beqz a5, 1f + csrr a5, mstatus + ori a5, a5, 0x200 + csrw mstatus, a5 + vsetivli x0, 0, e8, m1, ta, ma + csrwi vcsr, 0 +1: +#endif + + /* This is a C runtime, so main() is defined to have some arguments. Since + * there's nothing sane the METAL can pass we don't bother with that but + * instead just setup as close to a NOP as we can. */ + li a0, 1 /* argc=1 */ + la a1, argv /* argv = {"libgloss", NULL} */ + la a2, envp /* envp = {NULL} */ + call secondary_main + + /* Call exit to handle libc's cleanup routines. Under normal contains this + * shouldn't even get called, but I'm still not using a tail call here + * because returning to the METAL is the right thing to do in pathological + * situations. */ + call exit + + /* And here's where we return. Again, it's a bit odd but the METAL defines + * this as a bad idea (ie, as opposed to leaving it undefined) and at this + * point it's really the only thing left to do. */ + mv ra, s0 + ret + +.cfi_endproc + +/* RISC-V systems always use __libc_{init,fini}_array, but for compatibility we + * define _{init,fini} to do nothing. */ +.global _init +.type _init, @function +.global _fini +.type _fini, @function +_init: +_fini: + ret +.size _init, .-_init +.size _fini, .-_fini + +/* By default, secondary_main will cause secondary harts to spin forever. + * Users can redefine secondary_main themselves to run code on secondary harts */ +.weak secondary_main +.type secondary_main, @function + +secondary_main: + addi sp, sp, -16 +#if __riscv_xlen == 32 + sw ra, 4(sp) +#else + sd ra, 8(sp) +#endif + csrr t0, mhartid + la t1, __metal_boot_hart + beq t0, t1, 2f +1: + wfi + j 1b +2: + call main +#if __riscv_xlen == 32 + lw ra, 4(sp) +#else + ld ra, 8(sp) +#endif + addi sp, sp, 16 + ret + +/* This shim allows main() to be passed a set of arguments that can satisfy the + * requirements of the C API. */ +.section .rodata.libgloss.start +.balign 8 +argv: +.dc.a name +envp: +.dc.a 0 +name: +.asciz "libgloss" + +#ifdef __riscv_cmodel_compact +/* Compact stub. */ + .section .text.__global_pointer__, "aMG",@progbits, 8, __global_pointer__, comdat + .align 3 + .global __global_pointer__ + .type __global_pointer__, object +__global_pointer__: + .quad __global_pointer$ -. +#endif diff --git a/arch/riscv32/fe310/src/freedom-metal/src/drivers/fixed-clock.c b/arch/riscv32/fe310/src/freedom-metal/src/drivers/fixed-clock.c new file mode 100644 index 00000000..39519707 --- /dev/null +++ b/arch/riscv32/fe310/src/freedom-metal/src/drivers/fixed-clock.c @@ -0,0 +1,28 @@ +/* Copyright 2018 SiFive, Inc. */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include + +#ifdef METAL_FIXED_CLOCK + +#include +#include +#include + +long __metal_driver_fixed_clock_get_rate_hz(const struct metal_clock *gclk) { + return __metal_driver_fixed_clock_rate(gclk); +} + +long __metal_driver_fixed_clock_set_rate_hz(struct metal_clock *gclk, + long target_hz) { + return __metal_driver_fixed_clock_get_rate_hz(gclk); +} + +__METAL_DEFINE_VTABLE(__metal_driver_vtable_fixed_clock) = { + .clock.get_rate_hz = __metal_driver_fixed_clock_get_rate_hz, + .clock.set_rate_hz = __metal_driver_fixed_clock_set_rate_hz, +}; + +#endif /* METAL_FIXED_CLOCK */ + +typedef int no_empty_translation_units; diff --git a/arch/riscv32/fe310/src/freedom-metal/src/drivers/fixed-factor-clock.c b/arch/riscv32/fe310/src/freedom-metal/src/drivers/fixed-factor-clock.c new file mode 100644 index 00000000..4e8223bf --- /dev/null +++ b/arch/riscv32/fe310/src/freedom-metal/src/drivers/fixed-factor-clock.c @@ -0,0 +1,35 @@ +/* Copyright 2018 SiFive, Inc. */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include + +#ifdef METAL_FIXED_FACTOR_CLOCK + +#include +#include +#include + +long __metal_driver_fixed_factor_clock_get_rate_hz( + const struct metal_clock *gclk) { + struct metal_clock *parent = __metal_driver_fixed_factor_clock_parent(gclk); + long parent_rate = 1; + if (parent) { + parent_rate = parent->vtable->get_rate_hz(parent); + } + + return __metal_driver_fixed_factor_clock_mult(gclk) * parent_rate / + __metal_driver_fixed_factor_clock_div(gclk); +} + +long __metal_driver_fixed_factor_clock_set_rate_hz(struct metal_clock *gclk, + long target_hz) { + return __metal_driver_fixed_factor_clock_get_rate_hz(gclk); +} + +__METAL_DEFINE_VTABLE(__metal_driver_vtable_fixed_factor_clock) = { + .clock.get_rate_hz = __metal_driver_fixed_factor_clock_get_rate_hz, + .clock.set_rate_hz = __metal_driver_fixed_factor_clock_set_rate_hz, +}; +#endif /* METAL_FIXED_FACTOR_CLOCK */ + +typedef int no_empty_translation_units; diff --git a/arch/riscv32/fe310/src/freedom-metal/src/drivers/inline.c b/arch/riscv32/fe310/src/freedom-metal/src/drivers/inline.c new file mode 100644 index 00000000..a87c056b --- /dev/null +++ b/arch/riscv32/fe310/src/freedom-metal/src/drivers/inline.c @@ -0,0 +1,4 @@ +/* Copyright 2019 SiFive, Inc */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include diff --git a/arch/riscv32/fe310/src/freedom-metal/src/drivers/riscv_clint0.c b/arch/riscv32/fe310/src/freedom-metal/src/drivers/riscv_clint0.c new file mode 100644 index 00000000..3dd7c0eb --- /dev/null +++ b/arch/riscv32/fe310/src/freedom-metal/src/drivers/riscv_clint0.c @@ -0,0 +1,302 @@ +/* Copyright 2018 SiFive, Inc */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include + +#ifdef METAL_RISCV_CLINT0 + +#include +#include +#include +#include + +unsigned long long +__metal_clint0_mtime_get(struct __metal_driver_riscv_clint0 *clint) { + __metal_io_u32 lo, hi; + unsigned long control_base = + __metal_driver_sifive_clint0_control_base(&clint->controller); + + /* Guard against rollover when reading */ + do { + hi = __METAL_ACCESS_ONCE( + (__metal_io_u32 *)(control_base + METAL_RISCV_CLINT0_MTIME + 4)); + lo = __METAL_ACCESS_ONCE( + (__metal_io_u32 *)(control_base + METAL_RISCV_CLINT0_MTIME)); + } while (__METAL_ACCESS_ONCE((__metal_io_u32 *)(control_base + + METAL_RISCV_CLINT0_MTIME + + 4)) != hi); + + return (((unsigned long long)hi) << 32) | lo; +} + +int __metal_driver_riscv_clint0_mtimecmp_set(struct metal_interrupt *controller, + int hartid, + unsigned long long time) { + struct __metal_driver_riscv_clint0 *clint = + (struct __metal_driver_riscv_clint0 *)(controller); + unsigned long control_base = + __metal_driver_sifive_clint0_control_base(&clint->controller); + /* Per spec, the RISC-V MTIME/MTIMECMP registers are 64 bit, + * and are NOT internally latched for multiword transfers. + * Need to be careful about sequencing to avoid triggering + * spurious interrupts: For that set the high word to a max + * value first. + */ + __METAL_ACCESS_ONCE((__metal_io_u32 *)(control_base + (8 * hartid) + + METAL_RISCV_CLINT0_MTIMECMP_BASE + + 4)) = 0xFFFFFFFF; + __METAL_ACCESS_ONCE((__metal_io_u32 *)(control_base + (8 * hartid) + + METAL_RISCV_CLINT0_MTIMECMP_BASE)) = + (__metal_io_u32)time; + __METAL_ACCESS_ONCE((__metal_io_u32 *)(control_base + (8 * hartid) + + METAL_RISCV_CLINT0_MTIMECMP_BASE + + 4)) = (__metal_io_u32)(time >> 32); + return 0; +} + +static struct metal_interrupt *_get_cpu_intc() { + int hartid = 0; + __asm__ volatile("csrr %[hartid], mhartid" + : [hartid] "=r"(hartid)::"memory"); + + struct metal_cpu *cpu = metal_cpu_get(hartid); + + return metal_cpu_interrupt_controller(cpu); +} + +void __metal_driver_riscv_clint0_init(struct metal_interrupt *controller) { + int num_interrupts = + __metal_driver_sifive_clint0_num_interrupts(controller); + struct __metal_driver_riscv_clint0 *clint = + (struct __metal_driver_riscv_clint0 *)(controller); + + if (!clint->init_done) { + /* Register its interrupts with with parent controller, aka sw and + * timerto its default isr */ + for (int i = 0; i < num_interrupts; i++) { + struct metal_interrupt *intc = + __metal_driver_sifive_clint0_interrupt_parents(controller, i); + int line = + __metal_driver_sifive_clint0_interrupt_lines(controller, i); + intc->vtable->interrupt_register(intc, line, NULL, controller); + } + clint->init_done = 1; + } +} + +int __metal_driver_riscv_clint0_register(struct metal_interrupt *controller, + int id, metal_interrupt_handler_t isr, + void *priv) { + int rc = -1; + metal_vector_mode mode = __metal_controller_interrupt_vector_mode(); + struct metal_interrupt *intc = NULL; + struct metal_interrupt *cpu_intc = _get_cpu_intc(); + int num_interrupts = + __metal_driver_sifive_clint0_num_interrupts(controller); + + if ((mode != METAL_VECTOR_MODE) && (mode != METAL_DIRECT_MODE)) { + return rc; + } + + for (int i = 0; i < num_interrupts; i++) { + int line = __metal_driver_sifive_clint0_interrupt_lines(controller, i); + intc = __metal_driver_sifive_clint0_interrupt_parents(controller, i); + if (cpu_intc == intc && id == line) { + break; + } + intc = NULL; + } + + /* Register its interrupts with parent controller */ + if (intc) { + rc = intc->vtable->interrupt_register(intc, id, isr, priv); + } + return rc; +} + +int __metal_driver_riscv_clint0_vector_register( + struct metal_interrupt *controller, int id, + metal_interrupt_vector_handler_t isr, void *priv) { + /* Not supported. User can override the 'weak' handler with their own */ + int rc = -1; + return rc; +} + +metal_vector_mode __metal_driver_riscv_clint0_get_vector_mode( + struct metal_interrupt *controller) { + return __metal_controller_interrupt_vector_mode(); +} + +int __metal_driver_riscv_clint0_set_vector_mode( + struct metal_interrupt *controller, metal_vector_mode mode) { + int rc = -1; + struct metal_interrupt *intc = _get_cpu_intc(); + + if (intc) { + /* Valid vector modes are VECTOR and DIRECT, anything else is invalid + * (-1) */ + switch (mode) { + case METAL_VECTOR_MODE: + case METAL_DIRECT_MODE: + rc = intc->vtable->interrupt_set_vector_mode(intc, mode); + break; + default: + break; + } + } + return rc; +} + +int __metal_driver_riscv_clint0_enable(struct metal_interrupt *controller, + int id) { + int rc = -1; + + if (id) { + struct metal_interrupt *intc = NULL; + struct metal_interrupt *cpu_intc = _get_cpu_intc(); + int num_interrupts = + __metal_driver_sifive_clint0_num_interrupts(controller); + + for (int i = 0; i < num_interrupts; i++) { + int line = + __metal_driver_sifive_clint0_interrupt_lines(controller, i); + intc = + __metal_driver_sifive_clint0_interrupt_parents(controller, i); + if (cpu_intc == intc && id == line) { + break; + } + intc = NULL; + } + + /* Enable its interrupts with parent controller */ + if (intc) { + rc = intc->vtable->interrupt_enable(intc, id); + } + } + + return rc; +} + +int __metal_driver_riscv_clint0_disable(struct metal_interrupt *controller, + int id) { + int rc = -1; + + if (id) { + struct metal_interrupt *intc = NULL; + struct metal_interrupt *cpu_intc = _get_cpu_intc(); + int num_interrupts = + __metal_driver_sifive_clint0_num_interrupts(controller); + + for (int i = 0; i < num_interrupts; i++) { + int line = + __metal_driver_sifive_clint0_interrupt_lines(controller, i); + intc = + __metal_driver_sifive_clint0_interrupt_parents(controller, i); + if (cpu_intc == intc && id == line) { + break; + } + intc = NULL; + } + + /* Disable its interrupts with parent controller */ + if (intc) { + rc = intc->vtable->interrupt_disable(intc, id); + } + } + + return rc; +} + +int __metal_driver_riscv_clint0_command_request( + struct metal_interrupt *controller, int command, void *data) { + int hartid; + int rc = -1; + struct __metal_driver_riscv_clint0 *clint = + (struct __metal_driver_riscv_clint0 *)(controller); + unsigned long control_base = + __metal_driver_sifive_clint0_control_base(controller); + + switch (command) { + case METAL_TIMER_MTIME_GET: + if (data) { + *(unsigned long long *)data = __metal_clint0_mtime_get(clint); + rc = 0; + } + break; + case METAL_SOFTWARE_IPI_CLEAR: + if (data) { + hartid = *(int *)data; + __METAL_ACCESS_ONCE(( + __metal_io_u32 *)(control_base + (hartid * 4))) = METAL_DISABLE; + rc = 0; + } + break; + case METAL_SOFTWARE_IPI_SET: + if (data) { + hartid = *(int *)data; + __METAL_ACCESS_ONCE( + (__metal_io_u32 *)(control_base + (hartid * 4))) = METAL_ENABLE; + /* Callers of this function assume it's blocking, in the sense that + * the IPI is guarnteed to have been delivered before the function + * returns. We can't really guarnteed it's delivered, but we can + * read back the control register after writing it in at least an + * attempt to provide some semblence of ordering here. The fence + * ensures the read is order after the write -- it wouldn't be + * necessary under RVWMO because this is the same address, but we + * don't have an IO memory model so I'm being a bit overkill here. + */ + __METAL_IO_FENCE(o, i); + rc = __METAL_ACCESS_ONCE( + (__metal_io_u32 *)(control_base + (hartid * 4))); + rc = 0; + } + break; + case METAL_SOFTWARE_MSIP_GET: + rc = 0; + if (data) { + hartid = *(int *)data; + rc = __METAL_ACCESS_ONCE( + (__metal_io_u32 *)(control_base + (hartid * 4))); + } + break; + default: + break; + } + + return rc; +} + +int __metal_driver_riscv_clint0_clear_interrupt( + struct metal_interrupt *controller, int id) { + int hartid = metal_cpu_get_current_hartid(); + return __metal_driver_riscv_clint0_command_request( + controller, METAL_SOFTWARE_IPI_CLEAR, &hartid); +} + +int __metal_driver_riscv_clint0_set_interrupt( + struct metal_interrupt *controller, int id) { + int hartid = metal_cpu_get_current_hartid(); + return __metal_driver_riscv_clint0_command_request( + controller, METAL_SOFTWARE_IPI_SET, &hartid); +} + +__METAL_DEFINE_VTABLE(__metal_driver_vtable_riscv_clint0) = { + .clint_vtable.interrupt_init = __metal_driver_riscv_clint0_init, + .clint_vtable.interrupt_register = __metal_driver_riscv_clint0_register, + .clint_vtable.interrupt_vector_register = + __metal_driver_riscv_clint0_vector_register, + .clint_vtable.interrupt_enable = __metal_driver_riscv_clint0_enable, + .clint_vtable.interrupt_disable = __metal_driver_riscv_clint0_disable, + .clint_vtable.interrupt_get_vector_mode = + __metal_driver_riscv_clint0_get_vector_mode, + .clint_vtable.interrupt_set_vector_mode = + __metal_driver_riscv_clint0_set_vector_mode, + .clint_vtable.interrupt_clear = __metal_driver_riscv_clint0_clear_interrupt, + .clint_vtable.interrupt_set = __metal_driver_riscv_clint0_set_interrupt, + .clint_vtable.command_request = __metal_driver_riscv_clint0_command_request, + .clint_vtable.mtimecmp_set = __metal_driver_riscv_clint0_mtimecmp_set, +}; + +#endif /* METAL_RISCV_CLINT0 */ + +typedef int no_empty_translation_units; diff --git a/arch/riscv32/fe310/src/freedom-metal/src/drivers/riscv_cpu.c b/arch/riscv32/fe310/src/freedom-metal/src/drivers/riscv_cpu.c new file mode 100644 index 00000000..dda13b93 --- /dev/null +++ b/arch/riscv32/fe310/src/freedom-metal/src/drivers/riscv_cpu.c @@ -0,0 +1,876 @@ +/* Copyright 2018 SiFive, Inc */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include +#include +#include +#include + +#define __METAL_IRQ_VECTOR_HANDLER(id) \ + void *priv; \ + struct __metal_driver_riscv_cpu_intc *intc; \ + struct __metal_driver_cpu *cpu = __metal_cpu_table[__metal_myhart_id()]; \ + if (cpu) { \ + intc = (struct __metal_driver_riscv_cpu_intc *) \ + __metal_driver_cpu_interrupt_controller((struct metal_cpu *)cpu); \ + priv = intc->metal_int_table[id].exint_data; \ + intc->metal_int_table[id].handler(id, priv); \ + } + +extern void __metal_vector_table(); +unsigned long long __metal_driver_cpu_mtime_get(struct metal_cpu *cpu); +int __metal_driver_cpu_mtimecmp_set(struct metal_cpu *cpu, + unsigned long long time); + +struct metal_cpu *__metal_driver_cpu_get(int hartid) { + if (hartid < __METAL_DT_MAX_HARTS) { + return &(__metal_cpu_table[hartid]->cpu); + } + return (struct metal_cpu *)NULL; +} + +uintptr_t __metal_myhart_id(void) { + uintptr_t myhart; + __asm__ volatile("csrr %0, mhartid" : "=r"(myhart)); + return myhart; +} + +void __metal_zero_memory(unsigned char *base, unsigned int size) { + volatile unsigned char *ptr; + for (ptr = base; ptr < (base + size); ptr++) { + *ptr = 0; + } +} + +void __metal_interrupt_global_enable(void) { + uintptr_t m; + __asm__ volatile("csrrs %0, mstatus, %1" + : "=r"(m) + : "r"(METAL_MIE_INTERRUPT)); +} + +void __metal_interrupt_global_disable(void) { + uintptr_t m; + __asm__ volatile("csrrc %0, mstatus, %1" + : "=r"(m) + : "r"(METAL_MIE_INTERRUPT)); +} + +void __metal_interrupt_software_enable(void) { + uintptr_t m; + __asm__ volatile("csrrs %0, mie, %1" + : "=r"(m) + : "r"(METAL_LOCAL_INTERRUPT_SW)); +} + +void __metal_interrupt_software_disable(void) { + uintptr_t m; + __asm__ volatile("csrrc %0, mie, %1" + : "=r"(m) + : "r"(METAL_LOCAL_INTERRUPT_SW)); +} + +void __metal_interrupt_timer_enable(void) { + uintptr_t m; + __asm__ volatile("csrrs %0, mie, %1" + : "=r"(m) + : "r"(METAL_LOCAL_INTERRUPT_TMR)); +} + +void __metal_interrupt_timer_disable(void) { + uintptr_t m; + __asm__ volatile("csrrc %0, mie, %1" + : "=r"(m) + : "r"(METAL_LOCAL_INTERRUPT_TMR)); +} + +void __metal_interrupt_external_enable(void) { + uintptr_t m; + __asm__ volatile("csrrs %0, mie, %1" + : "=r"(m) + : "r"(METAL_LOCAL_INTERRUPT_EXT)); +} + +void __metal_interrupt_external_disable(void) { + unsigned long m; + __asm__ volatile("csrrc %0, mie, %1" + : "=r"(m) + : "r"(METAL_LOCAL_INTERRUPT_EXT)); +} + +void __metal_interrupt_local_enable(int id) { + uintptr_t b = 1 << id; + uintptr_t m; + __asm__ volatile("csrrs %0, mie, %1" : "=r"(m) : "r"(b)); +} + +void __metal_interrupt_local_disable(int id) { + uintptr_t b = 1 << id; + uintptr_t m; + __asm__ volatile("csrrc %0, mie, %1" : "=r"(m) : "r"(b)); +} + +void __metal_default_exception_handler(struct metal_cpu *cpu, int ecode) { + metal_shutdown(100); +} + +void __metal_default_interrupt_handler(int id, void *priv) { + metal_shutdown(200); +} + +/* The metal_interrupt_vector_handler() function can be redefined. */ +void __attribute__((weak, interrupt)) metal_interrupt_vector_handler(void) { + metal_shutdown(300); +} + +/* The metal_software_interrupt_vector_handler() function can be redefined. */ +void __attribute__((weak, interrupt)) +metal_software_interrupt_vector_handler(void) { + __METAL_IRQ_VECTOR_HANDLER(METAL_INTERRUPT_ID_SW); +} + +void __metal_default_sw_handler(int id, void *priv) { + uintptr_t mcause; + struct __metal_driver_riscv_cpu_intc *intc; + struct __metal_driver_cpu *cpu = __metal_cpu_table[__metal_myhart_id()]; + + __asm__ volatile("csrr %0, mcause" : "=r"(mcause)); + if (cpu) { + intc = (struct __metal_driver_riscv_cpu_intc *) + __metal_driver_cpu_interrupt_controller((struct metal_cpu *)cpu); + intc->metal_exception_table[mcause & METAL_MCAUSE_CAUSE]( + (struct metal_cpu *)cpu, id); + } +} + +/* The metal_timer_interrupt_vector_handler() function can be redefined. */ +void __attribute__((weak, interrupt)) +metal_timer_interrupt_vector_handler(void) { + __METAL_IRQ_VECTOR_HANDLER(METAL_INTERRUPT_ID_TMR); +} + +void __metal_default_beu_handler(int id, void *priv) {} + +void __metal_default_timer_handler(int id, void *priv) { + struct metal_cpu *cpu = __metal_driver_cpu_get(__metal_myhart_id()); + unsigned long long time = __metal_driver_cpu_mtime_get(cpu); + + /* Set a 10 cycle timer */ + __metal_driver_cpu_mtimecmp_set(cpu, time + 10); +} + +/* The metal_external_interrupt_vector_handler() function can be redefined. */ +void __attribute__((weak, interrupt)) +metal_external_interrupt_vector_handler(void) { + __METAL_IRQ_VECTOR_HANDLER(METAL_INTERRUPT_ID_EXT); +} + +void __metal_exception_handler(void) __attribute__((interrupt, aligned(128))); +void __metal_exception_handler(void) { + int id; + void *priv; + uintptr_t mcause, mepc, mtval, mtvec; + struct __metal_driver_riscv_cpu_intc *intc; + struct __metal_driver_cpu *cpu = __metal_cpu_table[__metal_myhart_id()]; + + __asm__ volatile("csrr %0, mcause" : "=r"(mcause)); + __asm__ volatile("csrr %0, mepc" : "=r"(mepc)); + __asm__ volatile("csrr %0, mtval" : "=r"(mtval)); + __asm__ volatile("csrr %0, mtvec" : "=r"(mtvec)); + + if (cpu) { + intc = (struct __metal_driver_riscv_cpu_intc *) + __metal_driver_cpu_interrupt_controller((struct metal_cpu *)cpu); + id = mcause & METAL_MCAUSE_CAUSE; + if (mcause & METAL_MCAUSE_INTR) { + if (id == METAL_INTERRUPT_ID_BEU) { + priv = intc->metal_int_beu.exint_data; + intc->metal_int_beu.handler(id, priv); + return; + } + if ((id < METAL_INTERRUPT_ID_CSW) || + ((mtvec & METAL_MTVEC_MASK) == METAL_MTVEC_DIRECT)) { + priv = intc->metal_int_table[id].exint_data; + intc->metal_int_table[id].handler(id, priv); + return; + } + if ((mtvec & METAL_MTVEC_MASK) == METAL_MTVEC_CLIC) { + uintptr_t mtvt; + metal_interrupt_handler_t mtvt_handler; + + __asm__ volatile("csrr %0, 0x307" : "=r"(mtvt)); + priv = intc->metal_int_table[METAL_INTERRUPT_ID_SW].sub_int; + mtvt_handler = (metal_interrupt_handler_t) * (uintptr_t *)mtvt; + mtvt_handler(id, priv); + return; + } + } else { + intc->metal_exception_table[id]((struct metal_cpu *)cpu, id); + } + } +} + +/* The metal_lc0_interrupt_vector_handler() function can be redefined. */ +void __attribute__((weak, interrupt)) metal_lc0_interrupt_vector_handler(void) { + __METAL_IRQ_VECTOR_HANDLER(METAL_INTERRUPT_ID_LC0); +} + +/* The metal_lc1_interrupt_vector_handler() function can be redefined. */ +void __attribute__((weak, interrupt)) metal_lc1_interrupt_vector_handler(void) { + __METAL_IRQ_VECTOR_HANDLER(METAL_INTERRUPT_ID_LC1); +} + +/* The metal_lc2_interrupt_vector_handler() function can be redefined. */ +void __attribute__((weak, interrupt)) metal_lc2_interrupt_vector_handler(void) { + __METAL_IRQ_VECTOR_HANDLER(METAL_INTERRUPT_ID_LC2); +} + +/* The metal_lc3_interrupt_vector_handler() function can be redefined. */ +void __attribute__((weak, interrupt)) metal_lc3_interrupt_vector_handler(void) { + __METAL_IRQ_VECTOR_HANDLER(METAL_INTERRUPT_ID_LC3); +} + +/* The metal_lc4_interrupt_vector_handler() function can be redefined. */ +void __attribute__((weak, interrupt)) metal_lc4_interrupt_vector_handler(void) { + __METAL_IRQ_VECTOR_HANDLER(METAL_INTERRUPT_ID_LC4); +} + +/* The metal_lc5_interrupt_vector_handler() function can be redefined. */ +void __attribute__((weak, interrupt)) metal_lc5_interrupt_vector_handler(void) { + __METAL_IRQ_VECTOR_HANDLER(METAL_INTERRUPT_ID_LC5); +} + +/* The metal_lc6_interrupt_vector_handler() function can be redefined. */ +void __attribute__((weak, interrupt)) metal_lc6_interrupt_vector_handler(void) { + __METAL_IRQ_VECTOR_HANDLER(METAL_INTERRUPT_ID_LC6); +} + +/* The metal_lc7_interrupt_vector_handler() function can be redefined. */ +void __attribute__((weak, interrupt)) metal_lc7_interrupt_vector_handler(void) { + __METAL_IRQ_VECTOR_HANDLER(METAL_INTERRUPT_ID_LC7); +} + +/* The metal_lc8_interrupt_vector_handler() function can be redefined. */ +void __attribute__((weak, interrupt)) metal_lc8_interrupt_vector_handler(void) { + __METAL_IRQ_VECTOR_HANDLER(METAL_INTERRUPT_ID_LC8); +} + +/* The metal_lc9_interrupt_vector_handler() function can be redefined. */ +void __attribute__((weak, interrupt)) metal_lc9_interrupt_vector_handler(void) { + __METAL_IRQ_VECTOR_HANDLER(METAL_INTERRUPT_ID_LC9); +} + +/* The metal_lc10_interrupt_vector_handler() function can be redefined. */ +void __attribute__((weak, interrupt)) +metal_lc10_interrupt_vector_handler(void) { + __METAL_IRQ_VECTOR_HANDLER(METAL_INTERRUPT_ID_LC10); +} + +/* The metal_lc11_interrupt_vector_handler() function can be redefined. */ +void __attribute__((weak, interrupt)) +metal_lc11_interrupt_vector_handler(void) { + __METAL_IRQ_VECTOR_HANDLER(METAL_INTERRUPT_ID_LC11); +} + +/* The metal_lc12_interrupt_vector_handler() function can be redefined. */ +void __attribute__((weak, interrupt)) +metal_lc12_interrupt_vector_handler(void) { + __METAL_IRQ_VECTOR_HANDLER(METAL_INTERRUPT_ID_LC12); +} + +/* The metal_lc13_interrupt_vector_handler() function can be redefined. */ +void __attribute__((weak, interrupt)) +metal_lc13_interrupt_vector_handler(void) { + __METAL_IRQ_VECTOR_HANDLER(METAL_INTERRUPT_ID_LC13); +} + +/* The metal_lc14_interrupt_vector_handler() function can be redefined. */ +void __attribute__((weak, interrupt)) +metal_lc14_interrupt_vector_handler(void) { + __METAL_IRQ_VECTOR_HANDLER(METAL_INTERRUPT_ID_LC14); +} + +/* The metal_lc15_interrupt_vector_handler() function can be redefined. */ +void __attribute__((weak, interrupt)) +metal_lc15_interrupt_vector_handler(void) { + __METAL_IRQ_VECTOR_HANDLER(METAL_INTERRUPT_ID_LC15); +} + +metal_vector_mode __metal_controller_interrupt_vector_mode(void) { + uintptr_t val; + + __asm__ volatile("csrr %0, mtvec" : "=r"(val)); + val &= METAL_MTVEC_MASK; + + switch (val) { + case METAL_MTVEC_CLIC: + return METAL_SELECTIVE_VECTOR_MODE; + case METAL_MTVEC_CLIC_VECTORED: + return METAL_HARDWARE_VECTOR_MODE; + case METAL_MTVEC_VECTORED: + return METAL_VECTOR_MODE; + } + return METAL_DIRECT_MODE; +} + +void __metal_controller_interrupt_vector(metal_vector_mode mode, + void *vec_table) { + uintptr_t trap_entry, val; + + __asm__ volatile("csrr %0, mtvec" : "=r"(val)); + val &= ~(METAL_MTVEC_CLIC_VECTORED | METAL_MTVEC_CLIC_RESERVED); + trap_entry = (uintptr_t)vec_table; + + switch (mode) { + case METAL_SELECTIVE_NONVECTOR_MODE: + case METAL_SELECTIVE_VECTOR_MODE: + __asm__ volatile("csrw 0x307, %0" ::"r"(trap_entry)); + __asm__ volatile("csrw mtvec, %0" ::"r"(val | METAL_MTVEC_CLIC)); + break; + case METAL_HARDWARE_VECTOR_MODE: + __asm__ volatile("csrw 0x307, %0" ::"r"(trap_entry)); + __asm__ volatile( + "csrw mtvec, %0" ::"r"(val | METAL_MTVEC_CLIC_VECTORED)); + break; + case METAL_VECTOR_MODE: + __asm__ volatile( + "csrw mtvec, %0" ::"r"(trap_entry | METAL_MTVEC_VECTORED)); + break; + case METAL_DIRECT_MODE: + __asm__ volatile( + "csrw mtvec, %0" ::"r"(trap_entry & ~METAL_MTVEC_CLIC_VECTORED)); + break; + } +} + +int __metal_valid_interrupt_id(int id) { + switch (id) { + case METAL_INTERRUPT_ID_SW: + case METAL_INTERRUPT_ID_TMR: + case METAL_INTERRUPT_ID_EXT: + case METAL_INTERRUPT_ID_LC0: + case METAL_INTERRUPT_ID_LC1: + case METAL_INTERRUPT_ID_LC2: + case METAL_INTERRUPT_ID_LC3: + case METAL_INTERRUPT_ID_LC4: + case METAL_INTERRUPT_ID_LC5: + case METAL_INTERRUPT_ID_LC6: + case METAL_INTERRUPT_ID_LC7: + case METAL_INTERRUPT_ID_LC8: + case METAL_INTERRUPT_ID_LC9: + case METAL_INTERRUPT_ID_LC10: + case METAL_INTERRUPT_ID_LC11: + case METAL_INTERRUPT_ID_LC12: + case METAL_INTERRUPT_ID_LC13: + case METAL_INTERRUPT_ID_LC14: + case METAL_INTERRUPT_ID_LC15: + case METAL_INTERRUPT_ID_BEU: + return 1; + default: + break; + } + + return 0; +} + +int __metal_local_interrupt_enable(struct metal_interrupt *controller, + metal_interrupt_id_e id, int enable) { + int rc = 0; + + if (!controller) { + return -1; + } + + switch (id) { + case METAL_INTERRUPT_ID_BASE: + if (enable) { + __metal_interrupt_global_enable(); + } else { + __metal_interrupt_global_disable(); + } + break; + case METAL_INTERRUPT_ID_SW: + if (enable) { + __metal_interrupt_software_enable(); + } else { + __metal_interrupt_software_disable(); + } + break; + case METAL_INTERRUPT_ID_TMR: + if (enable) { + __metal_interrupt_timer_enable(); + } else { + __metal_interrupt_timer_disable(); + } + break; + case METAL_INTERRUPT_ID_EXT: + if (enable) { + __metal_interrupt_external_enable(); + } else { + __metal_interrupt_external_disable(); + } + break; + case METAL_INTERRUPT_ID_LC0: + case METAL_INTERRUPT_ID_LC1: + case METAL_INTERRUPT_ID_LC2: + case METAL_INTERRUPT_ID_LC3: + case METAL_INTERRUPT_ID_LC4: + case METAL_INTERRUPT_ID_LC5: + case METAL_INTERRUPT_ID_LC6: + case METAL_INTERRUPT_ID_LC7: + case METAL_INTERRUPT_ID_LC8: + case METAL_INTERRUPT_ID_LC9: + case METAL_INTERRUPT_ID_LC10: + case METAL_INTERRUPT_ID_LC11: + case METAL_INTERRUPT_ID_LC12: + case METAL_INTERRUPT_ID_LC13: + case METAL_INTERRUPT_ID_LC14: + case METAL_INTERRUPT_ID_LC15: + if (enable) { + __metal_interrupt_local_enable(id); + } else { + __metal_interrupt_local_disable(id); + } + break; + default: + rc = -1; + } + return rc; +} + +int __metal_exception_register(struct metal_interrupt *controller, int ecode, + metal_exception_handler_t isr) { + struct __metal_driver_riscv_cpu_intc *intc = (void *)(controller); + + if ((ecode < METAL_MAX_EXCEPTION_CODE) && isr) { + intc->metal_exception_table[ecode] = isr; + return 0; + } + return -1; +} + +extern void early_trap_vector(void); +void __metal_driver_riscv_cpu_controller_interrupt_init( + struct metal_interrupt *controller) { + struct __metal_driver_riscv_cpu_intc *intc = (void *)(controller); + + if (!intc->init_done) { + /* Default to use direct interrupt, setup sw cb table*/ + for (int i = 0; i < METAL_MAX_MI; i++) { + intc->metal_int_table[i].handler = NULL; + intc->metal_int_table[i].sub_int = NULL; + intc->metal_int_table[i].exint_data = NULL; + } + + for (int i = 0; i < METAL_MAX_ME; i++) { + intc->metal_exception_table[i] = __metal_default_exception_handler; + } + + /* + * Set the real trap handler if the value of mtvec is equal to + * early_trap_vector. If mtvec is not equal to early_trap_vector, + * that means user has own trap handler, then we don't overwrite it. + */ + uintptr_t mtvec; + __asm__ volatile("csrr %0, mtvec" : "=r"(mtvec)); + if (mtvec == (uintptr_t)&early_trap_vector) { + __metal_controller_interrupt_vector( + METAL_DIRECT_MODE, + (void *)(uintptr_t)&__metal_exception_handler); + } + intc->init_done = 1; + } +} + +int __metal_driver_riscv_cpu_controller_interrupt_register( + struct metal_interrupt *controller, int id, metal_interrupt_handler_t isr, + void *priv) { + int rc = 0; + struct __metal_driver_riscv_cpu_intc *intc = (void *)(controller); + + if (!__metal_valid_interrupt_id(id)) { + return -11; + } + if ((id == METAL_INTERRUPT_ID_BEU) && + (__metal_controller_interrupt_vector_mode() != METAL_DIRECT_MODE)) { + /* Only allow registration of the bus error unit interrupt if + * interrupt vectoring if off */ + return -13; + } + + if (isr) { + if (id == METAL_INTERRUPT_ID_BEU) { + intc->metal_int_beu.handler = isr; + intc->metal_int_beu.exint_data = priv; + } else { + intc->metal_int_table[id].handler = isr; + intc->metal_int_table[id].exint_data = priv; + } + } else { + switch (id) { + case METAL_INTERRUPT_ID_SW: + intc->metal_int_table[id].handler = __metal_default_sw_handler; + intc->metal_int_table[id].sub_int = priv; + break; + case METAL_INTERRUPT_ID_TMR: + intc->metal_int_table[id].handler = __metal_default_timer_handler; + intc->metal_int_table[id].sub_int = priv; + break; + case METAL_INTERRUPT_ID_BEU: + intc->metal_int_beu.handler = __metal_default_beu_handler; + intc->metal_int_beu.exint_data = priv; + break; + case METAL_INTERRUPT_ID_EXT: + case METAL_INTERRUPT_ID_LC0: + case METAL_INTERRUPT_ID_LC1: + case METAL_INTERRUPT_ID_LC2: + case METAL_INTERRUPT_ID_LC3: + case METAL_INTERRUPT_ID_LC4: + case METAL_INTERRUPT_ID_LC5: + case METAL_INTERRUPT_ID_LC6: + case METAL_INTERRUPT_ID_LC7: + case METAL_INTERRUPT_ID_LC8: + case METAL_INTERRUPT_ID_LC9: + case METAL_INTERRUPT_ID_LC10: + case METAL_INTERRUPT_ID_LC11: + case METAL_INTERRUPT_ID_LC12: + case METAL_INTERRUPT_ID_LC13: + case METAL_INTERRUPT_ID_LC14: + case METAL_INTERRUPT_ID_LC15: + intc->metal_int_table[id].handler = + __metal_default_interrupt_handler; + intc->metal_int_table[id].sub_int = priv; + break; + default: + rc = -12; + } + } + return rc; +} + +int __metal_driver_riscv_cpu_controller_interrupt_enable( + struct metal_interrupt *controller, int id) { + return __metal_local_interrupt_enable(controller, id, METAL_ENABLE); +} + +int __metal_driver_riscv_cpu_controller_interrupt_disable( + struct metal_interrupt *controller, int id) { + return __metal_local_interrupt_enable(controller, id, METAL_DISABLE); +} + +int __metal_driver_riscv_cpu_controller_interrupt_enable_vector( + struct metal_interrupt *controller, int id, metal_vector_mode mode) { + struct __metal_driver_riscv_cpu_intc *intc = (void *)(controller); + + if (id == METAL_INTERRUPT_ID_BASE) { + if (mode == METAL_DIRECT_MODE) { + __metal_controller_interrupt_vector( + mode, (void *)(uintptr_t)&__metal_exception_handler); + return 0; + } + if (mode == METAL_VECTOR_MODE) { + __metal_controller_interrupt_vector( + mode, (void *)&intc->metal_mtvec_table); + return 0; + } + } + return -1; +} + +int __metal_driver_riscv_cpu_controller_interrupt_disable_vector( + struct metal_interrupt *controller, int id) { + if (id == METAL_INTERRUPT_ID_BASE) { + __metal_controller_interrupt_vector( + METAL_DIRECT_MODE, (void *)(uintptr_t)&__metal_exception_handler); + return 0; + } + return -1; +} + +metal_vector_mode __metal_driver_riscv_cpu_controller_get_vector_mode( + struct metal_interrupt *controller) { + return __metal_controller_interrupt_vector_mode(); +} + +int __metal_driver_riscv_cpu_controller_set_vector_mode( + struct metal_interrupt *controller, metal_vector_mode mode) { + + if (mode == METAL_DIRECT_MODE) { + __metal_controller_interrupt_vector( + mode, (void *)(uintptr_t)&__metal_exception_handler); + return 0; + } + if (mode == METAL_VECTOR_MODE) { + __metal_controller_interrupt_vector( + mode, (void *)(uintptr_t)__metal_vector_table); + return 0; + } + return -1; +} + +int __metal_driver_riscv_cpu_controller_command_request( + struct metal_interrupt *controller, int cmd, void *data) { + /* NOP for now, unless local interrupt lines the like of clic, clint, plic + */ + return 0; +} + +/* CPU driver !!! */ + +unsigned long long __metal_driver_cpu_mcycle_get(struct metal_cpu *cpu) { + unsigned long long val = 0; + +#if __riscv_xlen == 32 + unsigned long hi, hi1, lo; + + do { + __asm__ volatile("csrr %0, mcycleh" : "=r"(hi)); + __asm__ volatile("csrr %0, mcycle" : "=r"(lo)); + __asm__ volatile("csrr %0, mcycleh" : "=r"(hi1)); + /* hi != hi1 means mcycle overflow during we get value, + * so we must retry. */ + } while (hi != hi1); + + val = ((unsigned long long)hi << 32) | lo; +#else + __asm__ volatile("csrr %0, mcycle" : "=r"(val)); +#endif + + return val; +} + +unsigned long long __metal_driver_cpu_timebase_get(struct metal_cpu *cpu) { + int timebase; + if (!cpu) { + return 0; + } + + timebase = __metal_driver_cpu_timebase((struct metal_cpu *)cpu); + return timebase; +} + +unsigned long long __metal_driver_cpu_mtime_get(struct metal_cpu *cpu) { + unsigned long long time = 0; + struct metal_interrupt *tmr_intc; + struct __metal_driver_riscv_cpu_intc *intc = + (struct __metal_driver_riscv_cpu_intc *) + __metal_driver_cpu_interrupt_controller(cpu); + + if (intc) { + tmr_intc = intc->metal_int_table[METAL_INTERRUPT_ID_TMR].sub_int; + if (tmr_intc) { + tmr_intc->vtable->command_request(tmr_intc, METAL_TIMER_MTIME_GET, + &time); + } + } + return time; +} + +int __metal_driver_cpu_mtimecmp_set(struct metal_cpu *cpu, + unsigned long long time) { + int rc = -1; + struct metal_interrupt *tmr_intc; + struct __metal_driver_riscv_cpu_intc *intc = + (struct __metal_driver_riscv_cpu_intc *) + __metal_driver_cpu_interrupt_controller(cpu); + + if (intc) { + tmr_intc = intc->metal_int_table[METAL_INTERRUPT_ID_TMR].sub_int; + if (tmr_intc) { + rc = tmr_intc->vtable->mtimecmp_set( + tmr_intc, __metal_driver_cpu_hartid(cpu), time); + } + } + return rc; +} + +struct metal_interrupt * +__metal_driver_cpu_timer_controller_interrupt(struct metal_cpu *cpu) { +#ifdef __METAL_DT_RISCV_CLINT0_HANDLE + return __METAL_DT_RISCV_CLINT0_HANDLE; +#else +#ifdef __METAL_DT_SIFIVE_CLIC0_HANDLE + return __METAL_DT_SIFIVE_CLIC0_HANDLE; +#else +#pragma message("There is no interrupt controller for Timer interrupt") + return NULL; +#endif +#endif +} + +int __metal_driver_cpu_get_timer_interrupt_id(struct metal_cpu *cpu) { + return METAL_INTERRUPT_ID_TMR; +} + +struct metal_interrupt * +__metal_driver_cpu_sw_controller_interrupt(struct metal_cpu *cpu) { +#ifdef __METAL_DT_RISCV_CLINT0_HANDLE + return __METAL_DT_RISCV_CLINT0_HANDLE; +#else +#ifdef __METAL_DT_SIFIVE_CLIC0_HANDLE + return __METAL_DT_SIFIVE_CLIC0_HANDLE; +#else +#pragma message("There is no interrupt controller for Software interrupt") + return NULL; +#endif +#endif +} + +int __metal_driver_cpu_get_sw_interrupt_id(struct metal_cpu *cpu) { + return METAL_INTERRUPT_ID_SW; +} + +int __metal_driver_cpu_set_sw_ipi(struct metal_cpu *cpu, int hartid) { + int rc = -1; + struct metal_interrupt *sw_intc; + struct __metal_driver_riscv_cpu_intc *intc = + (struct __metal_driver_riscv_cpu_intc *) + __metal_driver_cpu_interrupt_controller(cpu); + + if (intc) { + sw_intc = intc->metal_int_table[METAL_INTERRUPT_ID_SW].sub_int; + if (sw_intc) { + rc = sw_intc->vtable->command_request( + sw_intc, METAL_SOFTWARE_IPI_SET, &hartid); + } + } + return rc; +} + +int __metal_driver_cpu_clear_sw_ipi(struct metal_cpu *cpu, int hartid) { + int rc = -1; + struct metal_interrupt *sw_intc; + struct __metal_driver_riscv_cpu_intc *intc = + (struct __metal_driver_riscv_cpu_intc *) + __metal_driver_cpu_interrupt_controller(cpu); + + if (intc) { + sw_intc = intc->metal_int_table[METAL_INTERRUPT_ID_SW].sub_int; + if (sw_intc) { + rc = sw_intc->vtable->command_request( + sw_intc, METAL_SOFTWARE_IPI_CLEAR, &hartid); + } + } + return rc; +} + +int __metal_driver_cpu_get_msip(struct metal_cpu *cpu, int hartid) { + int rc = 0; + struct metal_interrupt *sw_intc; + struct __metal_driver_riscv_cpu_intc *intc = + (struct __metal_driver_riscv_cpu_intc *) + __metal_driver_cpu_interrupt_controller(cpu); + + if (intc) { + sw_intc = intc->metal_int_table[METAL_INTERRUPT_ID_SW].sub_int; + if (sw_intc) { + rc = sw_intc->vtable->command_request( + sw_intc, METAL_SOFTWARE_MSIP_GET, &hartid); + } + } + return rc; +} + +struct metal_interrupt * +__metal_driver_cpu_controller_interrupt(struct metal_cpu *cpu) { + return __metal_driver_cpu_interrupt_controller(cpu); +} + +int __metal_driver_cpu_enable_interrupt(struct metal_cpu *cpu, void *priv) { + if (__metal_driver_cpu_interrupt_controller(cpu)) { + /* Only support machine mode for now */ + __metal_interrupt_global_enable(); + return 0; + } + return -1; +} + +int __metal_driver_cpu_disable_interrupt(struct metal_cpu *cpu, void *priv) { + if (__metal_driver_cpu_interrupt_controller(cpu)) { + /* Only support machine mode for now */ + __metal_interrupt_global_disable(); + return 0; + } + return -1; +} + +int __metal_driver_cpu_exception_register(struct metal_cpu *cpu, int ecode, + metal_exception_handler_t isr) { + struct __metal_driver_riscv_cpu_intc *intc = + (struct __metal_driver_riscv_cpu_intc *) + __metal_driver_cpu_interrupt_controller(cpu); + + if (intc) { + return __metal_exception_register((struct metal_interrupt *)intc, ecode, + isr); + } + return -1; +} + +int __metal_driver_cpu_get_instruction_length(struct metal_cpu *cpu, + uintptr_t epc) { + /** + * Per ISA compressed instruction has last two bits of opcode set. + * The encoding '00' '01' '10' are used for compressed instruction. + * Only enconding '11' isn't regarded as compressed instruction (>16b). + */ + return ((*(unsigned short *)epc & METAL_INSN_LENGTH_MASK) == + METAL_INSN_NOT_COMPRESSED) + ? 4 + : 2; +} + +uintptr_t __metal_driver_cpu_get_exception_pc(struct metal_cpu *cpu) { + uintptr_t mepc; + __asm__ volatile("csrr %0, mepc" : "=r"(mepc)); + return mepc; +} + +int __metal_driver_cpu_set_exception_pc(struct metal_cpu *cpu, uintptr_t mepc) { + __asm__ volatile("csrw mepc, %0" ::"r"(mepc)); + return 0; +} + +struct metal_buserror *__metal_driver_cpu_get_buserror(struct metal_cpu *cpu) { + return __metal_driver_cpu_buserror(cpu); +} + +__METAL_DEFINE_VTABLE(__metal_driver_vtable_riscv_cpu_intc) = { + .controller_vtable.interrupt_init = + __metal_driver_riscv_cpu_controller_interrupt_init, + .controller_vtable.interrupt_register = + __metal_driver_riscv_cpu_controller_interrupt_register, + .controller_vtable.interrupt_enable = + __metal_driver_riscv_cpu_controller_interrupt_enable, + .controller_vtable.interrupt_disable = + __metal_driver_riscv_cpu_controller_interrupt_disable, + .controller_vtable.interrupt_get_vector_mode = + __metal_driver_riscv_cpu_controller_get_vector_mode, + .controller_vtable.interrupt_set_vector_mode = + __metal_driver_riscv_cpu_controller_set_vector_mode, + .controller_vtable.command_request = + __metal_driver_riscv_cpu_controller_command_request, +}; + +__METAL_DEFINE_VTABLE(__metal_driver_vtable_cpu) = { + .cpu_vtable.mcycle_get = __metal_driver_cpu_mcycle_get, + .cpu_vtable.timebase_get = __metal_driver_cpu_timebase_get, + .cpu_vtable.mtime_get = __metal_driver_cpu_mtime_get, + .cpu_vtable.mtimecmp_set = __metal_driver_cpu_mtimecmp_set, + .cpu_vtable.tmr_controller_interrupt = + __metal_driver_cpu_timer_controller_interrupt, + .cpu_vtable.get_tmr_interrupt_id = + __metal_driver_cpu_get_timer_interrupt_id, + .cpu_vtable.sw_controller_interrupt = + __metal_driver_cpu_sw_controller_interrupt, + .cpu_vtable.get_sw_interrupt_id = __metal_driver_cpu_get_sw_interrupt_id, + .cpu_vtable.set_sw_ipi = __metal_driver_cpu_set_sw_ipi, + .cpu_vtable.clear_sw_ipi = __metal_driver_cpu_clear_sw_ipi, + .cpu_vtable.get_msip = __metal_driver_cpu_get_msip, + .cpu_vtable.controller_interrupt = __metal_driver_cpu_controller_interrupt, + .cpu_vtable.exception_register = __metal_driver_cpu_exception_register, + .cpu_vtable.get_ilen = __metal_driver_cpu_get_instruction_length, + .cpu_vtable.get_epc = __metal_driver_cpu_get_exception_pc, + .cpu_vtable.set_epc = __metal_driver_cpu_set_exception_pc, + .cpu_vtable.get_buserror = __metal_driver_cpu_get_buserror, +}; diff --git a/arch/riscv32/fe310/src/freedom-metal/src/drivers/riscv_plic0.c b/arch/riscv32/fe310/src/freedom-metal/src/drivers/riscv_plic0.c new file mode 100644 index 00000000..21d16ab1 --- /dev/null +++ b/arch/riscv32/fe310/src/freedom-metal/src/drivers/riscv_plic0.c @@ -0,0 +1,314 @@ +/* Copyright 2018 SiFive, Inc */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include + +#ifdef METAL_RISCV_PLIC0 + +#define PLIC0_MAX_INTERRUPTS 1024 + +#include +#include +#include +#include +#include + +unsigned int +__metal_plic0_claim_interrupt(struct __metal_driver_riscv_plic0 *plic, + int context_id) { + unsigned long control_base = __metal_driver_sifive_plic0_control_base( + (struct metal_interrupt *)plic); + return __METAL_ACCESS_ONCE( + (__metal_io_u32 *)(control_base + METAL_RISCV_PLIC0_CONTEXT_BASE + + (context_id * METAL_RISCV_PLIC0_CONTEXT_PER_HART) + + METAL_RISCV_PLIC0_CONTEXT_CLAIM)); +} + +void __metal_plic0_complete_interrupt(struct __metal_driver_riscv_plic0 *plic, + int context_id, unsigned int id) { + unsigned long control_base = __metal_driver_sifive_plic0_control_base( + (struct metal_interrupt *)plic); + __METAL_ACCESS_ONCE( + (__metal_io_u32 *)(control_base + METAL_RISCV_PLIC0_CONTEXT_BASE + + (context_id * METAL_RISCV_PLIC0_CONTEXT_PER_HART) + + METAL_RISCV_PLIC0_CONTEXT_CLAIM)) = id; +} + +int __metal_plic0_set_threshold(struct metal_interrupt *controller, + int context_id, unsigned int threshold) { + unsigned long control_base = + __metal_driver_sifive_plic0_control_base(controller); + __METAL_ACCESS_ONCE( + (__metal_io_u32 *)(control_base + METAL_RISCV_PLIC0_CONTEXT_BASE + + (context_id * METAL_RISCV_PLIC0_CONTEXT_PER_HART) + + METAL_RISCV_PLIC0_CONTEXT_THRESHOLD)) = threshold; + return 0; +} + +unsigned int __metal_plic0_get_threshold(struct metal_interrupt *controller, + int context_id) { + unsigned long control_base = + __metal_driver_sifive_plic0_control_base(controller); + return __METAL_ACCESS_ONCE( + (__metal_io_u32 *)(control_base + METAL_RISCV_PLIC0_CONTEXT_BASE + + (context_id * METAL_RISCV_PLIC0_CONTEXT_PER_HART) + + METAL_RISCV_PLIC0_CONTEXT_THRESHOLD)); +} + +int __metal_driver_riscv_plic0_set_priority(struct metal_interrupt *controller, + int id, unsigned int priority) { + unsigned long control_base = __metal_driver_sifive_plic0_control_base( + (struct metal_interrupt *)controller); + unsigned int max_priority = __metal_driver_sifive_plic0_max_priority( + (struct metal_interrupt *)controller); + if ((max_priority) && (priority < max_priority)) { + __METAL_ACCESS_ONCE( + (__metal_io_u32 *)(control_base + METAL_RISCV_PLIC0_PRIORITY_BASE + + (id << METAL_PLIC_SOURCE_PRIORITY_SHIFT))) = + priority; + return 0; + } + return -1; +} + +unsigned int +__metal_driver_riscv_plic0_get_priority(struct metal_interrupt *controller, + int id) { + unsigned long control_base = + __metal_driver_sifive_plic0_control_base(controller); + + return __METAL_ACCESS_ONCE( + (__metal_io_u32 *)(control_base + METAL_RISCV_PLIC0_PRIORITY_BASE + + (id << METAL_PLIC_SOURCE_PRIORITY_SHIFT))); +} + +int __metal_plic0_enable(struct __metal_driver_riscv_plic0 *plic, + int context_id, int id, int enable) { + unsigned int current; + unsigned long control_base = __metal_driver_sifive_plic0_control_base( + (struct metal_interrupt *)plic); + + current = __METAL_ACCESS_ONCE( + (__metal_io_u32 *)(control_base + METAL_RISCV_PLIC0_ENABLE_BASE + + (context_id * METAL_RISCV_PLIC0_ENABLE_PER_HART) + + (id >> METAL_PLIC_SOURCE_SHIFT) * 4)); + __METAL_ACCESS_ONCE( + (__metal_io_u32 *)(control_base + METAL_RISCV_PLIC0_ENABLE_BASE + + (context_id * METAL_RISCV_PLIC0_ENABLE_PER_HART) + + ((id >> METAL_PLIC_SOURCE_SHIFT) * 4))) = + enable ? (current | (1 << (id & METAL_PLIC_SOURCE_MASK))) + : (current & ~(1 << (id & METAL_PLIC_SOURCE_MASK))); + + return 0; +} + +void __metal_plic0_default_handler(int id, void *priv) { metal_shutdown(300); } + +void __metal_plic0_handler(int id, void *priv) { + struct __metal_driver_riscv_plic0 *plic = priv; + int contextid = + __metal_driver_sifive_plic0_context_ids(__metal_myhart_id()); + unsigned int idx = __metal_plic0_claim_interrupt(plic, contextid); + unsigned int num_interrupts = __metal_driver_sifive_plic0_num_interrupts( + (struct metal_interrupt *)plic); + + if ((idx < num_interrupts) && (plic->metal_exint_table[idx])) { + plic->metal_exint_table[idx](idx, + plic->metal_exdata_table[idx].exint_data); + } + + __metal_plic0_complete_interrupt(plic, contextid, idx); +} + +void __metal_driver_riscv_plic0_init(struct metal_interrupt *controller) { + struct __metal_driver_riscv_plic0 *plic = (void *)(controller); + + if (!plic->init_done) { + int num_interrupts, line; + struct metal_interrupt *intc; + + for (int parent = 0; parent < __METAL_PLIC_NUM_PARENTS; parent++) { + num_interrupts = + __metal_driver_sifive_plic0_num_interrupts(controller); + intc = __metal_driver_sifive_plic0_interrupt_parents(controller, + parent); + line = + __metal_driver_sifive_plic0_interrupt_lines(controller, parent); + + /* Initialize ist parent controller, aka cpu_intc. */ + intc->vtable->interrupt_init(intc); + + for (int i = 0; i < PLIC0_MAX_INTERRUPTS; i++) { + __metal_plic0_enable(plic, parent, i, METAL_DISABLE); + if (i < num_interrupts) { + __metal_driver_riscv_plic0_set_priority(controller, i, 0); + plic->metal_exint_table[i] = NULL; + plic->metal_exdata_table[i].sub_int = NULL; + plic->metal_exdata_table[i].exint_data = NULL; + } + } + + __metal_plic0_set_threshold(controller, parent, 0); + + /* Register plic (ext) interrupt with with parent controller */ + intc->vtable->interrupt_register(intc, line, NULL, plic); + /* Register plic handler for dispatching its device interrupts */ + intc->vtable->interrupt_register(intc, line, __metal_plic0_handler, + plic); + /* Enable plic (ext) interrupt with with parent controller */ + intc->vtable->interrupt_enable(intc, line); + } + plic->init_done = 1; + } +} + +int __metal_driver_riscv_plic0_register(struct metal_interrupt *controller, + int id, metal_interrupt_handler_t isr, + void *priv) { + struct __metal_driver_riscv_plic0 *plic = (void *)(controller); + + if (id >= __metal_driver_sifive_plic0_num_interrupts(controller)) { + return -1; + } + + if (isr) { + __metal_driver_riscv_plic0_set_priority(controller, id, 2); + plic->metal_exint_table[id] = isr; + plic->metal_exdata_table[id].exint_data = priv; + } else { + __metal_driver_riscv_plic0_set_priority(controller, id, 1); + plic->metal_exint_table[id] = __metal_plic0_default_handler; + plic->metal_exdata_table[id].sub_int = priv; + } + + return 0; +} + +int __metal_driver_riscv_plic0_enable(struct metal_interrupt *controller, + int id) { + struct __metal_driver_riscv_plic0 *plic = (void *)(controller); + + if (id >= __metal_driver_sifive_plic0_num_interrupts(controller)) { + return -1; + } + + __metal_plic0_enable(plic, __metal_myhart_id(), id, METAL_ENABLE); + return 0; +} + +int __metal_driver_riscv_plic0_disable(struct metal_interrupt *controller, + int id) { + struct __metal_driver_riscv_plic0 *plic = (void *)(controller); + + if (id >= __metal_driver_sifive_plic0_num_interrupts(controller)) { + return -1; + } + __metal_plic0_enable(plic, __metal_myhart_id(), id, METAL_DISABLE); + return 0; +} + +int __metal_driver_riscv_plic0_set_threshold(struct metal_interrupt *controller, + unsigned int threshold) { + return __metal_plic0_set_threshold(controller, __metal_myhart_id(), + threshold); +} + +unsigned int +__metal_driver_riscv_plic0_get_threshold(struct metal_interrupt *controller) { + return __metal_plic0_get_threshold(controller, __metal_myhart_id()); +} + +metal_affinity +__metal_driver_riscv_plic0_affinity_enable(struct metal_interrupt *controller, + metal_affinity bitmask, int id) { + metal_affinity ret = {0}; + int context; + + struct __metal_driver_riscv_plic0 *plic = (void *)(controller); + + if (id >= __metal_driver_sifive_plic0_num_interrupts(controller)) { + metal_affinity_set_val(ret, -1); + return ret; + } + + for_each_metal_affinity(context, bitmask) { + if (context != 0) + metal_affinity_set_bit( + ret, context, + __metal_plic0_enable(plic, context, id, METAL_ENABLE)); + } + + return ret; +} + +metal_affinity +__metal_driver_riscv_plic0_affinity_disable(struct metal_interrupt *controller, + metal_affinity bitmask, int id) { + metal_affinity ret = {0}; + int context; + + struct __metal_driver_riscv_plic0 *plic = (void *)(controller); + + if (id >= __metal_driver_sifive_plic0_num_interrupts(controller)) { + metal_affinity_set_val(ret, -1); + return ret; + } + + for_each_metal_affinity(context, bitmask) { + if (context != 0) + metal_affinity_set_bit( + ret, context, + __metal_plic0_enable(plic, context, id, METAL_DISABLE)); + } + + return ret; +} + +metal_affinity __metal_driver_riscv_plic0_affinity_set_threshold( + struct metal_interrupt *controller, metal_affinity bitmask, + unsigned int threshold) { + metal_affinity ret = {0}; + int context; + + for_each_metal_affinity(context, bitmask) { + if (context != 0) + metal_affinity_set_bit( + ret, context, + __metal_plic0_set_threshold(controller, context, threshold)); + } + + return ret; +} + +unsigned int __metal_driver_riscv_plic0_affinity_get_threshold( + struct metal_interrupt *controller, int context_id) { + __metal_plic0_get_threshold(controller, context_id); + return 0; +} + +__METAL_DEFINE_VTABLE(__metal_driver_vtable_riscv_plic0) = { + .plic_vtable.interrupt_init = __metal_driver_riscv_plic0_init, + .plic_vtable.interrupt_register = __metal_driver_riscv_plic0_register, + .plic_vtable.interrupt_enable = __metal_driver_riscv_plic0_enable, + .plic_vtable.interrupt_disable = __metal_driver_riscv_plic0_disable, + .plic_vtable.interrupt_get_threshold = + __metal_driver_riscv_plic0_get_threshold, + .plic_vtable.interrupt_set_threshold = + __metal_driver_riscv_plic0_set_threshold, + .plic_vtable.interrupt_get_priority = + __metal_driver_riscv_plic0_get_priority, + .plic_vtable.interrupt_set_priority = + __metal_driver_riscv_plic0_set_priority, + .plic_vtable.interrupt_affinity_enable = + __metal_driver_riscv_plic0_affinity_enable, + .plic_vtable.interrupt_affinity_disable = + __metal_driver_riscv_plic0_affinity_disable, + .plic_vtable.interrupt_affinity_get_threshold = + __metal_driver_riscv_plic0_affinity_get_threshold, + .plic_vtable.interrupt_affinity_set_threshold = + __metal_driver_riscv_plic0_affinity_set_threshold, +}; + +#endif /* METAL_RISCV_PLIC0 */ + +typedef int no_empty_translation_units; diff --git a/arch/riscv32/fe310/src/freedom-metal/src/drivers/sifive_buserror0.c b/arch/riscv32/fe310/src/freedom-metal/src/drivers/sifive_buserror0.c new file mode 100644 index 00000000..3a68fef4 --- /dev/null +++ b/arch/riscv32/fe310/src/freedom-metal/src/drivers/sifive_buserror0.c @@ -0,0 +1,257 @@ +/* Copyright 2020 SiFive, Inc */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include +#include +#include +#include + +#ifdef METAL_SIFIVE_BUSERROR0 + +#include +#include +#include +#include + +/* Enable all events on all hart bus error units */ +METAL_CONSTRUCTOR(metal_driver_sifive_buserror_init) { + for (int hart = 0; hart < metal_cpu_get_num_harts(); hart++) { + struct metal_cpu *cpu = metal_cpu_get(hart); + if (cpu != NULL) { + struct metal_buserror *beu = metal_cpu_get_buserror(cpu); + if (beu != NULL) { + metal_buserror_set_event_enabled(beu, METAL_BUSERROR_EVENT_ALL, + true); + } + } + } +} + +int metal_buserror_set_event_enabled(struct metal_buserror *beu, + metal_buserror_event_t events, + bool enabled) { + uintptr_t base = __metal_driver_sifive_buserror0_control_base(beu); + if (base == (uintptr_t)NULL) { + return 1; + } + if (!(events & METAL_BUSERROR_EVENT_ANY)) { + return 2; + } + + uintptr_t reg_enable = base + METAL_SIFIVE_BUSERROR0_ENABLE; + + if (enabled) { + __METAL_ACCESS_ONCE((__metal_io_u8 *)reg_enable) |= events; + } else { + __METAL_ACCESS_ONCE((__metal_io_u8 *)reg_enable) &= ~events; + } + + if (!(events & __METAL_ACCESS_ONCE((__metal_io_u8 *)reg_enable))) { + return __METAL_ACCESS_ONCE((__metal_io_u8 *)reg_enable); + } + + return 0; +} + +metal_buserror_event_t +metal_buserror_get_event_enabled(struct metal_buserror *beu) { + uintptr_t base = __metal_driver_sifive_buserror0_control_base(beu); + if (base == (uintptr_t)NULL) { + return 1; + } + + uintptr_t reg_enable = base + METAL_SIFIVE_BUSERROR0_ENABLE; + + return __METAL_ACCESS_ONCE((__metal_io_u8 *)reg_enable); +} + +int metal_buserror_set_platform_interrupt(struct metal_buserror *beu, + metal_buserror_event_t events, + bool enabled) { + uintptr_t base = __metal_driver_sifive_buserror0_control_base(beu); + if (base == (uintptr_t)NULL) { + return 1; + } + if (!(events & METAL_BUSERROR_EVENT_ANY)) { + return 2; + } + + uintptr_t platform_interrupt = + base + METAL_SIFIVE_BUSERROR0_PLATFORM_INTERRUPT; + + if (enabled) { + __METAL_ACCESS_ONCE((__metal_io_u8 *)platform_interrupt) |= events; + } else { + __METAL_ACCESS_ONCE((__metal_io_u8 *)platform_interrupt) &= ~events; + } + + return 0; +} + +int metal_buserror_set_local_interrupt(struct metal_buserror *beu, + metal_buserror_event_t events, + bool enabled) { + uintptr_t base = __metal_driver_sifive_buserror0_control_base(beu); + if (base == (uintptr_t)NULL) { + return 1; + } + if (!(events & METAL_BUSERROR_EVENT_ANY)) { + return 2; + } + + uintptr_t local_interrupt = base + METAL_SIFIVE_BUSERROR0_LOCAL_INTERRUPT; + + if (enabled) { + __METAL_ACCESS_ONCE((__metal_io_u8 *)local_interrupt) |= events; + } else { + __METAL_ACCESS_ONCE((__metal_io_u8 *)local_interrupt) &= ~events; + } + + return 0; +} + +metal_buserror_event_t metal_buserror_get_cause(struct metal_buserror *beu) { + uintptr_t base = __metal_driver_sifive_buserror0_control_base(beu); + if (base == (uintptr_t)NULL) { + return METAL_BUSERROR_EVENT_INVALID; + } + + uintptr_t cause = base + METAL_SIFIVE_BUSERROR0_CAUSE; + + return (metal_buserror_event_t)( + 1 << __METAL_ACCESS_ONCE((__metal_io_u8 *)cause)); +} + +int metal_buserror_clear_cause(struct metal_buserror *beu) { + uintptr_t base = __metal_driver_sifive_buserror0_control_base(beu); + if (base == (uintptr_t)NULL) { + /* We return (1 << 8) because the value of the cause register + * can never equal that value */ + return (1 << 8); + } + + uintptr_t cause = base + METAL_SIFIVE_BUSERROR0_CAUSE; + __METAL_ACCESS_ONCE((__metal_io_u8 *)cause) = METAL_BUSERROR_EVENT_NONE; + + return __METAL_ACCESS_ONCE((__metal_io_u8 *)cause); +} + +uintptr_t metal_buserror_get_event_address(struct metal_buserror *beu) { + uintptr_t base = __metal_driver_sifive_buserror0_control_base(beu); + if (base == (uintptr_t)NULL) { + return 0; + } + + uintptr_t value = base + METAL_SIFIVE_BUSERROR0_VALUE; + + return __METAL_ACCESS_ONCE((__metal_io_u8 *)value); +} + +bool metal_buserror_is_event_accrued(struct metal_buserror *beu, + metal_buserror_event_t events) { + uintptr_t base = __metal_driver_sifive_buserror0_control_base(beu); + if (base == (uintptr_t)NULL) { + return false; + } + + uintptr_t accrued = base + METAL_SIFIVE_BUSERROR0_ACCRUED; + + if (!(events & METAL_BUSERROR_EVENT_ANY)) { + return false; + } + + return !!(events & __METAL_ACCESS_ONCE((__metal_io_u8 *)accrued)); +} + +int metal_buserror_clear_event_accrued(struct metal_buserror *beu, + metal_buserror_event_t events) { + uintptr_t base = __metal_driver_sifive_buserror0_control_base(beu); + if (base == (uintptr_t)NULL) { + /* We return (1 << 8) because the value of the accrued register + * can never equal that value */ + return (1 << 8); + } + + uintptr_t accrued = base + METAL_SIFIVE_BUSERROR0_ACCRUED; + + if (!(events & METAL_BUSERROR_EVENT_ANY)) { + /* We return (1 << 9) because the value of the accrued register + * can never equal that value */ + return (1 << 9); + } else { + __METAL_ACCESS_ONCE((__metal_io_u8 *)accrued) &= ~events; + if (events & __METAL_ACCESS_ONCE((__metal_io_u8 *)accrued)) { + return __METAL_ACCESS_ONCE((__metal_io_u8 *)accrued); + } + } + + return 0; +} + +struct metal_interrupt * +metal_buserror_get_platform_interrupt_parent(struct metal_buserror *beu) { + return __metal_driver_sifive_buserror0_interrupt_parent(beu); +} + +int metal_buserror_get_platform_interrupt_id(struct metal_buserror *beu) { + return __metal_driver_sifive_buserror0_interrupt_id(beu); +} + +int metal_buserror_get_local_interrupt_id(struct metal_buserror *beu) { + return 128; +} + +#else + +int metal_buserror_set_event_enabled(struct metal_buserror *beu, + metal_buserror_event_t event, + bool enabled) { + return 1; +} + +int metal_buserror_set_platform_interrupt(struct metal_buserror *beu, + metal_buserror_event_t event, + bool enabled) { + return 1; +} + +int metal_buserror_set_local_interrupt(struct metal_buserror *beu, + metal_buserror_event_t event, + bool enabled) { + return 1; +} + +metal_buserror_event_t metal_buserror_get_cause(struct metal_buserror *beu) { + return METAL_BUSERROR_EVENT_INVALID; +} + +int metal_buserror_clear_cause(struct metal_buserror *beu) { return (1 << 9); } + +uintptr_t metal_buserror_get_event_address(struct metal_buserror *beu) { + return 0; +} + +bool metal_buserror_is_event_accrued(struct metal_buserror *beu, + metal_buserror_event_t event) { + return false; +} + +int metal_buserror_clear_event_accrued(struct metal_buserror *beu, + metal_buserror_event_t event) { + return (1 << 8); +} + +struct metal_interrupt * +metal_buserror_get_platform_interrupt_parent(struct metal_buserror *beu) { + return NULL; +} + +int metal_buserror_get_platform_interrupt_id(struct metal_buserror *beu) { + return 0; +} + +int metal_buserror_get_local_interrupt_id(struct metal_buserror *beu) { + return 0; +} + +#endif diff --git a/arch/riscv32/fe310/src/freedom-metal/src/drivers/sifive_ccache0.c b/arch/riscv32/fe310/src/freedom-metal/src/drivers/sifive_ccache0.c new file mode 100644 index 00000000..38e059ab --- /dev/null +++ b/arch/riscv32/fe310/src/freedom-metal/src/drivers/sifive_ccache0.c @@ -0,0 +1,347 @@ +/* Copyright 2020 SiFive, Inc */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include + +#ifdef METAL_SIFIVE_CCACHE0 + +#include +#include +#include +#include +#ifdef METAL_SIFIVE_PL2CACHE0 +#include +#endif /* METAL_SIFIVE_PL2CACHE0 */ + +/* Macros to access memory mapped registers */ +#define REGW(x) *(volatile uint32_t *)(METAL_SIFIVE_CCACHE0_0_BASE_ADDRESS + x) + +#define REGD(x) *(volatile uint64_t *)(METAL_SIFIVE_CCACHE0_0_BASE_ADDRESS + x) + +/* Macros to specify register bit shift */ +#define REG_SHIFT_4 4 +#define REG_SHIFT_8 8 +#define REG_SHIFT_16 16 +#define REG_SHIFT_24 24 + +#define SIFIVE_CCACHE0_BYTE_MASK 0xFFUL + +static int sifive_ccache0_interrupts[] = METAL_SIFIVE_CCACHE0_INTERRUPTS; + +/* Initialize cache at start-up via metal constructors */ +METAL_CONSTRUCTOR(_sifive_ccache0_init) { sifive_ccache0_init(); } + +/* Linker symbols to calculate LIM allocated size */ +extern char metal_segment_lim_target_start, metal_segment_lim_target_end; +/* Linker symbols for the bss section */ +extern char metal_segment_bss_target_start, metal_segment_bss_target_end; + +int sifive_ccache0_init(void) { + int ret; + + sifive_ccache0_config config; + + /* Get cache configuration data */ + sifive_ccache0_get_config(&config); + + int lim_size = + &metal_segment_lim_target_end - &metal_segment_lim_target_start; + + if (lim_size) { /* Do not enable cache ways, corresponding to LIM area in + use. */ + while (lim_size > 0) { + lim_size -= (config.block_size * config.num_sets * config.num_bank); + config.num_ways--; + } + } + + /* Sanity check */ + int ccache_size = + config.block_size * config.num_sets * config.num_bank * config.num_ways; + char *tmp; + + tmp = &metal_segment_bss_target_start; + if ((tmp >= &metal_segment_lim_target_start) && + (tmp < (&metal_segment_lim_target_start + ccache_size))) { + ret = -1; + goto ccache0_init_exit; + } + + tmp = &metal_segment_bss_target_end; + if ((tmp >= &metal_segment_lim_target_start) && + (tmp < (&metal_segment_lim_target_start + ccache_size))) { + ret = -1; + goto ccache0_init_exit; + } + + /* Enable ways */ + ret = sifive_ccache0_set_enabled_ways(config.num_ways); + +ccache0_init_exit: +#ifdef METAL_SIFIVE_PL2CACHE0 + /* ccache0 is used as a L3 cache */ + if (0 != ret) { + /* ccache0 was not configure as cache */ + sifive_pl2cache0_set_cleanEvictenale_bit(0); + } +#endif /* METAL_SIFIVE_PL2CACHE0 */ + + return ret; +} + +void sifive_ccache0_get_config(sifive_ccache0_config *config) { + uint32_t val; + + if (config) /* Check for NULL */ + { + val = REGW(METAL_SIFIVE_CCACHE0_CONFIG); + + /* Populate cache configuration data */ + config->num_bank = (val & SIFIVE_CCACHE0_BYTE_MASK); + config->num_ways = ((val >> REG_SHIFT_8) & SIFIVE_CCACHE0_BYTE_MASK); + /* no. of sets, block size is 2's power of register value + (2 << (value-1)) */ + config->num_sets = + 2 << (((val >> REG_SHIFT_16) & SIFIVE_CCACHE0_BYTE_MASK) - 1); + config->block_size = + 2 << (((val >> REG_SHIFT_24) & SIFIVE_CCACHE0_BYTE_MASK) - 1); + } +} + +uint32_t sifive_ccache0_get_enabled_ways(void) { + + uint32_t val = 0; + + val = SIFIVE_CCACHE0_BYTE_MASK & REGW(METAL_SIFIVE_CCACHE0_WAYENABLE); + + /* The stored number is the way index, so increment by 1 */ + val++; + + return val; +} + +int sifive_ccache0_set_enabled_ways(uint32_t ways) { + + int ret = 0; + + /* We can't decrease the number of enabled ways */ + if (sifive_ccache0_get_enabled_ways() > ways) { + ret = -1; + } else { + /* The stored value is the index, so subtract one */ + uint32_t value = 0xFF & (ways - 1); + + /* Set the number of enabled ways */ + REGW(METAL_SIFIVE_CCACHE0_WAYENABLE) = value; + + /* Make sure the number of ways was set correctly */ + if (sifive_ccache0_get_enabled_ways() != ways) { + ret = -2; + } + } + + return ret; +} + +void sifive_ccache0_inject_ecc_error(uint32_t bitindex, + sifive_ccache0_ecc_errtype_t type) { + /* Induce ECC error at given bit index and location */ + REGW(METAL_SIFIVE_CCACHE0_ECCINJECTERROR) = + (uint32_t)(((type & 0x01) << REG_SHIFT_16) | (bitindex & 0xFF)); +} + +void sifive_ccache0_flush(uintptr_t flush_addr) { + /* Block memory access until operation completed */ + __asm volatile("fence rw, io" : : : "memory"); + +#if __riscv_xlen == 32 + REGW(METAL_SIFIVE_CCACHE0_FLUSH32) = flush_addr >> REG_SHIFT_4; +#else + REGD(METAL_SIFIVE_CCACHE0_FLUSH64) = flush_addr; +#endif + + __asm volatile("fence io, rw" : : : "memory"); +} + +uintptr_t sifive_ccache0_get_ecc_fix_addr(sifive_ccache0_ecc_errtype_t type) { + uintptr_t addr = 0; + + switch (type) { + /* Get most recently ECC corrected address */ + case SIFIVE_CCACHE0_DATA: + addr = (uintptr_t)REGD(METAL_SIFIVE_CCACHE0_DATECCFIXLOW); + break; + + case SIFIVE_CCACHE0_DIR: + addr = (uintptr_t)REGD(METAL_SIFIVE_CCACHE0_DIRECCFIXLOW); + break; + } + + return addr; +} + +uint32_t sifive_ccache0_get_ecc_fix_count(sifive_ccache0_ecc_errtype_t type) { + uint32_t count = 0; + + switch (type) { + /* Get number of times ECC errors were corrected */ + case SIFIVE_CCACHE0_DATA: + count = REGW(METAL_SIFIVE_CCACHE0_DATECCFIXCOUNT); + break; + + case SIFIVE_CCACHE0_DIR: + count = REGW(METAL_SIFIVE_CCACHE0_DIRECCFIXCOUNT); + break; + } + + return count; +} + +uintptr_t sifive_ccache0_get_ecc_fail_addr(sifive_ccache0_ecc_errtype_t type) { + uintptr_t addr = 0; + + switch (type) { + /* Get address location of most recent uncorrected ECC error */ + case SIFIVE_CCACHE0_DATA: + addr = (uintptr_t)REGD(METAL_SIFIVE_CCACHE0_DATECCFAILLOW); + break; + + case SIFIVE_CCACHE0_DIR: + addr = (uintptr_t)REGD(METAL_SIFIVE_CCACHE0_DIRECCFAILLOW); + break; + } + + return addr; +} + +uint32_t sifive_ccache0_get_ecc_fail_count(sifive_ccache0_ecc_errtype_t type) { + uint32_t count = 0; + + switch (type) { + /* Get number of times ECC errors were not corrected */ + case SIFIVE_CCACHE0_DATA: + count = REGW(METAL_SIFIVE_CCACHE0_DATECCFAILCOUNT); + break; + + case SIFIVE_CCACHE0_DIR: + count = REGW(METAL_SIFIVE_CCACHE0_DIRECCFAILCOUNT); + break; + } + + return count; +} + +uint64_t sifive_ccache0_get_way_mask(uint32_t master_id) { + uint64_t val = 0; + + /* Get way mask for given master ID */ + val = REGD(METAL_SIFIVE_CCACHE0_WAYMASK0 + master_id * 8); + + return val; +} + +int sifive_ccache0_set_way_mask(uint32_t master_id, uint64_t waymask) { + + /* Set way mask for given master ID */ + REGD(METAL_SIFIVE_CCACHE0_WAYMASK0 + master_id * 8) = waymask; + + return 0; +} + +void sifive_ccache0_set_pmevent_selector(uint32_t counter, uint64_t mask) { + +#if METAL_SIFIVE_CCACHE0_PERFMON_COUNTERS > 0 + if (counter < METAL_SIFIVE_CCACHE0_PERFMON_COUNTERS) { + + /* Set event selector for specified L2 event counter */ + REGD(METAL_SIFIVE_CCACHE0_PMEVENTSELECT0 + counter * 8) = mask; + } +#endif + return; +} + +uint64_t sifive_ccache0_get_pmevent_selector(uint32_t counter) { + uint64_t val = 0; + +#if METAL_SIFIVE_CCACHE0_PERFMON_COUNTERS > 0 + if (counter < METAL_SIFIVE_CCACHE0_PERFMON_COUNTERS) { + + /* Get event selector for specified L2 event counter */ + val = REGD(METAL_SIFIVE_CCACHE0_PMEVENTSELECT0 + counter * 8); + } +#endif + return val; +} + +void sifive_ccache0_clr_pmevent_counter(uint32_t counter) { + +#if METAL_SIFIVE_CCACHE0_PERFMON_COUNTERS > 0 + if (counter < METAL_SIFIVE_CCACHE0_PERFMON_COUNTERS) { + /* Clear specified L2 event counter */ + REGD(METAL_SIFIVE_CCACHE0_PMEVENTCOUNTER0 + counter * 8) = 0; + } +#endif + return; +} + +uint64_t sifive_ccache0_get_pmevent_counter(uint32_t counter) { +#if __riscv_xlen == 32 + uint32_t vh = 0, vh1 = 0, vl = 0; +#else + uint64_t val = 0; +#endif +#if METAL_SIFIVE_CCACHE0_PERFMON_COUNTERS > 0 + if (counter < METAL_SIFIVE_CCACHE0_PERFMON_COUNTERS) { + /* Set counter register offset */ + counter *= 8; + +#if __riscv_xlen == 32 + do { + vh = REGW(METAL_SIFIVE_CCACHE0_PMEVENTCOUNTER0 + counter + 4); + vl = REGW(METAL_SIFIVE_CCACHE0_PMEVENTCOUNTER0 + counter); + vh1 = REGW(METAL_SIFIVE_CCACHE0_PMEVENTCOUNTER0 + counter + 4); + } while (vh != vh1); +#else + val = REGD(METAL_SIFIVE_CCACHE0_PMEVENTCOUNTER0 + counter); +#endif + } +#endif +#if __riscv_xlen == 32 + return ((((unsigned long long)vh) << 32) | vl); +#else + return val; +#endif +} + +void sifive_ccache0_set_client_filter(uint64_t mask) { + + /* Set clients to be excluded from performance monitoring */ + REGD(METAL_SIFIVE_CCACHE0_PMCLIENTFILTER) = mask; +} + +uint64_t sifive_ccache0_get_client_filter(void) { + uint64_t val = 0; + + /* Get currently active client filter mask */ + val = REGD(METAL_SIFIVE_CCACHE0_PMCLIENTFILTER); + + return val; +} + +int sifive_ccache0_get_interrupt_id(uint32_t src) { + int ret = 0; + + if (src < (uint32_t)sizeof(sifive_ccache0_interrupts) / sizeof(int)) { + ret = sifive_ccache0_interrupts[src]; + } + + return ret; +} + +struct metal_interrupt *sifive_ccache0_interrupt_controller(void) { + return METAL_SIFIVE_CCACHE0_INTERRUPT_PARENT; +} + +#endif + +typedef int no_empty_translation_units; diff --git a/arch/riscv32/fe310/src/freedom-metal/src/drivers/sifive_clic0.c b/arch/riscv32/fe310/src/freedom-metal/src/drivers/sifive_clic0.c new file mode 100644 index 00000000..3bdc5125 --- /dev/null +++ b/arch/riscv32/fe310/src/freedom-metal/src/drivers/sifive_clic0.c @@ -0,0 +1,821 @@ +/* Copyright 2018 SiFive, Inc */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include + +#ifdef METAL_SIFIVE_CLIC0 + +#include +#include +#include +#include +#include + +#define CLIC0_MAX_INTERRUPTS 1024 + +typedef enum metal_clic_vector_ { + METAL_CLIC_NONVECTOR = 0, + METAL_CLIC_VECTORED = 1 +} metal_clic_vector; + +struct __metal_clic_cfg { + unsigned char : 1, nmbits : 2, nlbits : 4, nvbit : 1; +}; + +const struct __metal_clic_cfg __metal_clic_defaultcfg = { + .nmbits = METAL_INTR_PRIV_M_MODE, + .nlbits = 0, + .nvbit = METAL_CLIC_NONVECTOR}; + +void __metal_clic0_handler(int id, void *priv) __attribute__((aligned(64))); + +void __metal_clic0_default_vector_handler(void) + __attribute__((interrupt, aligned(64))); + +struct __metal_clic_cfg +__metal_clic0_configuration(struct __metal_driver_sifive_clic0 *clic, + struct __metal_clic_cfg *cfg) { + volatile unsigned char val; + struct __metal_clic_cfg cliccfg; + unsigned long control_base = __metal_driver_sifive_clic0_control_base( + (struct metal_interrupt *)clic); + + if (cfg) { + val = cfg->nmbits << 5 | cfg->nlbits << 1 | cfg->nvbit; + __METAL_ACCESS_ONCE( + (__metal_io_u8 *)(control_base + METAL_SIFIVE_CLIC0_MMODE_APERTURE + + METAL_SIFIVE_CLIC0_CLICCFG)) = val; + } + val = __METAL_ACCESS_ONCE( + (__metal_io_u8 *)(control_base + METAL_SIFIVE_CLIC0_MMODE_APERTURE + + METAL_SIFIVE_CLIC0_CLICCFG)); + cliccfg.nmbits = (val & METAL_SIFIVE_CLIC0_CLICCFG_NMBITS_MASK) >> 5; + cliccfg.nlbits = (val & METAL_SIFIVE_CLIC0_CLICCFG_NLBITS_MASK) >> 1; + cliccfg.nvbit = val & METAL_SIFIVE_CLIC0_CLICCFG_NVBIT_MASK; + return cliccfg; +} + +int __metal_clic0_interrupt_set_mode(struct __metal_driver_sifive_clic0 *clic, + int id, int mode) { + uint8_t mask, val; + struct __metal_clic_cfg cfg = __metal_clic0_configuration(clic, NULL); + unsigned long control_base = __metal_driver_sifive_clic0_control_base( + (struct metal_interrupt *)clic); + + if (mode >= (cfg.nmbits << 1)) { + /* Do nothing, mode request same or exceed what configured in CLIC */ + return 0; + } + + /* Mask out nmbits and retain other values */ + mask = ((uint8_t)(-1)) >> cfg.nmbits; + val = + __METAL_ACCESS_ONCE( + (__metal_io_u8 *)(control_base + METAL_SIFIVE_CLIC0_MMODE_APERTURE + + METAL_SIFIVE_CLIC0_CLICINTCTL_BASE + id)) & + mask; + __METAL_ACCESS_ONCE( + (__metal_io_u8 *)(control_base + METAL_SIFIVE_CLIC0_MMODE_APERTURE + + METAL_SIFIVE_CLIC0_CLICINTCTL_BASE + id)) = + val | (mode << (8 - cfg.nmbits)); + return 0; +} + +int __metal_clic0_interrupt_set_level(struct __metal_driver_sifive_clic0 *clic, + int id, unsigned int level) { + + uint8_t mask, nmmask, nlmask, val; + struct __metal_clic_cfg cfg = __metal_clic0_configuration(clic, NULL); + unsigned long control_base = __metal_driver_sifive_clic0_control_base( + (struct metal_interrupt *)clic); + + /* Drop the LSBs that don't fit in nlbits */ + nlmask = (uint8_t)(-1) >> (cfg.nmbits + cfg.nlbits); + nmmask = ~((uint8_t)(-1) >> (cfg.nmbits)); + mask = ~(nlmask | nmmask); + + level &= mask; + val = __METAL_ACCESS_ONCE( + (__metal_io_u8 *)(control_base + METAL_SIFIVE_CLIC0_MMODE_APERTURE + + METAL_SIFIVE_CLIC0_CLICINTCTL_BASE + id)); + val &= ~mask; + __METAL_ACCESS_ONCE( + (__metal_io_u8 *)(control_base + METAL_SIFIVE_CLIC0_MMODE_APERTURE + + METAL_SIFIVE_CLIC0_CLICINTCTL_BASE + id)) = + (val | level); + + return 0; +} + +unsigned int +__metal_clic0_interrupt_get_level(struct __metal_driver_sifive_clic0 *clic, + int id) { + int level; + uint8_t mask, val, freebits, nlbits; + struct __metal_clic_cfg cfg = __metal_clic0_configuration(clic, NULL); + unsigned long control_base = __metal_driver_sifive_clic0_control_base( + (struct metal_interrupt *)clic); + int num_intbits = + __metal_driver_sifive_clic0_num_intbits((struct metal_interrupt *)clic); + + if ((cfg.nmbits + cfg.nlbits) >= num_intbits) { + nlbits = (num_intbits >= cfg.nmbits) ? (num_intbits - cfg.nmbits) : 0; + } else { + nlbits = __METAL_MIN((num_intbits - cfg.nmbits), cfg.nlbits); + } + + mask = ((1 << nlbits) - 1) + << (METAL_CLIC_MAX_NLBITS - (cfg.nmbits + nlbits)); + freebits = ((1 << METAL_CLIC_MAX_NLBITS) - 1) >> nlbits; + + if (mask == 0) { + level = (1 << METAL_CLIC_MAX_NLBITS) - 1; + } else { + val = __METAL_ACCESS_ONCE( + (__metal_io_u8 *)(control_base + METAL_SIFIVE_CLIC0_MMODE_APERTURE + + METAL_SIFIVE_CLIC0_CLICINTCTL_BASE + id)); + val = __METAL_GET_FIELD(val, mask); + level = (val << (METAL_CLIC_MAX_NLBITS - nlbits)) | freebits; + } + + return level; +} + +int __metal_clic0_interrupt_set_priority( + struct __metal_driver_sifive_clic0 *clic, int id, int priority) { + + uint8_t mask, nlmask, val, npbits; + struct __metal_clic_cfg cfg = __metal_clic0_configuration(clic, NULL); + unsigned long control_base = __metal_driver_sifive_clic0_control_base( + (struct metal_interrupt *)clic); + int num_intbits = + __metal_driver_sifive_clic0_num_intbits((struct metal_interrupt *)clic); + + if ((cfg.nmbits + cfg.nlbits) <= num_intbits) { + npbits = num_intbits - (cfg.nmbits + cfg.nlbits); + + mask = (uint8_t)(-1) >> (cfg.nmbits + cfg.nlbits + npbits); + nlmask = ~((uint8_t)(-1) >> (cfg.nmbits + cfg.nlbits)); + mask = ~(mask | nlmask); + + priority &= mask; + val = __METAL_ACCESS_ONCE( + (__metal_io_u8 *)(control_base + METAL_SIFIVE_CLIC0_MMODE_APERTURE + + METAL_SIFIVE_CLIC0_CLICINTCTL_BASE + id)); + val &= ~mask; + __METAL_ACCESS_ONCE( + (__metal_io_u8 *)(control_base + METAL_SIFIVE_CLIC0_MMODE_APERTURE + + METAL_SIFIVE_CLIC0_CLICINTCTL_BASE + id)) = + (val | priority); + } + return 0; +} + +int __metal_clic0_interrupt_get_priority( + struct __metal_driver_sifive_clic0 *clic, int id) { + int priority; + uint8_t mask, val, freebits, nlbits; + struct __metal_clic_cfg cfg = __metal_clic0_configuration(clic, NULL); + unsigned long control_base = __metal_driver_sifive_clic0_control_base( + (struct metal_interrupt *)clic); + int num_intbits = + __metal_driver_sifive_clic0_num_intbits((struct metal_interrupt *)clic); + + if ((cfg.nmbits + cfg.nlbits) >= num_intbits) { + nlbits = num_intbits - cfg.nmbits; + } else { + nlbits = cfg.nlbits; + } + + mask = ((1 << nlbits) - 1) << (8 - (cfg.nmbits + nlbits)); + freebits = ((1 << METAL_CLIC_MAX_NLBITS) - 1) >> nlbits; + + if (mask == 0) { + priority = (1 << METAL_CLIC_MAX_NLBITS) - 1; + } else { + val = __METAL_ACCESS_ONCE( + (__metal_io_u8 *)(control_base + METAL_SIFIVE_CLIC0_MMODE_APERTURE + + METAL_SIFIVE_CLIC0_CLICINTCTL_BASE + id)); + priority = __METAL_GET_FIELD(val, freebits); + } + return priority; +} + +int __metal_clic0_interrupt_set_vector_mode( + struct __metal_driver_sifive_clic0 *clic, int id, int enable) { + uint8_t mask, val; + unsigned long control_base = __metal_driver_sifive_clic0_control_base( + (struct metal_interrupt *)clic); + int num_intbits = + __metal_driver_sifive_clic0_num_intbits((struct metal_interrupt *)clic); + + mask = 1 << (8 - num_intbits); + val = __METAL_ACCESS_ONCE( + (__metal_io_u8 *)(control_base + METAL_SIFIVE_CLIC0_MMODE_APERTURE + + METAL_SIFIVE_CLIC0_CLICINTCTL_BASE + id)); + if (enable) { + val |= mask; + } else { + val &= ~mask; + } + __METAL_ACCESS_ONCE( + (__metal_io_u8 *)(control_base + METAL_SIFIVE_CLIC0_MMODE_APERTURE + + METAL_SIFIVE_CLIC0_CLICINTCTL_BASE + id)) = val; + return 0; +} + +int __metal_clic0_interrupt_is_vectored( + struct __metal_driver_sifive_clic0 *clic, int id) { + uint8_t mask, val; + unsigned long control_base = __metal_driver_sifive_clic0_control_base( + (struct metal_interrupt *)clic); + int num_intbits = + __metal_driver_sifive_clic0_num_intbits((struct metal_interrupt *)clic); + + mask = 1 << (8 - num_intbits); + val = __METAL_ACCESS_ONCE( + (__metal_io_u8 *)(control_base + METAL_SIFIVE_CLIC0_MMODE_APERTURE + + METAL_SIFIVE_CLIC0_CLICINTCTL_BASE + id)); + return __METAL_GET_FIELD(val, mask); +} + +int __metal_clic0_interrupt_enable(struct __metal_driver_sifive_clic0 *clic, + int id) { + unsigned long control_base = __metal_driver_sifive_clic0_control_base( + (struct metal_interrupt *)clic); + int num_subinterrupts = __metal_driver_sifive_clic0_num_subinterrupts( + (struct metal_interrupt *)clic); + + __METAL_ACCESS_ONCE( + (__metal_io_u8 *)(control_base + METAL_SIFIVE_CLIC0_MMODE_APERTURE + + METAL_SIFIVE_CLIC0_CLICINTIE_BASE + id)) = + METAL_ENABLE; + return 0; +} + +int __metal_clic0_interrupt_disable(struct __metal_driver_sifive_clic0 *clic, + int id) { + unsigned long control_base = __metal_driver_sifive_clic0_control_base( + (struct metal_interrupt *)clic); + int num_subinterrupts = __metal_driver_sifive_clic0_num_subinterrupts( + (struct metal_interrupt *)clic); + + __METAL_ACCESS_ONCE( + (__metal_io_u8 *)(control_base + METAL_SIFIVE_CLIC0_MMODE_APERTURE + + METAL_SIFIVE_CLIC0_CLICINTIE_BASE + id)) = + METAL_DISABLE; + return 0; +} + +int __metal_clic0_interrupt_is_enabled(struct __metal_driver_sifive_clic0 *clic, + int id) { + unsigned long control_base = __metal_driver_sifive_clic0_control_base( + (struct metal_interrupt *)clic); + int num_subinterrupts = __metal_driver_sifive_clic0_num_subinterrupts( + (struct metal_interrupt *)clic); + + if (id >= num_subinterrupts) { + return 0; + } + return __METAL_ACCESS_ONCE( + (__metal_io_u8 *)(control_base + METAL_SIFIVE_CLIC0_MMODE_APERTURE + + METAL_SIFIVE_CLIC0_CLICINTIE_BASE + id)); +} + +int __metal_clic0_interrupt_is_pending(struct __metal_driver_sifive_clic0 *clic, + int id) { + unsigned long control_base = __metal_driver_sifive_clic0_control_base( + (struct metal_interrupt *)clic); + int num_subinterrupts = __metal_driver_sifive_clic0_num_subinterrupts( + (struct metal_interrupt *)clic); + + if (id >= num_subinterrupts) { + return 0; + } + return __METAL_ACCESS_ONCE( + (__metal_io_u8 *)(control_base + METAL_SIFIVE_CLIC0_MMODE_APERTURE + + METAL_SIFIVE_CLIC0_CLICINTIP_BASE + id)); +} + +int __metal_clic0_interrupt_set(struct __metal_driver_sifive_clic0 *clic, + int id) { + unsigned long control_base = __metal_driver_sifive_clic0_control_base( + (struct metal_interrupt *)clic); + int num_subinterrupts = __metal_driver_sifive_clic0_num_subinterrupts( + (struct metal_interrupt *)clic); + + if (id < num_subinterrupts) { + __METAL_ACCESS_ONCE( + (__metal_io_u8 *)(control_base + METAL_SIFIVE_CLIC0_MMODE_APERTURE + + METAL_SIFIVE_CLIC0_CLICINTIP_BASE + id)) = + METAL_ENABLE; + } + return 0; +} + +int __metal_clic0_interrupt_clear(struct __metal_driver_sifive_clic0 *clic, + int id) { + unsigned long control_base = __metal_driver_sifive_clic0_control_base( + (struct metal_interrupt *)clic); + int num_subinterrupts = __metal_driver_sifive_clic0_num_subinterrupts( + (struct metal_interrupt *)clic); + + if (id < num_subinterrupts) { + __METAL_ACCESS_ONCE( + (__metal_io_u8 *)(control_base + METAL_SIFIVE_CLIC0_MMODE_APERTURE + + METAL_SIFIVE_CLIC0_CLICINTIP_BASE + id)) = + METAL_DISABLE; + } + return 0; +} + +int __metal_clic0_configure_set_vector_mode( + struct __metal_driver_sifive_clic0 *clic, metal_vector_mode mode) { + struct __metal_clic_cfg cfg = __metal_clic0_configuration(clic, NULL); + + switch (mode) { + case METAL_SELECTIVE_NONVECTOR_MODE: + cfg.nvbit = METAL_CLIC_NONVECTOR; + __metal_controller_interrupt_vector(mode, &clic->metal_mtvt_table); + break; + case METAL_SELECTIVE_VECTOR_MODE: + cfg.nvbit = METAL_CLIC_VECTORED; + __metal_controller_interrupt_vector(mode, &clic->metal_mtvt_table); + break; + case METAL_HARDWARE_VECTOR_MODE: + cfg.nvbit = METAL_CLIC_VECTORED; + __metal_controller_interrupt_vector(mode, &clic->metal_mtvt_table); + break; + default: + return -1; + } + __metal_clic0_configuration(clic, &cfg); + return 0; +} + +metal_vector_mode __metal_clic0_configure_get_vector_mode( + struct __metal_driver_sifive_clic0 *clic) { + struct __metal_clic_cfg cfg = __metal_clic0_configuration(clic, NULL); + metal_vector_mode mode = __metal_controller_interrupt_vector_mode(); + + if (mode == METAL_SELECTIVE_VECTOR_MODE) { + if (cfg.nvbit) { + return METAL_SELECTIVE_VECTOR_MODE; + } else { + return METAL_SELECTIVE_NONVECTOR_MODE; + } + } else { + return mode; + } +} + +int __metal_clic0_configure_set_privilege( + struct __metal_driver_sifive_clic0 *clic, metal_intr_priv_mode priv) { + struct __metal_clic_cfg cfg = __metal_clic0_configuration(clic, NULL); + + cfg.nmbits = priv; + __metal_clic0_configuration(clic, &cfg); + return 0; +} + +metal_intr_priv_mode __metal_clic0_configure_get_privilege( + struct __metal_driver_sifive_clic0 *clic) { + struct __metal_clic_cfg cfg = __metal_clic0_configuration(clic, NULL); + + return cfg.nmbits; +} + +int __metal_clic0_configure_set_level(struct __metal_driver_sifive_clic0 *clic, + int level) { + struct __metal_clic_cfg cfg = __metal_clic0_configuration(clic, NULL); + + cfg.nlbits = level & 0xF; + __metal_clic0_configuration(clic, &cfg); + return 0; +} + +int __metal_clic0_configure_get_level( + struct __metal_driver_sifive_clic0 *clic) { + struct __metal_clic_cfg cfg = __metal_clic0_configuration(clic, NULL); + + return cfg.nlbits; +} + +unsigned long long +__metal_clic0_mtime_get(struct __metal_driver_sifive_clic0 *clic) { + __metal_io_u32 lo, hi; + unsigned long control_base = __metal_driver_sifive_clic0_control_base( + (struct metal_interrupt *)clic); + + /* Guard against rollover when reading */ + do { + hi = __METAL_ACCESS_ONCE( + (__metal_io_u32 *)(control_base + METAL_SIFIVE_CLIC0_MTIME + 4)); + lo = __METAL_ACCESS_ONCE( + (__metal_io_u32 *)(control_base + METAL_SIFIVE_CLIC0_MTIME)); + } while (__METAL_ACCESS_ONCE((__metal_io_u32 *)(control_base + + METAL_SIFIVE_CLIC0_MTIME + + 4)) != hi); + + return (((unsigned long long)hi) << 32) | lo; +} + +int __metal_driver_sifive_clic0_mtimecmp_set(struct metal_interrupt *controller, + int hartid, + unsigned long long time) { + struct __metal_driver_sifive_clic0 *clic = + (struct __metal_driver_sifive_clic0 *)(controller); + + unsigned long control_base = __metal_driver_sifive_clic0_control_base( + (struct metal_interrupt *)clic); + /* Per spec, the RISC-V MTIME/MTIMECMP registers are 64 bit, + * and are NOT internally latched for multiword transfers. + * Need to be careful about sequencing to avoid triggering + * spurious interrupts: For that set the high word to a max + * value first. + */ + __METAL_ACCESS_ONCE((__metal_io_u32 *)(control_base + (8 * hartid) + + METAL_SIFIVE_CLIC0_MTIMECMP_BASE + + 4)) = 0xFFFFFFFF; + __METAL_ACCESS_ONCE((__metal_io_u32 *)(control_base + (8 * hartid) + + METAL_SIFIVE_CLIC0_MTIMECMP_BASE)) = + (__metal_io_u32)time; + __METAL_ACCESS_ONCE((__metal_io_u32 *)(control_base + (8 * hartid) + + METAL_SIFIVE_CLIC0_MTIMECMP_BASE + + 4)) = (__metal_io_u32)(time >> 32); + return 0; +} + +void __metal_clic0_handler(int id, void *priv) { + struct __metal_driver_sifive_clic0 *clic = priv; + int num_subinterrupts = __metal_driver_sifive_clic0_num_subinterrupts( + (struct metal_interrupt *)clic); + + if ((id < num_subinterrupts) && (clic->metal_exint_table[id].handler)) { + clic->metal_exint_table[id].handler( + id, clic->metal_exint_table[id].exint_data); + } +} + +void __metal_clic0_default_handler(int id, void *priv) { metal_shutdown(300); } + +void __metal_clic0_default_vector_handler(void) { metal_shutdown(400); } + +void __metal_driver_sifive_clic0_init(struct metal_interrupt *controller) { + struct __metal_driver_sifive_clic0 *clic = + (struct __metal_driver_sifive_clic0 *)(controller); + + if (!clic->init_done) { + int level, max_levels, line, num_interrupts, num_subinterrupts; + struct __metal_clic_cfg cfg = __metal_clic_defaultcfg; + struct metal_interrupt *intc = + __metal_driver_sifive_clic0_interrupt_parent(controller); + + /* Initialize ist parent controller, aka cpu_intc. */ + intc->vtable->interrupt_init(intc); + __metal_controller_interrupt_vector(METAL_SELECTIVE_NONVECTOR_MODE, + &clic->metal_mtvt_table); + + /* + * Register its interrupts with with parent controller, + * aka sw, timer and ext to its default isr + */ + num_interrupts = __metal_driver_sifive_clic0_num_interrupts(controller); + for (int i = 0; i < num_interrupts; i++) { + line = __metal_driver_sifive_clic0_interrupt_lines(controller, i); + intc->vtable->interrupt_register(intc, line, NULL, clic); + } + + /* Default CLIC mode to per dts */ + max_levels = __metal_driver_sifive_clic0_max_levels(controller); + cfg.nlbits = (max_levels > METAL_CLIC_MAX_NLBITS) + ? METAL_CLIC_MAX_NLBITS + : max_levels; + __metal_clic0_configuration(clic, &cfg); + + level = (1 << cfg.nlbits) - 1; + num_subinterrupts = + __metal_driver_sifive_clic0_num_subinterrupts(controller); + clic->metal_mtvt_table[0] = &__metal_clic0_handler; + __metal_clic0_interrupt_disable(clic, 0); + __metal_clic0_interrupt_set_level(clic, 0, level); + for (int i = 1; i < CLIC0_MAX_INTERRUPTS; i++) { + if (i < num_subinterrupts) { + clic->metal_mtvt_table[i] = NULL; + clic->metal_exint_table[i].handler = NULL; + clic->metal_exint_table[i].sub_int = NULL; + clic->metal_exint_table[i].exint_data = NULL; + __metal_clic0_interrupt_set_level(clic, i, level); + } + __metal_clic0_interrupt_disable(clic, i); + } + clic->init_done = 1; + } +} + +int __metal_driver_sifive_clic0_register(struct metal_interrupt *controller, + int id, metal_interrupt_handler_t isr, + void *priv) { + int rc = -1; + int num_subinterrupts; + struct __metal_driver_sifive_clic0 *clic = + (struct __metal_driver_sifive_clic0 *)(controller); + struct metal_interrupt *intc = + __metal_driver_sifive_clic0_interrupt_parent(controller); + metal_vector_mode mode = __metal_clic0_configure_get_vector_mode(clic); + + if (((mode == METAL_SELECTIVE_VECTOR_MODE) && + (__metal_clic0_interrupt_is_vectored(clic, id))) || + (mode == METAL_HARDWARE_VECTOR_MODE) || (mode == METAL_VECTOR_MODE) || + (mode == METAL_DIRECT_MODE)) { + return rc; + } + + /* Register its interrupts with parent controller */ + if (id < METAL_INTERRUPT_ID_CSW) { + return intc->vtable->interrupt_register(intc, id, isr, priv); + } + + /* + * CLIC (sub-interrupts) devices interrupts start at 16 but offset from 0 + * Reset the IDs to reflects this. + */ + num_subinterrupts = + __metal_driver_sifive_clic0_num_subinterrupts(controller); + if (id < num_subinterrupts) { + if (isr) { + clic->metal_exint_table[id].handler = isr; + clic->metal_exint_table[id].exint_data = priv; + } else { + clic->metal_exint_table[id].handler = __metal_clic0_default_handler; + clic->metal_exint_table[id].sub_int = priv; + } + rc = 0; + } + return rc; +} + +int __metal_driver_sifive_clic0_vector_register( + struct metal_interrupt *controller, int id, + metal_interrupt_vector_handler_t isr, void *priv) { + int rc = -1; + struct __metal_driver_sifive_clic0 *clic = + (struct __metal_driver_sifive_clic0 *)(controller); + struct metal_interrupt *intc = + __metal_driver_sifive_clic0_interrupt_parent(controller); + int num_subinterrupts = + __metal_driver_sifive_clic0_num_subinterrupts(controller); + metal_vector_mode mode = __metal_clic0_configure_get_vector_mode(clic); + + if ((mode != METAL_SELECTIVE_VECTOR_MODE) && + (mode != METAL_HARDWARE_VECTOR_MODE)) { + return rc; + } + if ((mode == METAL_SELECTIVE_VECTOR_MODE) && + (__metal_clic0_interrupt_is_vectored(clic, id) == 0)) { + return rc; + } + if (id < num_subinterrupts) { + if (isr) { + clic->metal_mtvt_table[id] = isr; + clic->metal_exint_table[id].exint_data = priv; + } else { + clic->metal_mtvt_table[id] = __metal_clic0_default_vector_handler; + clic->metal_exint_table[id].sub_int = priv; + } + rc = 0; + } + return rc; +} + +int __metal_driver_sifive_clic0_enable(struct metal_interrupt *controller, + int id) { + struct __metal_driver_sifive_clic0 *clic = + (struct __metal_driver_sifive_clic0 *)(controller); + return __metal_clic0_interrupt_enable(clic, id); +} + +int __metal_driver_sifive_clic0_disable(struct metal_interrupt *controller, + int id) { + struct __metal_driver_sifive_clic0 *clic = + (struct __metal_driver_sifive_clic0 *)(controller); + return __metal_clic0_interrupt_disable(clic, id); +} + +int __metal_driver_sifive_clic0_enable_interrupt_vector( + struct metal_interrupt *controller, int id) { + int rc = -1; + int num_subinterrupts = + __metal_driver_sifive_clic0_num_subinterrupts(controller); + struct __metal_driver_sifive_clic0 *clic = + (struct __metal_driver_sifive_clic0 *)(controller); + metal_vector_mode mode = __metal_clic0_configure_get_vector_mode(clic); + + if ((mode != METAL_SELECTIVE_VECTOR_MODE) && + (mode != METAL_HARDWARE_VECTOR_MODE)) { + return rc; + } + if (id < num_subinterrupts) { + __metal_clic0_interrupt_set_vector_mode(clic, id, METAL_ENABLE); + return 0; + } + return -1; +} + +int __metal_driver_sifive_clic0_disable_interrupt_vector( + struct metal_interrupt *controller, int id) { + int num_subinterrupts; + struct __metal_driver_sifive_clic0 *clic = + (struct __metal_driver_sifive_clic0 *)(controller); + + num_subinterrupts = + __metal_driver_sifive_clic0_num_subinterrupts(controller); + if (id < num_subinterrupts) { + __metal_clic0_interrupt_set_vector_mode(clic, id, METAL_DISABLE); + return 0; + } + return -1; +} + +metal_vector_mode __metal_driver_sifive_clic0_get_vector_mode( + struct metal_interrupt *controller) { + struct __metal_driver_sifive_clic0 *clic = + (struct __metal_driver_sifive_clic0 *)(controller); + return __metal_clic0_configure_get_vector_mode(clic); +} + +int __metal_driver_sifive_clic0_set_vector_mode( + struct metal_interrupt *controller, metal_vector_mode mode) { + struct __metal_driver_sifive_clic0 *clic = + (struct __metal_driver_sifive_clic0 *)(controller); + return __metal_clic0_configure_set_vector_mode(clic, mode); +} + +metal_intr_priv_mode +__metal_driver_sifive_clic0_get_privilege(struct metal_interrupt *controller) { + struct __metal_driver_sifive_clic0 *clic = + (struct __metal_driver_sifive_clic0 *)(controller); + return __metal_clic0_configure_get_privilege(clic); +} + +int __metal_driver_sifive_clic0_set_privilege( + struct metal_interrupt *controller, metal_intr_priv_mode priv) { + struct __metal_driver_sifive_clic0 *clic = + (struct __metal_driver_sifive_clic0 *)(controller); + return __metal_clic0_configure_set_privilege(clic, priv); +} + +unsigned int +__metal_driver_sifive_clic0_get_threshold(struct metal_interrupt *controller) { + struct __metal_driver_sifive_clic0 *clic = + (struct __metal_driver_sifive_clic0 *)(controller); + return __metal_clic0_configure_get_level(clic); +} + +int __metal_driver_sifive_clic0_set_threshold( + struct metal_interrupt *controller, unsigned int level) { + struct __metal_driver_sifive_clic0 *clic = + (struct __metal_driver_sifive_clic0 *)(controller); + return __metal_clic0_configure_set_level(clic, level); +} + +unsigned int +__metal_driver_sifive_clic0_get_priority(struct metal_interrupt *controller, + int id) { + struct __metal_driver_sifive_clic0 *clic = + (struct __metal_driver_sifive_clic0 *)(controller); + return __metal_clic0_interrupt_get_priority(clic, id); +} + +int __metal_driver_sifive_clic0_set_priority(struct metal_interrupt *controller, + int id, unsigned int priority) { + struct __metal_driver_sifive_clic0 *clic = + (struct __metal_driver_sifive_clic0 *)(controller); + return __metal_clic0_interrupt_set_priority(clic, id, priority); +} + +unsigned int __metal_driver_sifive_clic0_get_preemptive_level( + struct metal_interrupt *controller, int id) { + struct __metal_driver_sifive_clic0 *clic = + (struct __metal_driver_sifive_clic0 *)(controller); + return __metal_clic0_interrupt_get_level(clic, id); +} + +int __metal_driver_sifive_clic0_set_preemptive_level( + struct metal_interrupt *controller, int id, unsigned int level) { + struct __metal_driver_sifive_clic0 *clic = + (struct __metal_driver_sifive_clic0 *)(controller); + __metal_clic0_interrupt_set_level(clic, id, level); + return (__metal_clic0_interrupt_set_priority(clic, id, level)); +} + +int __metal_driver_sifive_clic0_clear_interrupt( + struct metal_interrupt *controller, int id) { + struct __metal_driver_sifive_clic0 *clic = + (struct __metal_driver_sifive_clic0 *)(controller); + return __metal_clic0_interrupt_clear(clic, id); +} + +int __metal_driver_sifive_clic0_set_interrupt( + struct metal_interrupt *controller, int id) { + struct __metal_driver_sifive_clic0 *clic = + (struct __metal_driver_sifive_clic0 *)(controller); + return __metal_clic0_interrupt_set(clic, id); +} + +int __metal_driver_sifive_clic0_command_request( + struct metal_interrupt *controller, int command, void *data) { + int hartid; + int rc = -1; + struct __metal_driver_sifive_clic0 *clic = + (struct __metal_driver_sifive_clic0 *)(controller); + unsigned long control_base = + __metal_driver_sifive_clic0_control_base(controller); + + switch (command) { + case METAL_TIMER_MTIME_GET: + if (data) { + *(unsigned long long *)data = __metal_clic0_mtime_get(clic); + rc = 0; + } + break; + case METAL_SOFTWARE_IPI_CLEAR: + if (data) { + hartid = *(int *)data; + __METAL_ACCESS_ONCE(( + __metal_io_u32 *)(control_base + (hartid * 4))) = METAL_DISABLE; + __METAL_ACCESS_ONCE( + (__metal_io_u8 *)(control_base + + METAL_SIFIVE_CLIC0_MMODE_APERTURE + + METAL_SIFIVE_CLIC0_CLICINTIP_BASE)) = + METAL_DISABLE; + rc = 0; + } + break; + case METAL_SOFTWARE_IPI_SET: + if (data) { + hartid = *(int *)data; + __METAL_ACCESS_ONCE( + (__metal_io_u32 *)(control_base + (hartid * 4))) = METAL_ENABLE; + __METAL_ACCESS_ONCE( + (__metal_io_u8 *)(control_base + + METAL_SIFIVE_CLIC0_MMODE_APERTURE + + METAL_SIFIVE_CLIC0_CLICINTIP_BASE)) = + METAL_ENABLE; + rc = 0; + } + break; + case METAL_SOFTWARE_MSIP_GET: + rc = 0; + if (data) { + hartid = *(int *)data; + rc = __METAL_ACCESS_ONCE( + (__metal_io_u32 *)(control_base + (hartid * 4))); + } + break; + default: + break; + } + + return rc; +} +__METAL_DEFINE_VTABLE(__metal_driver_vtable_sifive_clic0) = { + .clic_vtable.interrupt_init = __metal_driver_sifive_clic0_init, + .clic_vtable.interrupt_register = __metal_driver_sifive_clic0_register, + .clic_vtable.interrupt_vector_register = + __metal_driver_sifive_clic0_vector_register, + .clic_vtable.interrupt_enable = __metal_driver_sifive_clic0_enable, + .clic_vtable.interrupt_disable = __metal_driver_sifive_clic0_disable, + .clic_vtable.interrupt_vector_enable = + __metal_driver_sifive_clic0_enable_interrupt_vector, + .clic_vtable.interrupt_vector_disable = + __metal_driver_sifive_clic0_disable_interrupt_vector, + .clic_vtable.interrupt_get_vector_mode = + __metal_driver_sifive_clic0_get_vector_mode, + .clic_vtable.interrupt_set_vector_mode = + __metal_driver_sifive_clic0_set_vector_mode, + .clic_vtable.interrupt_get_privilege = + __metal_driver_sifive_clic0_get_privilege, + .clic_vtable.interrupt_set_privilege = + __metal_driver_sifive_clic0_set_privilege, + .clic_vtable.interrupt_get_threshold = + __metal_driver_sifive_clic0_get_threshold, + .clic_vtable.interrupt_set_threshold = + __metal_driver_sifive_clic0_set_threshold, + .clic_vtable.interrupt_get_priority = + __metal_driver_sifive_clic0_get_priority, + .clic_vtable.interrupt_set_priority = + __metal_driver_sifive_clic0_set_priority, + .clic_vtable.interrupt_get_preemptive_level = + __metal_driver_sifive_clic0_get_preemptive_level, + .clic_vtable.interrupt_set_preemptive_level = + __metal_driver_sifive_clic0_set_preemptive_level, + .clic_vtable.interrupt_clear = __metal_driver_sifive_clic0_clear_interrupt, + .clic_vtable.interrupt_set = __metal_driver_sifive_clic0_set_interrupt, + .clic_vtable.command_request = __metal_driver_sifive_clic0_command_request, + .clic_vtable.mtimecmp_set = __metal_driver_sifive_clic0_mtimecmp_set, +}; + +#endif /* METAL_SIFIVE_CLIC0 */ + +typedef int no_empty_translation_units; diff --git a/arch/riscv32/fe310/src/freedom-metal/src/drivers/sifive_fe310-g000_hfrosc.c b/arch/riscv32/fe310/src/freedom-metal/src/drivers/sifive_fe310-g000_hfrosc.c new file mode 100644 index 00000000..d4df0402 --- /dev/null +++ b/arch/riscv32/fe310/src/freedom-metal/src/drivers/sifive_fe310-g000_hfrosc.c @@ -0,0 +1,46 @@ +/* Copyright 2018 SiFive, Inc */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include + +#ifdef METAL_SIFIVE_FE310_G000_HFROSC + +#include +#include + +#define CONFIG_DIVIDER 0x0000003FUL +#define CONFIG_TRIM 0x001F0000UL +#define CONFIG_ENABLE 0x40000000UL +#define CONFIG_READY 0x80000000UL + +long __metal_driver_sifive_fe310_g000_hfrosc_get_rate_hz( + const struct metal_clock *clock) { + struct metal_clock *ref = + __metal_driver_sifive_fe310_g000_hfrosc_ref(clock); + long config_offset = + __metal_driver_sifive_fe310_g000_hfrosc_config_offset(clock); + struct __metal_driver_sifive_fe310_g000_prci *config_base = + __metal_driver_sifive_fe310_g000_hfrosc_config_base(clock); + const struct __metal_driver_vtable_sifive_fe310_g000_prci *vtable = + __metal_driver_sifive_fe310_g000_prci_vtable(); + long cfg = vtable->get_reg(config_base, config_offset); + + if ((cfg & CONFIG_ENABLE) == 0) + return -1; + if ((cfg & CONFIG_READY) == 0) + return -1; + return metal_clock_get_rate_hz(ref) / ((cfg & CONFIG_DIVIDER) + 1); +} + +long __metal_driver_sifive_fe310_g000_hfrosc_set_rate_hz( + struct metal_clock *clock, long rate) { + return __metal_driver_sifive_fe310_g000_hfrosc_get_rate_hz(clock); +} + +__METAL_DEFINE_VTABLE(__metal_driver_vtable_sifive_fe310_g000_hfrosc) = { + .clock.get_rate_hz = &__metal_driver_sifive_fe310_g000_hfrosc_get_rate_hz, + .clock.set_rate_hz = &__metal_driver_sifive_fe310_g000_hfrosc_set_rate_hz, +}; +#endif /* METAL_SIFIVE_FE310_G000_HFROSC */ + +typedef int no_empty_translation_units; diff --git a/arch/riscv32/fe310/src/freedom-metal/src/drivers/sifive_fe310-g000_hfxosc.c b/arch/riscv32/fe310/src/freedom-metal/src/drivers/sifive_fe310-g000_hfxosc.c new file mode 100644 index 00000000..b8a9c6dd --- /dev/null +++ b/arch/riscv32/fe310/src/freedom-metal/src/drivers/sifive_fe310-g000_hfxosc.c @@ -0,0 +1,45 @@ +/* Copyright 2018 SiFive, Inc */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include + +#ifdef METAL_SIFIVE_FE310_G000_HFXOSC + +#include +#include + +#define CONFIG_ENABLE 0x40000000UL +#define CONFIG_READY 0x80000000UL + +long __metal_driver_sifive_fe310_g000_hfxosc_get_rate_hz( + const struct metal_clock *clock) { + struct metal_clock *ref = + __metal_driver_sifive_fe310_g000_hfxosc_ref(clock); + long config_offset = + __metal_driver_sifive_fe310_g000_hfxosc_config_offset(clock); + struct __metal_driver_sifive_fe310_g000_prci *config_base = + __metal_driver_sifive_fe310_g000_hfxosc_config_base(clock); + const struct __metal_driver_vtable_sifive_fe310_g000_prci *vtable = + __metal_driver_sifive_fe310_g000_prci_vtable(); + long cfg = vtable->get_reg(config_base, config_offset); + + if ((cfg & CONFIG_ENABLE) == 0) + return -1; + if ((cfg & CONFIG_READY) == 0) + return -1; + return metal_clock_get_rate_hz(ref); +} + +long __metal_driver_sifive_fe310_g000_hfxosc_set_rate_hz( + struct metal_clock *clock, long rate) { + return __metal_driver_sifive_fe310_g000_hfxosc_get_rate_hz(clock); +} + +__METAL_DEFINE_VTABLE(__metal_driver_vtable_sifive_fe310_g000_hfxosc) = { + .clock.get_rate_hz = __metal_driver_sifive_fe310_g000_hfxosc_get_rate_hz, + .clock.set_rate_hz = __metal_driver_sifive_fe310_g000_hfxosc_set_rate_hz, +}; + +#endif /* METAL_SIFIVE_FE310_G000_HFXOSC */ + +typedef int no_empty_translation_units; diff --git a/arch/riscv32/fe310/src/freedom-metal/src/drivers/sifive_fe310-g000_lfrosc.c b/arch/riscv32/fe310/src/freedom-metal/src/drivers/sifive_fe310-g000_lfrosc.c new file mode 100644 index 00000000..926ad9de --- /dev/null +++ b/arch/riscv32/fe310/src/freedom-metal/src/drivers/sifive_fe310-g000_lfrosc.c @@ -0,0 +1,57 @@ +/* Copyright 2019 SiFive, Inc */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include + +#ifdef METAL_SIFIVE_FE310_G000_LFROSC + +#include +#include + +/* LFROSCCFG */ +#define METAL_LFROSCCFG_DIV_MASK 0x3F +#define METAL_LFROSCCFG_TRIM_SHIFT 16 +#define METAL_LFROSCCFG_TRIM_MASK (0x1F << METAL_LFROSCCFG_TRIM_SHIFT) +#define METAL_LFROSCCFG_EN (1 << 30) +#define METAL_LFROSCCFG_RDY (1 << 31) + +/* LFCLKMUX */ +#define METAL_LFCLKMUX_SEL 1 +#define METAL_LFCLKMUX_EXT_MUX_STATUS (1 << 31) + +#define LFROSC_REGW(addr) (__METAL_ACCESS_ONCE((__metal_io_u32 *)addr)) + +long __metal_driver_sifive_fe310_g000_lfrosc_get_rate_hz( + const struct metal_clock *clock) { + struct metal_clock *internal_ref = + __metal_driver_sifive_fe310_g000_lfrosc_lfrosc(clock); + struct metal_clock *external_ref = + __metal_driver_sifive_fe310_g000_lfrosc_psdlfaltclk(clock); + + unsigned long int cfg_reg = + __metal_driver_sifive_fe310_g000_lfrosc_config_reg(clock); + unsigned long int mux_reg = + __metal_driver_sifive_fe310_g000_lfrosc_mux_reg(clock); + + if (LFROSC_REGW(mux_reg) & METAL_LFCLKMUX_EXT_MUX_STATUS) { + return metal_clock_get_rate_hz(external_ref); + } + + const unsigned long int div = + (LFROSC_REGW(cfg_reg) & METAL_LFROSCCFG_DIV_MASK) + 1; + + return metal_clock_get_rate_hz(internal_ref) / div; +} + +long __metal_driver_sifive_fe310_g000_lfrosc_set_rate_hz( + struct metal_clock *clock, long rate) { + return __metal_driver_sifive_fe310_g000_lfrosc_get_rate_hz(clock); +} + +__METAL_DEFINE_VTABLE(__metal_driver_vtable_sifive_fe310_g000_lfrosc) = { + .clock.get_rate_hz = &__metal_driver_sifive_fe310_g000_lfrosc_get_rate_hz, + .clock.set_rate_hz = &__metal_driver_sifive_fe310_g000_lfrosc_set_rate_hz, +}; +#endif /* METAL_SIFIVE_FE310_G000_LFROSC */ + +typedef int no_empty_translation_units; diff --git a/arch/riscv32/fe310/src/freedom-metal/src/drivers/sifive_fe310-g000_pll.c b/arch/riscv32/fe310/src/freedom-metal/src/drivers/sifive_fe310-g000_pll.c new file mode 100644 index 00000000..5f26d32d --- /dev/null +++ b/arch/riscv32/fe310/src/freedom-metal/src/drivers/sifive_fe310-g000_pll.c @@ -0,0 +1,404 @@ +/* Copyright 2018 SiFive, Inc */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include + +#ifdef METAL_SIFIVE_FE310_G000_PLL + +#include +#include + +#include +#include +#include + +#include +#include + +#define PLL_R 0x00000007UL +#define PLL_F 0x000003F0UL +#define PLL_Q 0x00000C00UL +#define PLL_SEL 0x00010000UL +#define PLL_REFSEL 0x00020000UL +#define PLL_BYPASS 0x00040000UL +#define PLL_LOCK 0x80000000UL + +#define DIV_DIV 0x0000003FUL +#define DIV_1 0x00000100UL + +#define PLL_R_SHIFT(r) ((r << 0) & PLL_R) +#define PLL_F_SHIFT(f) ((f << 4) & PLL_F) +#define PLL_Q_SHIFT(q) ((q << 10) & PLL_Q) +#define PLL_DIV_SHIFT(d) ((d << 0) & DIV_DIV) + +struct pll_config_t { + unsigned long multiplier; + unsigned long divisor; + unsigned long min_input_rate; + unsigned long max_input_rate; + unsigned long r; + unsigned long f; + unsigned long q; + long d; /* < 0 if disabled */ +}; + +static const struct pll_config_t pll_configs[] = { + /* + * multiplier + * ^ divisor + * | ^ min_input_rate + * | | ^ max_input_rate + * | | | ^ r + * | | | | ^ f + * | | | | | ^ q + * | | | | | | ^ d + * | | | | | | | ^ + * | | | | | | | | */ + {1, 32, 12000000, 24000000, 1, 31, 3, 63}, + {1, 32, 24000000, 48000000, 3, 31, 2, 63}, + {1, 16, 6000000, 12000000, 0, 31, 3, 63}, + {1, 16, 12000000, 24000000, 1, 31, 2, 63}, + {1, 16, 24000000, 48000000, 3, 31, 2, 31}, + {1, 8, 6000000, 12000000, 0, 31, 3, 31}, + {1, 8, 12000000, 24000000, 1, 31, 2, 31}, + {1, 8, 24000000, 48000000, 3, 31, 2, 15}, + {1, 4, 6000000, 12000000, 0, 31, 3, 15}, + {1, 4, 12000000, 24000000, 1, 31, 2, 15}, + {1, 4, 24000000, 48000000, 3, 31, 2, 7}, + {1, 2, 6000000, 12000000, 0, 31, 2, 15}, + {1, 2, 12000000, 24000000, 1, 31, 1, 15}, + {1, 2, 24000000, 48000000, 3, 31, 1, 7}, + {2, 1, 6000000, 12000000, 0, 31, 1, 7}, + {2, 1, 12000000, 24000000, 1, 31, 1, 3}, + {2, 1, 24000000, 48000000, 3, 31, 3, -1}, + {4, 1, 6000000, 12000000, 0, 31, 3, 0}, + {4, 1, 12000000, 24000000, 1, 31, 3, -1}, + {4, 1, 24000000, 48000000, 3, 31, 2, -1}, + {6, 1, 6000000, 10666666, 0, 35, 1, 2}, + {6, 1, 10666666, 12000000, 0, 23, 3, -1}, + {6, 1, 12000000, 16000000, 1, 47, 3, -1}, + {6, 1, 16000000, 18000000, 1, 23, 2, -1}, + {6, 1, 18000000, 21333333, 2, 35, 2, -1}, + {8, 1, 6000000, 12000000, 0, 31, 3, -1}, + {8, 1, 12000000, 24000000, 1, 31, 2, -1}, + {8, 1, 24000000, 48000000, 3, 31, 1, -1}, + {10, 1, 6000000, 9600000, 0, 39, 3, -1}, + {10, 1, 9600000, 12000000, 0, 19, 2, -1}, + {10, 1, 12000000, 19200000, 1, 39, 2, -1}, + {10, 1, 19200000, 24000000, 1, 19, 1, -1}, + {10, 1, 24000000, 38400000, 3, 39, 1, -1}, + {12, 1, 6000000, 8000000, 0, 47, 3, -1}, + {12, 1, 8000000, 12000000, 0, 23, 2, -1}, + {12, 1, 12000000, 16000000, 1, 47, 2, -1}, + {12, 1, 16000000, 24000000, 1, 23, 1, -1}, + {12, 1, 24000000, 30000000, 3, 47, 1, -1}, + {12, 1, 30000000, 32000000, 3, 47, 1, -1}, + {14, 1, 6000000, 6857142, 0, 55, 3, -1}, + {14, 1, 6857143, 12000000, 0, 27, 2, -1}, + {14, 1, 12000000, 13714285, 1, 55, 2, -1}, + {14, 1, 13714286, 24000000, 1, 27, 1, -1}, + {14, 1, 24000000, 27428571, 3, 55, 1, -1}, + {16, 1, 6000000, 12000000, 0, 31, 2, -1}, + {16, 1, 12000000, 24000000, 1, 31, 1, -1}, + {18, 1, 6000000, 10666666, 0, 35, 2, -1}, + {18, 1, 10666667, 12000000, 0, 17, 1, -1}, + {18, 1, 12000000, 21333333, 1, 35, 1, -1}, + {20, 1, 6000000, 9600000, 0, 39, 2, -1}, + {20, 1, 9600000, 12000000, 0, 19, 1, -1}, + {20, 1, 12000000, 19200000, 1, 39, 1, -1}, + {22, 1, 6000000, 8727272, 0, 43, 2, -1}, + {22, 1, 8727273, 12000000, 0, 21, 1, -1}, + {22, 1, 12000000, 17454545, 1, 43, 1, -1}, + {24, 1, 6000000, 8000000, 0, 47, 2, -1}, + {24, 1, 8000000, 12000000, 0, 23, 1, -1}, + {24, 1, 12000000, 16000000, 1, 47, 1, -1}, + {26, 1, 6000000, 7384615, 0, 51, 2, -1}, + {26, 1, 7384616, 12000000, 0, 25, 1, -1}, + {26, 1, 12000000, 14768230, 1, 51, 1, -1}, + {28, 1, 6000000, 6857142, 0, 55, 2, -1}, + {28, 1, 6857143, 12000000, 0, 27, 1, -1}, + {28, 1, 12000000, 13714285, 1, 55, 1, -1}, + {30, 1, 6000000, 6400000, 0, 59, 2, -1}, + {30, 1, 6400000, 12000000, 0, 29, 1, -1}, + {30, 1, 12000000, 12800000, 1, 59, 1, -1}, + {32, 1, 6000000, 12000000, 0, 31, 1, -1}}; + +#define PLL_CONFIG_NOT_VALID -1 + +void __metal_driver_sifive_fe310_g000_pll_init( + struct __metal_driver_sifive_fe310_g000_pll *pll); + +/* Given the rate of the PLL input frequency and a PLL configuration, what + * will the resulting PLL output frequency be? + * Arguments: + * - pll_input_rate the PLL input frequency in hertz + * - config the PLL configuration + * Returns: + * - PLL_CONFIG_NOT_VALID if the configuration is not valid for the input + * frequency + * - the output frequency, in hertz */ +static long get_pll_config_freq(unsigned long pll_input_rate, + const struct pll_config_t *config) { + if (pll_input_rate < config->min_input_rate || + pll_input_rate > config->max_input_rate) + return PLL_CONFIG_NOT_VALID; + + return pll_input_rate * config->multiplier / config->divisor; +} + +#ifdef __METAL_DT_SIFIVE_FE310_G000_PLL_HANDLE + +METAL_CONSTRUCTOR(metal_sifive_fe310_g000_pll_init) { + long init_rate = __metal_driver_sifive_fe310_g000_pll_init_rate(); + /* If the PLL init_rate is zero, don't initialize the PLL */ + if (init_rate != 0) + __metal_driver_sifive_fe310_g000_pll_init( + __METAL_DT_SIFIVE_FE310_G000_PLL_HANDLE); +} + +#endif /* __METAL_DT_SIFIVE_FE310_G000__PLL_HANDLE */ + +void __metal_driver_sifive_fe310_g000_pll_init( + struct __metal_driver_sifive_fe310_g000_pll *pll) { + struct metal_clock *pllref = + __metal_driver_sifive_fe310_g000_pll_pllref(&(pll->clock)); + long init_rate = __metal_driver_sifive_fe310_g000_pll_init_rate(); + long config_offset = __metal_driver_sifive_fe310_g000_pll_config_offset(); + long base = __metal_driver_sifive_fe310_g000_prci_base(); + + __metal_io_u32 *pllcfg = (__metal_io_u32 *)(base + config_offset); + + /* If the PLL clock has had a _pre_rate_change_callback configured, call it + */ + _metal_clock_call_all_callbacks(pll->clock._pre_rate_change_callback); + + /* If we're running off of the PLL, switch off before we start configuring + * it*/ + if ((__METAL_ACCESS_ONCE(pllcfg) & PLL_SEL) != 0) + __METAL_ACCESS_ONCE(pllcfg) &= ~(PLL_SEL); + + /* Make sure we're running off of the external oscillator for stability */ + if (pllref != NULL) + __METAL_ACCESS_ONCE(pllcfg) |= PLL_REFSEL; + + /* Configure the PLL to run at the requested init frequency. + * Using the vtable instead of the user API because we want to control + * when the callbacks occur. */ + pll->clock.vtable->set_rate_hz(&(pll->clock), init_rate); + + /* If the PLL clock has had a rate_change_callback configured, call it */ + _metal_clock_call_all_callbacks(pll->clock._post_rate_change_callback); +} + +long __metal_driver_sifive_fe310_g000_pll_get_rate_hz( + const struct metal_clock *clock) { + struct metal_clock *pllref = + __metal_driver_sifive_fe310_g000_pll_pllref(clock); + struct metal_clock *pllsel0 = + __metal_driver_sifive_fe310_g000_pll_pllsel0(clock); + long config_offset = + __metal_driver_sifive_fe310_g000_pll_config_offset(clock); + struct __metal_driver_sifive_fe310_g000_prci *config_base = + __metal_driver_sifive_fe310_g000_pll_config_base(clock); + long divider_offset = + __metal_driver_sifive_fe310_g000_pll_divider_offset(clock); + struct __metal_driver_sifive_fe310_g000_prci *divider_base = + __metal_driver_sifive_fe310_g000_pll_divider_base(clock); + const struct __metal_driver_vtable_sifive_fe310_g000_prci *vtable = + __metal_driver_sifive_fe310_g000_prci_vtable(); + + long cfg = vtable->get_reg(config_base, config_offset); + long div = vtable->get_reg(divider_base, divider_offset); + + /* At the end of the PLL there's one big mux: it either selects the HFROSC + * (bypassing the PLL entirely) or uses the PLL. */ + if (__METAL_GET_FIELD(cfg, PLL_SEL) == 0) + return metal_clock_get_rate_hz(pllsel0); + + /* There's a clock mux before the PLL that selects between the HFROSC adn + * the HFXOSC as the PLL's input clock. */ + long ref_hz = metal_clock_get_rate_hz( + __METAL_GET_FIELD(cfg, PLL_REFSEL) ? pllref : pllsel0); + + /* It's possible to bypass the PLL, which is an internal bpyass. This + * still obays the PLL's input clock mu. */ + if (__METAL_GET_FIELD(cfg, PLL_BYPASS)) + return ref_hz; + + /* Logically the PLL is a three stage div-mul-div. */ + long div_r = __METAL_GET_FIELD(cfg, PLL_R) + 1; + long mul_f = 2 * (__METAL_GET_FIELD(cfg, PLL_F) + 1); + if (__METAL_GET_FIELD(cfg, PLL_Q) == 0) + return -1; + long div_q = 1 << __METAL_GET_FIELD(cfg, PLL_Q); + + /* In addition to the dividers inherent in the PLL, there's an additional + * clock divider that lives after the PLL and lets us pick a more + * interesting range of frequencies. */ + long pllout = (((ref_hz / div_r) * mul_f) / div_q); + if (__METAL_GET_FIELD(div, DIV_1)) + return pllout; + + return pllout / (2 * (__METAL_GET_FIELD(div, DIV_DIV) + 1)); +} + +/* Find a valid configuration for the PLL which is closest to the desired + * output frequency. + * Arguments: + * - ref_hz PLL input frequency + * - rate desired PLL output frequency + * Returns: + * -1 if no valid configuration is available + * the index into pll_configs of a valid configuration */ +static int find_closest_config(long ref_hz, long rate) { + int closest_index = -1; + long closest_diff = LONG_MAX; + + /* We're probably trying for a fast output frequency, so start from + * the high end of the configs. */ + for (int i = (sizeof(pll_configs) / sizeof(pll_configs[0])) - 1; i >= 0; + i--) { + long config_freq = get_pll_config_freq(ref_hz, &(pll_configs[i])); + if (config_freq != PLL_CONFIG_NOT_VALID) { + long freq_diff = labs(config_freq - rate); + + if (freq_diff < closest_diff) { + closest_index = i; + closest_diff = freq_diff; + } + } + } + + return closest_index; +} + +/* The PLL needs 100 usec to stabilize before we test PLL_LOCK. Since LFROSC + * on all targets with the FE310-G000 PLL runs at 32768 Hz, we need to wait + * at least + * + * ceil(100 usec * 32768 ticks/sec * 1 sec / 1000000 usec) = 4 ticks + * + * of mtime before we test PLL_LOCK. + * + * TODO: Determine the mtime timebase at compile or runtime and use that + * here. + */ +#define PLL_LOCK_WAIT_TICKS 4 + +/* Configure the PLL and wait for it to lock */ +static void configure_pll(__metal_io_u32 *pllcfg, __metal_io_u32 *plloutdiv, + const struct pll_config_t *config) { + __METAL_ACCESS_ONCE(pllcfg) &= ~(PLL_R); + __METAL_ACCESS_ONCE(pllcfg) |= PLL_R_SHIFT(config->r); + + __METAL_ACCESS_ONCE(pllcfg) &= ~(PLL_F); + __METAL_ACCESS_ONCE(pllcfg) |= PLL_F_SHIFT(config->f); + + __METAL_ACCESS_ONCE(pllcfg) &= ~(PLL_Q); + __METAL_ACCESS_ONCE(pllcfg) |= PLL_Q_SHIFT(config->q); + + if (config->d < 0) { + /* disable final divider */ + __METAL_ACCESS_ONCE(plloutdiv) |= DIV_1; + + __METAL_ACCESS_ONCE(plloutdiv) &= ~(DIV_DIV); + __METAL_ACCESS_ONCE(plloutdiv) |= PLL_DIV_SHIFT(1); + } else { + __METAL_ACCESS_ONCE(plloutdiv) &= ~(DIV_1); + + __METAL_ACCESS_ONCE(plloutdiv) &= ~(DIV_DIV); + __METAL_ACCESS_ONCE(plloutdiv) |= PLL_DIV_SHIFT(config->d); + } + + __METAL_ACCESS_ONCE(pllcfg) &= ~(PLL_BYPASS); + + /* Wait for the PLL to stabilize before testing it for lock */ +#ifdef __METAL_DT_RISCV_CLINT0_HANDLE + unsigned long long mtime, mtime_end; + __metal_driver_riscv_clint0_command_request(__METAL_DT_RISCV_CLINT0_HANDLE, + METAL_TIMER_MTIME_GET, &mtime); + mtime_end = mtime + PLL_LOCK_WAIT_TICKS; + while (mtime <= mtime_end) { + __metal_driver_riscv_clint0_command_request( + __METAL_DT_RISCV_CLINT0_HANDLE, METAL_TIMER_MTIME_GET, &mtime); + } +#elif __METAL_DT_RISCV_CLIC0_HANDLE + unsigned long long mtime, mtime_end; + __metal_driver_sifive_clic0_command_request(__METAL_DT_RISCV_CLIC0_HANDLE, + METAL_TIMER_MTIME_GET, &mtime); + mtime_end = mtime + PLL_LOCK_WAIT_TICKS; + while (mtime <= mtime_end) { + __metal_driver_sifive_clic0_command_request( + __METAL_DT_RISCV_CLIC0_HANDLE, METAL_TIMER_MTIME_GET, &mtime); + } +#else +#pragma message( \ + No handle for CLINT or CLIC found, PLL might race with lock signal !) +#endif + + /* Wait for PLL to lock */ + while ((__METAL_ACCESS_ONCE(pllcfg) & PLL_LOCK) == 0) + ; +} + +long __metal_driver_sifive_fe310_g000_pll_set_rate_hz(struct metal_clock *clock, + long rate) { + struct metal_clock *pllref = + __metal_driver_sifive_fe310_g000_pll_pllref(clock); + struct metal_clock *pllsel0 = + __metal_driver_sifive_fe310_g000_pll_pllsel0(clock); + long config_offset = + __metal_driver_sifive_fe310_g000_pll_config_offset(clock); + long divider_offset = + __metal_driver_sifive_fe310_g000_pll_divider_offset(clock); + long base = __metal_driver_sifive_fe310_g000_prci_base(); + + __metal_io_u32 *pllcfg = (__metal_io_u32 *)(base + config_offset); + __metal_io_u32 *plloutdiv = (__metal_io_u32 *)(base + divider_offset); + + /* We can't modify the PLL if coreclk is driven by it, so switch it off */ + if (__METAL_ACCESS_ONCE(pllcfg) & PLL_SEL) + __METAL_ACCESS_ONCE(pllcfg) &= ~(PLL_SEL); + + /* There's a clock mux before the PLL that selects between the HFROSC and + * the HFXOSC as the PLL's input clock. */ + long ref_hz = metal_clock_get_rate_hz( + __METAL_ACCESS_ONCE(pllcfg) & PLL_REFSEL ? pllref : pllsel0); + + /* if the desired rate is within 75%-125% of the input clock, bypass the PLL + */ + if ((ref_hz * 3 / 4) <= rate && (ref_hz * 5 / 4) >= rate) { + __METAL_ACCESS_ONCE(pllcfg) |= PLL_BYPASS; + } else { + int config_index = find_closest_config(ref_hz, rate); + if (config_index != -1) { + configure_pll(pllcfg, plloutdiv, &(pll_configs[config_index])); + } else { + /* unable to find a valid configuration */ + __METAL_ACCESS_ONCE(pllcfg) |= PLL_BYPASS; + } + } + + /* Enable the PLL */ + __METAL_ACCESS_ONCE(pllcfg) |= PLL_SEL; + + return __metal_driver_sifive_fe310_g000_pll_get_rate_hz(clock); +} + +#ifdef __METAL_DT_SIFIVE_FE310_G000_PLL_HANDLE +METAL_CONSTRUCTOR(use_hfxosc) { + long init_rate = __metal_driver_sifive_fe310_g000_pll_init_rate(); + metal_clock_set_rate_hz(&__METAL_DT_SIFIVE_FE310_G000_PLL_HANDLE->clock, + init_rate); +} +#endif + +__METAL_DEFINE_VTABLE(__metal_driver_vtable_sifive_fe310_g000_pll) = { + .init = __metal_driver_sifive_fe310_g000_pll_init, + .clock.get_rate_hz = __metal_driver_sifive_fe310_g000_pll_get_rate_hz, + .clock.set_rate_hz = __metal_driver_sifive_fe310_g000_pll_set_rate_hz, +}; + +#endif /* METAL_SIFIVE_FE310_G000_PLL */ + +typedef int no_empty_translation_units; diff --git a/arch/riscv32/fe310/src/freedom-metal/src/drivers/sifive_fe310-g000_prci.c b/arch/riscv32/fe310/src/freedom-metal/src/drivers/sifive_fe310-g000_prci.c new file mode 100644 index 00000000..f863feb3 --- /dev/null +++ b/arch/riscv32/fe310/src/freedom-metal/src/drivers/sifive_fe310-g000_prci.c @@ -0,0 +1,31 @@ +/* Copyright 2018 SiFive, Inc */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include + +#ifdef METAL_SIFIVE_FE310_G000_PRCI + +#include +#include + +long __metal_driver_sifive_fe310_g000_prci_get_reg( + const struct __metal_driver_sifive_fe310_g000_prci *prci, long offset) { + unsigned long base = __metal_driver_sifive_fe310_g000_prci_base(); + return __METAL_ACCESS_ONCE((__metal_io_u32 *)(base + offset)); +} + +long __metal_driver_sifive_fe310_g000_prci_set_reg( + const struct __metal_driver_sifive_fe310_g000_prci *prci, long offset, + long value) { + unsigned long base = __metal_driver_sifive_fe310_g000_prci_base(); + return __METAL_ACCESS_ONCE((__metal_io_u32 *)(base + offset)) = value; +} + +__METAL_DEFINE_VTABLE(__metal_driver_vtable_sifive_fe310_g000_prci) = { + .get_reg = __metal_driver_sifive_fe310_g000_prci_get_reg, + .set_reg = __metal_driver_sifive_fe310_g000_prci_set_reg, +}; + +#endif /* METAL_SIFIVE_FE310_G000_PRCI */ + +typedef int no_empty_translation_units; diff --git a/arch/riscv32/fe310/src/freedom-metal/src/drivers/sifive_global-external-interrupts0.c b/arch/riscv32/fe310/src/freedom-metal/src/drivers/sifive_global-external-interrupts0.c new file mode 100644 index 00000000..3fd96b91 --- /dev/null +++ b/arch/riscv32/fe310/src/freedom-metal/src/drivers/sifive_global-external-interrupts0.c @@ -0,0 +1,192 @@ +/* Copyright 2018 SiFive, Inc */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include + +#ifdef METAL_SIFIVE_GLOBAL_EXTERNAL_INTERRUPTS0 + +#include +#include +#include +#include + +void __metal_driver_sifive_global_external_interrupt_init( + struct metal_interrupt *controller) { + struct __metal_driver_sifive_global_external_interrupts0 *global0; + + global0 = (struct __metal_driver_sifive_global_external_interrupts0 + *)(controller); + if (!global0->init_done) { + struct metal_interrupt *intc = + __metal_driver_sifive_global_external_interrupts0_interrupt_parent( + controller); + + if (intc) { + intc->vtable->interrupt_init(intc); + /* Register its interrupts with with parent controller */ + for ( + int i = 0; + i < + __metal_driver_sifive_global_external_interrupts0_num_interrupts( + controller); + i++) { + intc->vtable->interrupt_register( + intc, + __metal_driver_sifive_global_external_interrupts0_interrupt_lines( + controller, i), + NULL, controller); + } + global0->init_done = 1; + } + } +} + +int __metal_driver_sifive_global_external_interrupt_register( + struct metal_interrupt *controller, int id, metal_interrupt_handler_t isr, + void *priv) { + int rc = -1; + + if (id != 0) { + struct metal_interrupt *intc = + __metal_driver_sifive_global_external_interrupts0_interrupt_parent( + controller); + + /* Enable its interrupts with parent controller */ + if (intc) { + rc = intc->vtable->interrupt_register(intc, id, isr, priv); + } + } + return rc; +} + +int __metal_driver_sifive_global_external_interrupt_enable( + struct metal_interrupt *controller, int id) { + int rc = -1; + + if (id != 0) { + struct metal_interrupt *intc = + __metal_driver_sifive_global_external_interrupts0_interrupt_parent( + controller); + + /* Enable its interrupts with parent controller */ + if (intc) { + rc = intc->vtable->interrupt_enable(intc, id); + } + } + return rc; +} + +int __metal_driver_sifive_global_external_interrupt_disable( + struct metal_interrupt *controller, int id) { + int rc = -1; + + if (id != 0) { + struct metal_interrupt *intc = + __metal_driver_sifive_global_external_interrupts0_interrupt_parent( + controller); + + /* Enable its interrupts with parent controller */ + if (intc) { + rc = intc->vtable->interrupt_disable(intc, id); + } + } + return rc; +} + +int __metal_driver_sifive_global_external_interrupt_set_threshold( + struct metal_interrupt *controller, unsigned int threshold) { + struct metal_interrupt *intc = + __metal_driver_sifive_global_external_interrupts0_interrupt_parent( + controller); + if (intc) { + return intc->vtable->interrupt_set_threshold(intc, threshold); + } + return -1; +} + +unsigned int __metal_driver_sifive_global_external_interrupt_get_threshold( + struct metal_interrupt *controller) { + struct metal_interrupt *intc = + __metal_driver_sifive_global_external_interrupts0_interrupt_parent( + controller); + + if (intc) { + return intc->vtable->interrupt_get_threshold(intc); + } + return 0; +} + +int __metal_driver_sifive_global_external_interrupt_set_priority( + struct metal_interrupt *controller, int id, unsigned int priority) { + struct metal_interrupt *intc = + __metal_driver_sifive_global_external_interrupts0_interrupt_parent( + controller); + if (intc) { + return intc->vtable->interrupt_set_priority(intc, id, priority); + } + return -1; +} + +unsigned int __metal_driver_sifive_global_external_interrupt_get_priority( + struct metal_interrupt *controller, int id) { + struct metal_interrupt *intc = + __metal_driver_sifive_global_external_interrupts0_interrupt_parent( + controller); + + if (intc) { + return intc->vtable->interrupt_get_priority(intc, id); + } + return 0; +} + +int __metal_driver_sifive_global_external_command_request( + struct metal_interrupt *controller, int command, void *data) { + int idx; + int rc = -1; + + switch (command) { + case METAL_MAX_INTERRUPT_GET: + rc = __metal_driver_sifive_global_external_interrupts0_num_interrupts( + controller); + break; + case METAL_INDEX_INTERRUPT_GET: + rc = 0; + if (data) { + idx = *(int *)data; + rc = + __metal_driver_sifive_global_external_interrupts0_interrupt_lines( + controller, idx); + } + break; + default: + break; + } + + return rc; +} + +__METAL_DEFINE_VTABLE( + __metal_driver_vtable_sifive_global_external_interrupts0) = { + .global0_vtable.interrupt_init = + __metal_driver_sifive_global_external_interrupt_init, + .global0_vtable.interrupt_register = + __metal_driver_sifive_global_external_interrupt_register, + .global0_vtable.interrupt_enable = + __metal_driver_sifive_global_external_interrupt_enable, + .global0_vtable.interrupt_disable = + __metal_driver_sifive_global_external_interrupt_disable, + .global0_vtable.interrupt_get_threshold = + __metal_driver_sifive_global_external_interrupt_get_threshold, + .global0_vtable.interrupt_set_threshold = + __metal_driver_sifive_global_external_interrupt_set_threshold, + .global0_vtable.interrupt_get_priority = + __metal_driver_sifive_global_external_interrupt_get_priority, + .global0_vtable.interrupt_set_priority = + __metal_driver_sifive_global_external_interrupt_set_priority, + .global0_vtable.command_request = + __metal_driver_sifive_global_external_command_request, +}; + +#endif + +typedef int no_empty_translation_units; diff --git a/arch/riscv32/fe310/src/freedom-metal/src/drivers/sifive_gpio-buttons.c b/arch/riscv32/fe310/src/freedom-metal/src/drivers/sifive_gpio-buttons.c new file mode 100644 index 00000000..119bd8ab --- /dev/null +++ b/arch/riscv32/fe310/src/freedom-metal/src/drivers/sifive_gpio-buttons.c @@ -0,0 +1,53 @@ +/* Copyright 2018 SiFive, Inc */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include + +#ifdef METAL_SIFIVE_GPIO_BUTTONS + +#include +#include +#include +#include + +int __metal_driver_button_exist(struct metal_button *button, char *label) { + if (strcmp(__metal_driver_sifive_gpio_button_label(button), label) == 0) { + return 1; + } + return 0; +} + +struct metal_interrupt * +__metal_driver_button_interrupt_controller(struct metal_button *button) { + return __metal_driver_sifive_gpio_button_interrupt_controller(button); +} + +int __metal_driver_button_get_interrupt_id(struct metal_button *button) { + int irq, max_irq; + struct metal_interrupt *irc; + + irq = __metal_driver_sifive_gpio_button_interrupt_line(button); + irc = __metal_driver_sifive_gpio_button_interrupt_controller(button); + + if (irc != NULL) { + max_irq = _metal_interrupt_command_request(irc, METAL_MAX_INTERRUPT_GET, + NULL); + + if (irq < max_irq) { + return _metal_interrupt_command_request( + irc, METAL_INDEX_INTERRUPT_GET, (void *)&irq); + } + } + return METAL_INTERRUPT_ID_LCMX; +} + +__METAL_DEFINE_VTABLE(__metal_driver_vtable_sifive_button) = { + .button_vtable.button_exist = __metal_driver_button_exist, + .button_vtable.interrupt_controller = + __metal_driver_button_interrupt_controller, + .button_vtable.get_interrupt_id = __metal_driver_button_get_interrupt_id, +}; + +#endif + +typedef int no_empty_translation_units; diff --git a/arch/riscv32/fe310/src/freedom-metal/src/drivers/sifive_gpio-leds.c b/arch/riscv32/fe310/src/freedom-metal/src/drivers/sifive_gpio-leds.c new file mode 100644 index 00000000..ef32528c --- /dev/null +++ b/arch/riscv32/fe310/src/freedom-metal/src/drivers/sifive_gpio-leds.c @@ -0,0 +1,80 @@ +/* Copyright 2018 SiFive, Inc */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include + +#ifdef METAL_SIFIVE_GPIO_LEDS + +#include +#include +#include +#include + +int __metal_driver_led_exist(struct metal_led *led, char *label) { + if (strcmp(__metal_driver_sifive_gpio_led_label(led), label) == 0) { + return 1; + } + return 0; +} + +void __metal_driver_led_enable(struct metal_led *led) { + int pin; + struct metal_gpio *gpio; + + pin = __metal_driver_sifive_gpio_led_pin(led); + gpio = __metal_driver_sifive_gpio_led_gpio(led); + + if (gpio != NULL) { + /* Configure LED as output */ + metal_gpio_disable_input((struct metal_gpio *)gpio, pin); + metal_gpio_enable_output((struct metal_gpio *)gpio, pin); + } +} + +void __metal_driver_led_on(struct metal_led *led) { + int pin; + struct metal_gpio *gpio; + + pin = __metal_driver_sifive_gpio_led_pin(led); + gpio = __metal_driver_sifive_gpio_led_gpio(led); + + if (gpio != NULL) { + metal_gpio_set_pin((struct metal_gpio *)gpio, pin, 1); + } +} + +void __metal_driver_led_off(struct metal_led *led) { + int pin; + struct metal_gpio *gpio; + + pin = __metal_driver_sifive_gpio_led_pin(led); + gpio = __metal_driver_sifive_gpio_led_gpio(led); + + if (gpio != NULL) { + metal_gpio_set_pin((struct metal_gpio *)gpio, pin, 0); + } +} + +void __metal_driver_led_toggle(struct metal_led *led) { + int pin; + struct metal_gpio *gpio; + + pin = __metal_driver_sifive_gpio_led_pin(led); + gpio = __metal_driver_sifive_gpio_led_gpio(led); + + if (gpio != NULL) { + metal_gpio_toggle_pin((struct metal_gpio *)gpio, pin); + } +} + +__METAL_DEFINE_VTABLE(__metal_driver_vtable_sifive_led) = { + .led_vtable.led_exist = __metal_driver_led_exist, + .led_vtable.led_enable = __metal_driver_led_enable, + .led_vtable.led_on = __metal_driver_led_on, + .led_vtable.led_off = __metal_driver_led_off, + .led_vtable.led_toggle = __metal_driver_led_toggle, +}; + +#endif + +typedef int no_empty_translation_units; diff --git a/arch/riscv32/fe310/src/freedom-metal/src/drivers/sifive_gpio-switches.c b/arch/riscv32/fe310/src/freedom-metal/src/drivers/sifive_gpio-switches.c new file mode 100644 index 00000000..dc0adb79 --- /dev/null +++ b/arch/riscv32/fe310/src/freedom-metal/src/drivers/sifive_gpio-switches.c @@ -0,0 +1,52 @@ +/* Copyright 2018 SiFive, Inc */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include + +#ifdef METAL_SIFIVE_GPIO_SWITCHES + +#include +#include +#include +#include + +int __metal_driver_switch_exist(struct metal_switch *flip, char *label) { + if (strcmp(__metal_driver_sifive_gpio_switch_label(flip), label) == 0) { + return 1; + } + return 0; +} + +struct metal_interrupt * +__metal_driver_switch_interrupt_controller(struct metal_switch *flip) { + return __metal_driver_sifive_gpio_switch_interrupt_controller(flip); +} + +int __metal_driver_switch_get_interrupt_id(struct metal_switch *flip) { + int irq, max_irq; + struct metal_interrupt *irc; + + irq = __metal_driver_sifive_gpio_switch_interrupt_line(flip); + irc = __metal_driver_sifive_gpio_switch_interrupt_controller(flip); + if (irc != NULL) { + max_irq = _metal_interrupt_command_request(irc, METAL_MAX_INTERRUPT_GET, + NULL); + + if (irq < max_irq) { + return _metal_interrupt_command_request( + irc, METAL_INDEX_INTERRUPT_GET, (void *)&irq); + } + } + return METAL_INTERRUPT_ID_LCMX; +} + +__METAL_DEFINE_VTABLE(__metal_driver_vtable_sifive_switch) = { + .switch_vtable.switch_exist = __metal_driver_switch_exist, + .switch_vtable.interrupt_controller = + __metal_driver_switch_interrupt_controller, + .switch_vtable.get_interrupt_id = __metal_driver_switch_get_interrupt_id, +}; + +#endif + +typedef int no_empty_translation_units; diff --git a/arch/riscv32/fe310/src/freedom-metal/src/drivers/sifive_gpio0.c b/arch/riscv32/fe310/src/freedom-metal/src/drivers/sifive_gpio0.c new file mode 100644 index 00000000..1324eb6e --- /dev/null +++ b/arch/riscv32/fe310/src/freedom-metal/src/drivers/sifive_gpio0.c @@ -0,0 +1,255 @@ +/* Copyright 2018 SiFive, Inc */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include + +#ifdef METAL_SIFIVE_GPIO0 + +#include +#include +#include + +int __metal_driver_sifive_gpio0_enable_input(struct metal_gpio *ggpio, + long source) { + long base = __metal_driver_sifive_gpio0_base(ggpio); + + __METAL_ACCESS_ONCE( + (__metal_io_u32 *)(base + METAL_SIFIVE_GPIO0_INPUT_EN)) |= source; + + return 0; +} + +int __metal_driver_sifive_gpio0_disable_input(struct metal_gpio *ggpio, + long source) { + long base = __metal_driver_sifive_gpio0_base(ggpio); + + __METAL_ACCESS_ONCE( + (__metal_io_u32 *)(base + METAL_SIFIVE_GPIO0_INPUT_EN)) &= ~source; + + return 0; +} + +long __metal_driver_sifive_gpio0_input(struct metal_gpio *ggpio) { + long base = __metal_driver_sifive_gpio0_base(ggpio); + + return __METAL_ACCESS_ONCE( + (__metal_io_u32 *)(base + METAL_SIFIVE_GPIO0_VALUE)); +} + +long __metal_driver_sifive_gpio0_output(struct metal_gpio *ggpio) { + long base = __metal_driver_sifive_gpio0_base(ggpio); + + return __METAL_ACCESS_ONCE( + (__metal_io_u32 *)(base + METAL_SIFIVE_GPIO0_PORT)); +} + +int __metal_driver_sifive_gpio0_disable_output(struct metal_gpio *ggpio, + long source) { + long base = __metal_driver_sifive_gpio0_base(ggpio); + + __METAL_ACCESS_ONCE( + (__metal_io_u32 *)(base + METAL_SIFIVE_GPIO0_OUTPUT_EN)) &= ~source; + + return 0; +} + +int __metal_driver_sifive_gpio0_enable_output(struct metal_gpio *ggpio, + long source) { + long base = __metal_driver_sifive_gpio0_base(ggpio); + + __METAL_ACCESS_ONCE( + (__metal_io_u32 *)(base + METAL_SIFIVE_GPIO0_OUTPUT_EN)) |= source; + + return 0; +} + +int __metal_driver_sifive_gpio0_output_set(struct metal_gpio *ggpio, + long value) { + long base = __metal_driver_sifive_gpio0_base(ggpio); + + __METAL_ACCESS_ONCE((__metal_io_u32 *)(base + METAL_SIFIVE_GPIO0_PORT)) |= + value; + + return 0; +} + +int __metal_driver_sifive_gpio0_output_clear(struct metal_gpio *ggpio, + long value) { + long base = __metal_driver_sifive_gpio0_base(ggpio); + + __METAL_ACCESS_ONCE((__metal_io_u32 *)(base + METAL_SIFIVE_GPIO0_PORT)) &= + ~value; + + return 0; +} + +int __metal_driver_sifive_gpio0_output_toggle(struct metal_gpio *ggpio, + long value) { + long base = __metal_driver_sifive_gpio0_base(ggpio); + + __METAL_ACCESS_ONCE((__metal_io_u32 *)(base + METAL_SIFIVE_GPIO0_PORT)) = + __METAL_ACCESS_ONCE( + (__metal_io_u32 *)(base + METAL_SIFIVE_GPIO0_PORT)) ^ + value; + + return 0; +} + +int __metal_driver_sifive_gpio0_enable_io(struct metal_gpio *ggpio, long source, + long dest) { + long base = __metal_driver_sifive_gpio0_base(ggpio); + + __METAL_ACCESS_ONCE( + (__metal_io_u32 *)(base + METAL_SIFIVE_GPIO0_IOF_SEL)) |= source; + __METAL_ACCESS_ONCE((__metal_io_u32 *)(base + METAL_SIFIVE_GPIO0_IOF_EN)) |= + dest; + + return 0; +} + +int __metal_driver_sifive_gpio0_disable_io(struct metal_gpio *ggpio, + long source) { + long base = __metal_driver_sifive_gpio0_base(ggpio); + + __METAL_ACCESS_ONCE((__metal_io_u32 *)(base + METAL_SIFIVE_GPIO0_IOF_EN)) &= + ~source; + + return 0; +} + +int __metal_driver_sifive_gpio0_config_int(struct metal_gpio *ggpio, + long source, int intr_type) { + long base = __metal_driver_sifive_gpio0_base(ggpio); + + switch (intr_type) { + case METAL_GPIO_INT_DISABLE: + __METAL_ACCESS_ONCE( + (__metal_io_u32 *)(base + METAL_SIFIVE_GPIO0_RISE_IE)) &= ~source; + __METAL_ACCESS_ONCE( + (__metal_io_u32 *)(base + METAL_SIFIVE_GPIO0_FALL_IE)) &= ~source; + __METAL_ACCESS_ONCE( + (__metal_io_u32 *)(base + METAL_SIFIVE_GPIO0_HIGH_IE)) &= ~source; + __METAL_ACCESS_ONCE( + (__metal_io_u32 *)(base + METAL_SIFIVE_GPIO0_LOW_IE)) &= ~source; + break; + case METAL_GPIO_INT_RISING: + __METAL_ACCESS_ONCE( + (__metal_io_u32 *)(base + METAL_SIFIVE_GPIO0_RISE_IE)) |= source; + break; + case METAL_GPIO_INT_FALLING: + __METAL_ACCESS_ONCE( + (__metal_io_u32 *)(base + METAL_SIFIVE_GPIO0_FALL_IE)) |= source; + break; + case METAL_GPIO_INT_BOTH_EDGE: + __METAL_ACCESS_ONCE( + (__metal_io_u32 *)(base + METAL_SIFIVE_GPIO0_RISE_IE)) |= source; + __METAL_ACCESS_ONCE( + (__metal_io_u32 *)(base + METAL_SIFIVE_GPIO0_FALL_IE)) |= source; + break; + case METAL_GPIO_INT_HIGH: + __METAL_ACCESS_ONCE( + (__metal_io_u32 *)(base + METAL_SIFIVE_GPIO0_HIGH_IE)) |= source; + break; + case METAL_GPIO_INT_LOW: + __METAL_ACCESS_ONCE( + (__metal_io_u32 *)(base + METAL_SIFIVE_GPIO0_LOW_IE)) |= source; + break; + case METAL_GPIO_INT_BOTH_LEVEL: + __METAL_ACCESS_ONCE( + (__metal_io_u32 *)(base + METAL_SIFIVE_GPIO0_HIGH_IE)) |= source; + __METAL_ACCESS_ONCE( + (__metal_io_u32 *)(base + METAL_SIFIVE_GPIO0_LOW_IE)) |= source; + break; + case METAL_GPIO_INT_MAX: + __METAL_ACCESS_ONCE( + (__metal_io_u32 *)(base + METAL_SIFIVE_GPIO0_RISE_IE)) |= source; + __METAL_ACCESS_ONCE( + (__metal_io_u32 *)(base + METAL_SIFIVE_GPIO0_FALL_IE)) |= source; + __METAL_ACCESS_ONCE( + (__metal_io_u32 *)(base + METAL_SIFIVE_GPIO0_HIGH_IE)) |= source; + __METAL_ACCESS_ONCE( + (__metal_io_u32 *)(base + METAL_SIFIVE_GPIO0_LOW_IE)) |= source; + break; + } + return 0; +} + +int __metal_driver_sifive_gpio0_clear_int(struct metal_gpio *ggpio, long source, + int intr_type) { + long base = __metal_driver_sifive_gpio0_base(ggpio); + + switch (intr_type) { + case METAL_GPIO_INT_RISING: + __METAL_ACCESS_ONCE( + (__metal_io_u32 *)(base + METAL_SIFIVE_GPIO0_RISE_IP)) |= source; + break; + case METAL_GPIO_INT_FALLING: + __METAL_ACCESS_ONCE( + (__metal_io_u32 *)(base + METAL_SIFIVE_GPIO0_FALL_IP)) |= source; + break; + case METAL_GPIO_INT_BOTH_EDGE: + __METAL_ACCESS_ONCE( + (__metal_io_u32 *)(base + METAL_SIFIVE_GPIO0_RISE_IP)) |= source; + __METAL_ACCESS_ONCE( + (__metal_io_u32 *)(base + METAL_SIFIVE_GPIO0_FALL_IP)) |= source; + break; + case METAL_GPIO_INT_HIGH: + __METAL_ACCESS_ONCE( + (__metal_io_u32 *)(base + METAL_SIFIVE_GPIO0_HIGH_IP)) |= source; + break; + case METAL_GPIO_INT_LOW: + __METAL_ACCESS_ONCE( + (__metal_io_u32 *)(base + METAL_SIFIVE_GPIO0_LOW_IP)) |= source; + break; + case METAL_GPIO_INT_BOTH_LEVEL: + __METAL_ACCESS_ONCE( + (__metal_io_u32 *)(base + METAL_SIFIVE_GPIO0_HIGH_IP)) |= source; + __METAL_ACCESS_ONCE( + (__metal_io_u32 *)(base + METAL_SIFIVE_GPIO0_LOW_IP)) |= source; + break; + case METAL_GPIO_INT_MAX: + __METAL_ACCESS_ONCE( + (__metal_io_u32 *)(base + METAL_SIFIVE_GPIO0_RISE_IP)) |= source; + __METAL_ACCESS_ONCE( + (__metal_io_u32 *)(base + METAL_SIFIVE_GPIO0_FALL_IP)) |= source; + __METAL_ACCESS_ONCE( + (__metal_io_u32 *)(base + METAL_SIFIVE_GPIO0_HIGH_IP)) |= source; + __METAL_ACCESS_ONCE( + (__metal_io_u32 *)(base + METAL_SIFIVE_GPIO0_LOW_IP)) |= source; + break; + } + return 0; +} + +struct metal_interrupt * +__metal_driver_gpio_interrupt_controller(struct metal_gpio *gpio) { + return __metal_driver_sifive_gpio0_interrupt_parent(gpio); +} + +int __metal_driver_gpio_get_interrupt_id(struct metal_gpio *gpio, int pin) { + int irq; + irq = __metal_driver_sifive_gpio0_interrupt_lines(gpio, pin); + return irq; +} + +__METAL_DEFINE_VTABLE(__metal_driver_vtable_sifive_gpio0) = { + .gpio.disable_input = __metal_driver_sifive_gpio0_disable_input, + .gpio.enable_input = __metal_driver_sifive_gpio0_enable_input, + .gpio.input = __metal_driver_sifive_gpio0_input, + .gpio.output = __metal_driver_sifive_gpio0_output, + .gpio.disable_output = __metal_driver_sifive_gpio0_disable_output, + .gpio.enable_output = __metal_driver_sifive_gpio0_enable_output, + .gpio.output_set = __metal_driver_sifive_gpio0_output_set, + .gpio.output_clear = __metal_driver_sifive_gpio0_output_clear, + .gpio.output_toggle = __metal_driver_sifive_gpio0_output_toggle, + .gpio.enable_io = __metal_driver_sifive_gpio0_enable_io, + .gpio.disable_io = __metal_driver_sifive_gpio0_disable_io, + .gpio.config_int = __metal_driver_sifive_gpio0_config_int, + .gpio.clear_int = __metal_driver_sifive_gpio0_clear_int, + .gpio.interrupt_controller = __metal_driver_gpio_interrupt_controller, + .gpio.get_interrupt_id = __metal_driver_gpio_get_interrupt_id, +}; + +#endif /* METAL_SIFIVE_GPIO0 */ + +typedef int no_empty_translation_units; diff --git a/arch/riscv32/fe310/src/freedom-metal/src/drivers/sifive_i2c0.c b/arch/riscv32/fe310/src/freedom-metal/src/drivers/sifive_i2c0.c new file mode 100644 index 00000000..ec79bbc3 --- /dev/null +++ b/arch/riscv32/fe310/src/freedom-metal/src/drivers/sifive_i2c0.c @@ -0,0 +1,428 @@ +/* Copyright 2019 SiFive, Inc */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include + +#ifdef METAL_SIFIVE_I2C0 +#include +#include +#include +#include +#include +#include +#include +#include + +/* Register fields */ +#define METAL_I2C_CONTROL_EN (1UL << 7) +#define METAL_I2C_CONTROL_IE (1UL << 6) +#define METAL_I2C_WRITE (0UL << 0) +#define METAL_I2C_READ (1UL << 0) +#define METAL_I2C_CMD_START (1UL << 7) +#define METAL_I2C_CMD_STOP (1UL << 6) +#define METAL_I2C_CMD_READ (1UL << 5) +#define METAL_I2C_CMD_WRITE (1UL << 4) +#define METAL_I2C_CMD_ACK (1UL << 3) +#define METAL_I2C_CMD_IACK (1UL << 0) +#define METAL_I2C_STATUS_RXACK (1UL << 7) +#define METAL_I2C_STATUS_BUSY (1UL << 6) +#define METAL_I2C_STATUS_AL (1UL << 5) +#define METAL_I2C_STATUS_TIP (1UL << 1) +#define METAL_I2C_STATUS_IP (1UL << 0) + +/* Prescaler max value */ +#define METAL_I2C_PRESCALE_MAX 0xFFFF +/* Macros to access registers */ +#define METAL_I2C_REG(offset) ((base + offset)) +#define METAL_I2C_REGB(offset) \ + (__METAL_ACCESS_ONCE((__metal_io_u8 *)METAL_I2C_REG(offset))) +#define METAL_I2C_REGW(offset) \ + (__METAL_ACCESS_ONCE((__metal_io_u32 *)METAL_I2C_REG(offset))) + +/* Timeout macros for register status checks */ +#define METAL_I2C_RXDATA_TIMEOUT 1 +#define METAL_I2C_TIMEOUT_RESET(timeout) \ + timeout = metal_time() + METAL_I2C_RXDATA_TIMEOUT +#define METAL_I2C_TIMEOUT_CHECK(timeout) \ + if (metal_time() > timeout) { \ + METAL_I2C_LOG("I2C timeout error.\n"); \ + return METAL_I2C_RET_ERR; \ + } +#define METAL_I2C_REG_CHECK(exp, timeout) \ + while (exp) { \ + METAL_I2C_TIMEOUT_CHECK(timeout) \ + } + +/* Driver console logging */ +#if defined(METAL_I2C_DEBUG) +#define METAL_I2C_LOG(x) printf(x) +#else +#define METAL_I2C_LOG(x) +#endif + +/* Check endianess */ +#if __BYTE_ORDER__ != __ORDER_LITTLE_ENDIAN__ +#error *** Unsupported endianess *** +#endif + +#define METAL_SIFIVE_I2C_INSERT_STOP(stop_flag) ((stop_flag & 0x01UL) << 6) +#define METAL_SIFIVE_I2C_INSERT_RW_BIT(addr, rw) \ + ((addr & 0x7FUL) << 1 | (rw & 0x01UL)) +#define METAL_SIFIVE_I2C_GET_PRESCALER(baud) \ + ((clock_rate / (baud_rate * 5)) - 1) +#define METAL_I2C_INIT_OK 1 +#define METAL_I2C_RET_OK 0 +#define METAL_I2C_RET_ERR -1 + +static void pre_rate_change_callback(void *priv) { + unsigned long base = + __metal_driver_sifive_i2c0_control_base((struct metal_i2c *)priv); + /* Check for any pending transfers */ + while (METAL_I2C_REGB(METAL_SIFIVE_I2C0_STATUS) & METAL_I2C_STATUS_TIP) + ; +} + +static void post_rate_change_callback(void *priv) { + struct __metal_driver_sifive_i2c0 *i2c = priv; + /* Set baud rate after clock rate change */ + metal_i2c_set_baud_rate(&i2c->i2c, i2c->baud_rate); +} + +static void __metal_driver_sifive_i2c0_init(struct metal_i2c *gi2c, + unsigned int baud_rate, + metal_i2c_mode_t mode) { + struct __metal_driver_sifive_gpio0 *pinmux = + __metal_driver_sifive_i2c0_pinmux(gi2c); + struct __metal_driver_sifive_i2c0 *i2c = (void *)gi2c; + + if ((pinmux != NULL) && (gi2c != NULL)) { + /* configure I2C I/O pins */ + long pinmux_output_selector = + __metal_driver_sifive_i2c0_pinmux_output_selector(gi2c); + long pinmux_source_selector = + __metal_driver_sifive_i2c0_pinmux_source_selector(gi2c); + pinmux->gpio.vtable->enable_io((struct metal_gpio *)pinmux, + pinmux_output_selector, + pinmux_source_selector); + + /* 1: Master 0: Slave */ + if (mode == METAL_I2C_MASTER) { + /* Set requested baud rate */ + if (metal_i2c_set_baud_rate(gi2c, baud_rate) == METAL_I2C_RET_OK) { + i2c->init_done = METAL_I2C_INIT_OK; + } + } else { + /* Nothing to do. slave mode not supported */ + } + } +} + +static int __metal_driver_sifive_i2c0_get_baud_rate(struct metal_i2c *gi2c) { + struct __metal_driver_sifive_i2c0 *i2c = (void *)gi2c; + return i2c->baud_rate; +} + +static int __metal_driver_sifive_i2c0_set_baud_rate(struct metal_i2c *gi2c, + unsigned int baud_rate) { + struct metal_clock *clock = __metal_driver_sifive_i2c0_clock(gi2c); + struct __metal_driver_sifive_i2c0 *i2c = (void *)gi2c; + unsigned long base = __metal_driver_sifive_i2c0_control_base(gi2c); + int ret = METAL_I2C_RET_ERR; + + if ((clock != NULL) && (gi2c != NULL)) { + long clock_rate = clock->vtable->get_rate_hz(clock); + + i2c->pre_rate_change_callback.callback = &pre_rate_change_callback; + i2c->pre_rate_change_callback.priv = i2c; + metal_clock_register_pre_rate_change_callback( + clock, &(i2c->pre_rate_change_callback)); + + i2c->post_rate_change_callback.callback = &post_rate_change_callback; + i2c->post_rate_change_callback.priv = i2c; + metal_clock_register_post_rate_change_callback( + clock, &(i2c->post_rate_change_callback)); + + /* Calculate prescaler value */ + long prescaler = METAL_SIFIVE_I2C_GET_PRESCALER(baud_rate); + + if ((prescaler > METAL_I2C_PRESCALE_MAX) || (prescaler < 0)) { + /* Out of range value, return error */ + METAL_I2C_LOG("I2C Set baud failed.\n"); + } else { + /* Set pre-scaler value */ + METAL_I2C_REGB(METAL_SIFIVE_I2C0_CONTROL) &= ~METAL_I2C_CONTROL_EN; + METAL_I2C_REGB(METAL_SIFIVE_I2C0_PRESCALE_LOW) = prescaler & 0xFF; + METAL_I2C_REGB(METAL_SIFIVE_I2C0_PRESCALE_HIGH) = + (prescaler >> 8) & 0xFF; + METAL_I2C_REGB(METAL_SIFIVE_I2C0_CONTROL) |= METAL_I2C_CONTROL_EN; + + i2c->baud_rate = baud_rate; + ret = METAL_I2C_RET_OK; + } + } else { + METAL_I2C_LOG("I2C Set baud failed.\n"); + } + + return ret; +} + +static int __metal_driver_sifive_i2c0_write_addr(unsigned long base, + unsigned int addr, + unsigned char rw_flag) { + time_t timeout; + int ret = METAL_I2C_RET_OK; + /* Reset timeout */ + METAL_I2C_TIMEOUT_RESET(timeout); + + /* Check if any transfer is in progress */ + METAL_I2C_REG_CHECK( + (METAL_I2C_REGB(METAL_SIFIVE_I2C0_STATUS) & METAL_I2C_STATUS_TIP), + timeout); + + /* Set transmit register to given address with read/write flag */ + METAL_I2C_REGB(METAL_SIFIVE_I2C0_TRANSMIT) = + METAL_SIFIVE_I2C_INSERT_RW_BIT(addr, rw_flag); + + /* Set start flag to trigger the address transfer */ + METAL_I2C_REGB(METAL_SIFIVE_I2C0_COMMAND) = + METAL_I2C_CMD_WRITE | METAL_I2C_CMD_START; + /* Reset timeout */ + METAL_I2C_TIMEOUT_RESET(timeout); + + /* Check for transmit completion */ + METAL_I2C_REG_CHECK( + (METAL_I2C_REGB(METAL_SIFIVE_I2C0_STATUS) & METAL_I2C_STATUS_TIP), + timeout); + + /* Check for ACK from slave */ + if ((METAL_I2C_REGB(METAL_SIFIVE_I2C0_STATUS) & METAL_I2C_STATUS_RXACK)) { + /* No ACK, return error */ + METAL_I2C_LOG("I2C RX ACK failed.\n"); + ret = METAL_I2C_RET_ERR; + } + + return ret; +} + +static int __metal_driver_sifive_i2c0_write(struct metal_i2c *i2c, + unsigned int addr, unsigned int len, + unsigned char buf[], + metal_i2c_stop_bit_t stop_bit) { + __metal_io_u8 command; + time_t timeout; + int ret; + unsigned long base = __metal_driver_sifive_i2c0_control_base(i2c); + unsigned int i; + + if ((i2c != NULL) && + ((struct __metal_driver_sifive_i2c0 *)i2c)->init_done) { + + /* Send address over I2C bus, current driver supports only 7bit + * addressing */ + ret = + __metal_driver_sifive_i2c0_write_addr(base, addr, METAL_I2C_WRITE); + + if (ret != METAL_I2C_RET_OK) { + /* Write address failed */ + METAL_I2C_LOG("I2C Address Write failed.\n"); + } else { + /* Set command flags */ + command = METAL_I2C_CMD_WRITE; + + for (i = 0; i < len; i++) { + /* Copy into transmit register */ + METAL_I2C_REGB(METAL_SIFIVE_I2C0_TRANSMIT) = buf[i]; + + /* for last byte transfer, check if stop condition is requested + */ + if (i == (len - 1)) { + command |= METAL_SIFIVE_I2C_INSERT_STOP(stop_bit); + } + /* Write command register */ + METAL_I2C_REGB(METAL_SIFIVE_I2C0_COMMAND) = command; + /* Reset timeout */ + METAL_I2C_TIMEOUT_RESET(timeout); + + /* Check for transfer completion */ + METAL_I2C_REG_CHECK((METAL_I2C_REGB(METAL_SIFIVE_I2C0_STATUS) & + METAL_I2C_STATUS_TIP), + timeout); + + /* Check for ACK from slave */ + if ((METAL_I2C_REGB(METAL_SIFIVE_I2C0_STATUS) & + METAL_I2C_STATUS_RXACK)) { + /* No ACK, return error */ + METAL_I2C_LOG("I2C RX ACK failed.\n"); + ret = METAL_I2C_RET_ERR; + break; + } + } + } + + } else { + /* I2C device not initialized, return error */ + METAL_I2C_LOG("I2C device not initialized.\n"); + ret = METAL_I2C_RET_ERR; + } + + return ret; +} +static int __metal_driver_sifive_i2c0_read(struct metal_i2c *i2c, + unsigned int addr, unsigned int len, + unsigned char buf[], + metal_i2c_stop_bit_t stop_bit) { + int ret; + __metal_io_u8 command; + time_t timeout; + unsigned int i; + unsigned long base = __metal_driver_sifive_i2c0_control_base(i2c); + + if ((i2c != NULL) && + ((struct __metal_driver_sifive_i2c0 *)i2c)->init_done) { + + /* Send address over I2C bus, current driver supports only 7bit + * addressing */ + ret = __metal_driver_sifive_i2c0_write_addr(base, addr, METAL_I2C_READ); + + if (ret != METAL_I2C_RET_OK) { + /* Write address failed */ + METAL_I2C_LOG("I2C Read failed.\n"); + } else { + /* Set command flags */ + command = METAL_I2C_CMD_READ; + + for (i = 0; i < len; i++) { + /* check for last transfer */ + if (i == (len - 1)) { + /* Set NACK to end read, if requested generate STOP + * condition */ + command |= (METAL_I2C_CMD_ACK | + METAL_SIFIVE_I2C_INSERT_STOP(stop_bit)); + } + /* Write command register */ + METAL_I2C_REGB(METAL_SIFIVE_I2C0_COMMAND) = command; + /* Reset timeout */ + METAL_I2C_TIMEOUT_RESET(timeout); + + /* Wait for the read to complete */ + METAL_I2C_REG_CHECK((METAL_I2C_REGB(METAL_SIFIVE_I2C0_STATUS) & + METAL_I2C_STATUS_TIP), + timeout); + /* Store the received byte */ + buf[i] = METAL_I2C_REGB(METAL_SIFIVE_I2C0_TRANSMIT); + } + } + } else { + /* I2C device not initialized, return error */ + METAL_I2C_LOG("I2C device not initialized.\n"); + ret = METAL_I2C_RET_ERR; + } + + return ret; +} + +static int +__metal_driver_sifive_i2c0_transfer(struct metal_i2c *i2c, unsigned int addr, + unsigned char txbuf[], unsigned int txlen, + unsigned char rxbuf[], unsigned int rxlen) { + __metal_io_u8 command; + time_t timeout; + int ret; + unsigned int i; + unsigned long base = __metal_driver_sifive_i2c0_control_base(i2c); + + if ((i2c != NULL) && + ((struct __metal_driver_sifive_i2c0 *)i2c)->init_done) { + if (txlen) { + /* Set command flags */ + command = METAL_I2C_CMD_WRITE; + /* Send address over I2C bus, current driver supports only 7bit + * addressing */ + ret = __metal_driver_sifive_i2c0_write_addr(base, addr, + METAL_I2C_WRITE); + + if (ret != METAL_I2C_RET_OK) { + /* Write address failed */ + METAL_I2C_LOG("I2C Write failed.\n"); + return ret; + } + for (i = 0; i < txlen; i++) { + /* Copy into transmit register */ + METAL_I2C_REGB(METAL_SIFIVE_I2C0_TRANSMIT) = txbuf[i]; + + if (i == (txlen - 1) && (rxlen == 0)) { + /* Insert stop condition to end transfer */ + command |= METAL_I2C_CMD_STOP; + } + /* Write command register */ + METAL_I2C_REGB(METAL_SIFIVE_I2C0_COMMAND) = command; + /* Reset timeout */ + METAL_I2C_TIMEOUT_RESET(timeout); + + /* Check for transfer completion. */ + METAL_I2C_REG_CHECK((METAL_I2C_REGB(METAL_SIFIVE_I2C0_STATUS) & + METAL_I2C_STATUS_TIP), + timeout); + + /* Check for ACK from slave. */ + if ((METAL_I2C_REGB(METAL_SIFIVE_I2C0_STATUS) & + METAL_I2C_STATUS_RXACK)) { + /* No ACK, return error */ + METAL_I2C_LOG("I2C RX ACK failed.\n"); + ret = METAL_I2C_RET_ERR; + break; + } + } + } + if (rxlen) { + command = METAL_I2C_CMD_READ; /* Set command flags */ + /* Send address over I2C bus, current driver supports only 7bit + * addressing */ + ret = __metal_driver_sifive_i2c0_write_addr(base, addr, + METAL_I2C_READ); + + if (ret != METAL_I2C_RET_OK) { + /* Return error */ + METAL_I2C_LOG("I2C Read failed.\n"); + return ret; + } + for (i = 0; i < rxlen; i++) { + /* check for last transfer */ + if (i == (rxlen - 1)) { + /* Set NACK to end read, generate STOP condition */ + command |= (METAL_I2C_CMD_ACK | METAL_I2C_CMD_STOP); + } + /* Write command register */ + METAL_I2C_REGB(METAL_SIFIVE_I2C0_COMMAND) = command; + /* Reset timeout */ + METAL_I2C_TIMEOUT_RESET(timeout); + + /* Wait for the read to complete */ + METAL_I2C_REG_CHECK((METAL_I2C_REGB(METAL_SIFIVE_I2C0_STATUS) & + METAL_I2C_STATUS_TIP), + timeout); + /* Store the received byte */ + rxbuf[i] = METAL_I2C_REGB(METAL_SIFIVE_I2C0_TRANSMIT); + } + } + } else { + /* I2C device not initialized, return error */ + METAL_I2C_LOG("I2C device not initialized.\n"); + ret = METAL_I2C_RET_ERR; + } + + return ret; +} + +__METAL_DEFINE_VTABLE(__metal_driver_vtable_sifive_i2c0) = { + .i2c.init = __metal_driver_sifive_i2c0_init, + .i2c.write = __metal_driver_sifive_i2c0_write, + .i2c.read = __metal_driver_sifive_i2c0_read, + .i2c.transfer = __metal_driver_sifive_i2c0_transfer, + .i2c.get_baud_rate = __metal_driver_sifive_i2c0_get_baud_rate, + .i2c.set_baud_rate = __metal_driver_sifive_i2c0_set_baud_rate, +}; + +#endif /* METAL_SIFIVE_I2C0 */ + +typedef int no_empty_translation_units; diff --git a/arch/riscv32/fe310/src/freedom-metal/src/drivers/sifive_l2pf0.c b/arch/riscv32/fe310/src/freedom-metal/src/drivers/sifive_l2pf0.c new file mode 100644 index 00000000..30e7920a --- /dev/null +++ b/arch/riscv32/fe310/src/freedom-metal/src/drivers/sifive_l2pf0.c @@ -0,0 +1,139 @@ +/* Copyright 2020 SiFive, Inc */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include + +#ifdef METAL_SIFIVE_L2PF0 + +#include +#include + +/* Macros to access memory mapped registers */ +#define REGW(x) *((volatile uint32_t *)(l2pf_base[hartid] + x)) + +/* Macros for register bit masks */ +#define REG_MASK_BITWIDTH1 0x01 +#define REG_MASK_BITWIDTH4 0x0F +#define REG_MASK_BITWIDTH5 0x1F +#define REG_MASK_BITWIDTH6 0x3F +#define REG_MASK_BITWIDTH7 0x7F + +/* Macros to specify register bit shift */ +#define REG_BITSHIFT_1 1 +#define REG_BITSHIFT_2 2 +#define REG_BITSHIFT_4 4 +#define REG_BITSHIFT_8 8 +#define REG_BITSHIFT_9 9 +#define REG_BITSHIFT_13 13 +#define REG_BITSHIFT_14 14 +#define REG_BITSHIFT_20 20 +#define REG_BITSHIFT_21 21 +#define REG_BITSHIFT_28 28 + +/* Array of base addresses with HART IDs as the index */ +unsigned long l2pf_base[] = METAL_SIFIVE_L2PF0_BASE_ADDR; +int l2pf_base_len = sizeof(l2pf_base) / sizeof(unsigned long); + +void sifive_l2pf0_enable(void) { + int hartid; + __asm__ volatile("csrr %0, mhartid" : "=r"(hartid)); + + if ((hartid < l2pf_base_len) && (l2pf_base[hartid] != 0UL)) { + uint32_t val = REGW(METAL_SIFIVE_L2PF0_BASIC_CONTROL); + + /* Enable L2 prefetch unit for current hart */ + val |= REG_MASK_BITWIDTH1; + + REGW(METAL_SIFIVE_L2PF0_BASIC_CONTROL) = val; + } +} + +void sifive_l2pf0_disable(void) { + int hartid; + __asm__ volatile("csrr %0, mhartid" : "=r"(hartid)); + + if ((hartid < l2pf_base_len) && (l2pf_base[hartid] != 0UL)) { + uint32_t val = REGW(METAL_SIFIVE_L2PF0_BASIC_CONTROL); + + /* Disable L2 prefetch unit for current hart */ + val &= ~REG_MASK_BITWIDTH1; + + REGW(METAL_SIFIVE_L2PF0_BASIC_CONTROL) = val; + } +} + +void sifive_l2pf0_get_config(sifive_l2pf0_config *config) { + int hartid; + __asm__ volatile("csrr %0, mhartid" : "=r"(hartid)); + uint32_t val; + + /* Check for NULL, valid base address */ + if ((config) && (hartid < l2pf_base_len) && (l2pf_base[hartid] != 0UL)) { + /* Get currently active L2 prefetch configuration values */ + val = REGW(METAL_SIFIVE_L2PF0_BASIC_CONTROL); + + config->HwPrefetchEnable = (val & REG_MASK_BITWIDTH1); + config->CrossPageOptmDisable = + ((val >> REG_BITSHIFT_1) & REG_MASK_BITWIDTH1); + config->PrefetchDistance = + ((val >> REG_BITSHIFT_2) & REG_MASK_BITWIDTH6); + config->MaxAllowedDistance = + ((val >> REG_BITSHIFT_8) & REG_MASK_BITWIDTH6); + config->LinToExpThreshold = + ((val >> REG_BITSHIFT_14) & REG_MASK_BITWIDTH6); + config->AgeOutEn = ((val >> REG_BITSHIFT_20) & REG_MASK_BITWIDTH1); + config->NumLdsToAgeOut = + ((val >> REG_BITSHIFT_21) & REG_MASK_BITWIDTH7); + config->CrossPageEn = ((val >> REG_BITSHIFT_28) & REG_MASK_BITWIDTH1); + + val = REGW(METAL_SIFIVE_L2PF0_USER_CONTROL); + + config->QFullnessThreshold = (val & REG_MASK_BITWIDTH4); + config->HitCacheThreshold = + ((val >> REG_BITSHIFT_4) & REG_MASK_BITWIDTH5); + config->hitMSHRThreshold = + ((val >> REG_BITSHIFT_9) & REG_MASK_BITWIDTH4); + config->Window = ((val >> REG_BITSHIFT_13) & REG_MASK_BITWIDTH6); + } +} + +void sifive_l2pf0_set_config(sifive_l2pf0_config *config) { + int hartid; + __asm__ volatile("csrr %0, mhartid" : "=r"(hartid)); + uint32_t val; + + /* Check for NULL, valid base address */ + if ((config) && (hartid < l2pf_base_len) && (l2pf_base[hartid] != 0UL)) { + /* Get values from configuration to write into register */ + val = (uint32_t)( + (config->HwPrefetchEnable & REG_MASK_BITWIDTH1) | + ((config->CrossPageOptmDisable & REG_MASK_BITWIDTH1) + << REG_BITSHIFT_1) | + ((config->PrefetchDistance & REG_MASK_BITWIDTH6) + << REG_BITSHIFT_2) | + ((config->MaxAllowedDistance & REG_MASK_BITWIDTH6) + << REG_BITSHIFT_8) | + ((config->LinToExpThreshold & REG_MASK_BITWIDTH6) + << REG_BITSHIFT_14) | + ((config->AgeOutEn & REG_MASK_BITWIDTH1) << REG_BITSHIFT_20) | + ((config->NumLdsToAgeOut & REG_MASK_BITWIDTH7) << REG_BITSHIFT_21) | + ((config->CrossPageEn & REG_MASK_BITWIDTH1) << REG_BITSHIFT_28)); + + /* Set user specified L2 prefetch configuration values */ + REGW(METAL_SIFIVE_L2PF0_BASIC_CONTROL) = val; + + val = (uint32_t)( + (config->QFullnessThreshold & REG_MASK_BITWIDTH4) | + ((config->HitCacheThreshold & REG_MASK_BITWIDTH5) + << REG_BITSHIFT_4) | + ((config->hitMSHRThreshold & REG_MASK_BITWIDTH4) + << REG_BITSHIFT_9) | + ((config->Window & REG_MASK_BITWIDTH6) << REG_BITSHIFT_13)); + + REGW(METAL_SIFIVE_L2PF0_USER_CONTROL) = val; + } +} + +#endif + +typedef int no_empty_translation_units; diff --git a/arch/riscv32/fe310/src/freedom-metal/src/drivers/sifive_l2pf1.c b/arch/riscv32/fe310/src/freedom-metal/src/drivers/sifive_l2pf1.c new file mode 100644 index 00000000..7059c7a6 --- /dev/null +++ b/arch/riscv32/fe310/src/freedom-metal/src/drivers/sifive_l2pf1.c @@ -0,0 +1,175 @@ +/* Copyright 2021 SiFive, Inc */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include + +#ifdef METAL_SIFIVE_L2PF1 + +#include +#include +#include + +/* Macros to access memory mapped registers. */ +#define REGW(x) *((volatile uint32_t *)(l2pf_base[hartid] + x)) + +/* Macros for register bit masks. */ +#define REG_MASK_BITWIDTH_1 0x01 +#define REG_MASK_BITWIDTH_2 0x03 +#define REG_MASK_BITWIDTH_4 0x0f +#define REG_MASK_BITWIDTH_5 0x1f +#define REG_MASK_BITWIDTH_6 0x3f + +/* Macros to specify register bit shift. */ +#define REG_BITSHIFT_2 2 +#define REG_BITSHIFT_4 4 +#define REG_BITSHIFT_8 8 +#define REG_BITSHIFT_9 9 +#define REG_BITSHIFT_13 13 +#define REG_BITSHIFT_14 14 +#define REG_BITSHIFT_19 19 +#define REG_BITSHIFT_20 20 +#define REG_BITSHIFT_21 21 +#define REG_BITSHIFT_28 28 +#define REG_BITSHIFT_29 29 + +/* Array of base addresses with HART IDs as the index. */ +unsigned long l2pf_base[] = METAL_SIFIVE_L2PF1_BASE_ADDR; +int l2pf_base_len = sizeof(l2pf_base) / sizeof(unsigned long); + +/* Array of distance bits with HART IDs as the index. */ +unsigned long l2pf_distance_bits[] = METAL_SIFIVE_L2PF1_DISTANCE_BITS; + +static void _sifive_l2pf1_set_config(int hartid, sifive_l2pf1_config *config) { + uint32_t val; + + /* Check for NULL, valid base address. */ + if ((config) && (hartid < l2pf_base_len) && (l2pf_base[hartid] != 0UL)) { + uint32_t distance_bits_mask = (1 << l2pf_distance_bits[hartid]) - 1; + + /* Set basic control register configuration values. */ + val = (uint32_t)( + (config->ScalarLoadSupportEn & REG_MASK_BITWIDTH_1) | + ((config->Dist & distance_bits_mask) << REG_BITSHIFT_2) | + ((config->MaxAllowedDist & distance_bits_mask) << REG_BITSHIFT_8) | + ((config->LinToExpThrd & distance_bits_mask) << REG_BITSHIFT_14) | + ((config->CrossPageEn & REG_MASK_BITWIDTH_1) << REG_BITSHIFT_28) | + ((config->ForgiveThrd & REG_MASK_BITWIDTH_1) << REG_BITSHIFT_29)); + + /* Set L2 user bits control register configuration values. */ + REGW(METAL_SIFIVE_L2PF1_BASIC_CONTROL) = val; + + val = (uint32_t)( + (config->QFullnessThrd & REG_MASK_BITWIDTH_4) | + ((config->HitCacheThrd & REG_MASK_BITWIDTH_5) << REG_BITSHIFT_4) | + ((config->HitMSHRThrd & REG_MASK_BITWIDTH_4) << REG_BITSHIFT_9) | + ((config->Window & REG_MASK_BITWIDTH_6) << REG_BITSHIFT_13) | + ((config->ScalarStoreSupportEn & REG_MASK_BITWIDTH_1) + << REG_BITSHIFT_19) | + ((config->VectorLoadSupportEn & REG_MASK_BITWIDTH_1) + << REG_BITSHIFT_20) | + ((config->VectorStoreSupportEn & REG_MASK_BITWIDTH_1) + << REG_BITSHIFT_21)); + + REGW(METAL_SIFIVE_L2PF1_USER_CONTROL) = val; + } +} + +static void _sifive_l2pf1_set_all_harts_config(sifive_l2pf1_config *config) { + for (int hartid = 0; hartid < l2pf_base_len; hartid++) { + _sifive_l2pf1_set_config(hartid, config); + } +} + +void sifive_l2pf1_enable(void) { + int hartid; + __asm__ volatile("csrr %0, mhartid" : "=r"(hartid)); + + if ((hartid < l2pf_base_len) && (l2pf_base[hartid] != 0UL)) { + uint32_t val = REGW(METAL_SIFIVE_L2PF1_BASIC_CONTROL); + + /* Enable L2 stride prefetch unit for current hart. */ + val |= REG_MASK_BITWIDTH_1; + + REGW(METAL_SIFIVE_L2PF1_BASIC_CONTROL) = val; + } +} + +void sifive_l2pf1_disable(void) { + int hartid; + __asm__ volatile("csrr %0, mhartid" : "=r"(hartid)); + + if ((hartid < l2pf_base_len) && (l2pf_base[hartid] != 0UL)) { + uint32_t val = REGW(METAL_SIFIVE_L2PF1_BASIC_CONTROL); + + /* Disable L2 stride prefetch unit for current hart. */ + val &= ~REG_MASK_BITWIDTH_1; + + REGW(METAL_SIFIVE_L2PF1_BASIC_CONTROL) = val; + } +} + +void sifive_l2pf1_get_config(sifive_l2pf1_config *config) { + int hartid; + __asm__ volatile("csrr %0, mhartid" : "=r"(hartid)); + uint32_t val; + + /* Check for NULL, valid base address. */ + if ((config) && (hartid < l2pf_base_len) && (l2pf_base[hartid] != 0UL)) { + uint32_t distance_bits_mask = (1 << l2pf_distance_bits[hartid]) - 1; + + /* Get basic control register configuration values. */ + val = REGW(METAL_SIFIVE_L2PF1_BASIC_CONTROL); + + config->ScalarLoadSupportEn = (val & REG_MASK_BITWIDTH_1); + config->Dist = ((val >> REG_BITSHIFT_2) & distance_bits_mask); + config->MaxAllowedDist = ((val >> REG_BITSHIFT_8) & distance_bits_mask); + config->LinToExpThrd = ((val >> REG_BITSHIFT_14) & distance_bits_mask); + config->CrossPageEn = ((val >> REG_BITSHIFT_28) & REG_MASK_BITWIDTH_1); + config->ForgiveThrd = ((val >> REG_BITSHIFT_29) & REG_MASK_BITWIDTH_2); + + /* Get L2 user bits control register configuration values. */ + val = REGW(METAL_SIFIVE_L2PF1_USER_CONTROL); + + config->QFullnessThrd = (val & REG_MASK_BITWIDTH_4); + config->HitCacheThrd = ((val >> REG_BITSHIFT_4) & REG_MASK_BITWIDTH_5); + config->HitMSHRThrd = ((val >> REG_BITSHIFT_9) & REG_MASK_BITWIDTH_4); + config->Window = ((val >> REG_BITSHIFT_13) & REG_MASK_BITWIDTH_6); + config->ScalarStoreSupportEn = + ((val >> REG_BITSHIFT_19) & REG_MASK_BITWIDTH_1); + config->VectorLoadSupportEn = + ((val >> REG_BITSHIFT_20) & REG_MASK_BITWIDTH_1); + config->VectorStoreSupportEn = + ((val >> REG_BITSHIFT_21) & REG_MASK_BITWIDTH_1); + } +} + +void sifive_l2pf1_set_config(sifive_l2pf1_config *config) { + int hartid; + __asm__ volatile("csrr %0, mhartid" : "=r"(hartid)); + _sifive_l2pf1_set_config(hartid, config); +} + +void sifive_l2pf1_init(void) { + sifive_l2pf1_config config; + + /* Basic control register initial configuration (0x15811). */ + config.ScalarLoadSupportEn = 0x1; + config.Dist = 0x4; + config.MaxAllowedDist = 0x18; + config.LinToExpThrd = 0x5; + + /* L2 user bits control register initial configuration (0x38c84e). */ + config.QFullnessThrd = 0xe; + config.HitCacheThrd = 0x4; + config.HitMSHRThrd = 0x4; + config.Window = 0x6; + config.ScalarStoreSupportEn = 0x1; + config.VectorLoadSupportEn = 0x1; + config.VectorStoreSupportEn = 0x1; + + _sifive_l2pf1_set_all_harts_config(&config); +} + +#endif + +typedef int no_empty_translation_units; diff --git a/arch/riscv32/fe310/src/freedom-metal/src/drivers/sifive_local-external-interrupts0.c b/arch/riscv32/fe310/src/freedom-metal/src/drivers/sifive_local-external-interrupts0.c new file mode 100644 index 00000000..99b5e3a8 --- /dev/null +++ b/arch/riscv32/fe310/src/freedom-metal/src/drivers/sifive_local-external-interrupts0.c @@ -0,0 +1,169 @@ +/* Copyright 2018 SiFive, Inc */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include + +#ifdef METAL_SIFIVE_LOCAL_EXTERNAL_INTERRUPTS0 + +#include +#include +#include + +void __metal_driver_sifive_local_external_interrupt_init( + struct metal_interrupt *controller) { + struct __metal_driver_sifive_local_external_interrupts0 *local0; + + local0 = + (struct __metal_driver_sifive_local_external_interrupts0 *)(controller); + if (!local0->init_done) { + struct metal_interrupt *intc = + __metal_driver_sifive_local_external_interrupts0_interrupt_parent( + controller); + + if (intc) { + /* Register its interruptswith with parent controller, aka all + * external to default isr */ + for ( + int i = 0; + i < + __metal_driver_sifive_local_external_interrupts0_num_interrupts( + controller); + i++) { + intc->vtable->interrupt_register( + intc, + __metal_driver_sifive_local_external_interrupts0_interrupt_lines( + controller, i), + NULL, controller); + } + local0->init_done = 1; + } + } +} + +int __metal_driver_sifive_local_external_interrupt_register( + struct metal_interrupt *controller, int id, metal_interrupt_handler_t isr, + void *priv) { + int rc = -1; + + if (id != 0) { + struct metal_interrupt *intc = + __metal_driver_sifive_local_external_interrupts0_interrupt_parent( + controller); + + /* Enable its interrupts with parent controller */ + if (intc) { + rc = intc->vtable->interrupt_register(intc, id, isr, priv); + } + } + return rc; +} + +int __metal_driver_sifive_local_external_interrupt_enable( + struct metal_interrupt *controller, int id) { + int rc = -1; + + if (id != 0) { + struct metal_interrupt *intc = + __metal_driver_sifive_local_external_interrupts0_interrupt_parent( + controller); + + /* Enable its interrupts with parent controller */ + if (intc) { + rc = intc->vtable->interrupt_enable(intc, id); + } + } + return rc; +} + +int __metal_driver_sifive_local_external_interrupt_disable( + struct metal_interrupt *controller, int id) { + int rc = -1; + + if (id != 0) { + struct metal_interrupt *intc = + __metal_driver_sifive_local_external_interrupts0_interrupt_parent( + controller); + + /* Enable its interrupts with parent controller */ + if (intc) { + rc = intc->vtable->interrupt_disable(intc, id); + } + } + return rc; +} + +int __metal_driver_sifive_local_external_interrupt_set_threshold( + struct metal_interrupt *controller, unsigned int threshold) { + /* Core controller does not support threshold configuration */ + return -1; +} + +unsigned int __metal_driver_sifive_local_external_interrupt_get_threshold( + struct metal_interrupt *controller) { + /* Core controller does not support threshold configuration */ + return 0; +} + +int __metal_driver_sifive_local_external_interrupt_set_priority( + struct metal_interrupt *controller, int id, unsigned int priority) { + /* Core controller does not support priority configuration */ + return -1; +} + +unsigned int __metal_driver_sifive_local_external_interrupt_get_priority( + struct metal_interrupt *controller, int id) { + /* Core controller does not support priority configuration */ + return 0; +} + +int __metal_driver_sifive_local_external_command_request( + struct metal_interrupt *controller, int command, void *data) { + int idx; + int rc = -1; + + switch (command) { + case METAL_MAX_INTERRUPT_GET: + rc = __metal_driver_sifive_local_external_interrupts0_num_interrupts( + controller); + break; + case METAL_INDEX_INTERRUPT_GET: + rc = 0; + if (data) { + idx = *(int *)data; + rc = + __metal_driver_sifive_local_external_interrupts0_interrupt_lines( + controller, idx); + } + break; + default: + break; + } + + return rc; +} + +__METAL_DEFINE_VTABLE( + __metal_driver_vtable_sifive_local_external_interrupts0) = { + .local0_vtable.interrupt_init = + __metal_driver_sifive_local_external_interrupt_init, + .local0_vtable.interrupt_register = + __metal_driver_sifive_local_external_interrupt_register, + .local0_vtable.interrupt_enable = + __metal_driver_sifive_local_external_interrupt_enable, + .local0_vtable.interrupt_disable = + __metal_driver_sifive_local_external_interrupt_disable, + .local0_vtable.interrupt_get_threshold = + __metal_driver_sifive_local_external_interrupt_get_threshold, + .local0_vtable.interrupt_set_threshold = + __metal_driver_sifive_local_external_interrupt_set_threshold, + .local0_vtable.interrupt_get_priority = + __metal_driver_sifive_local_external_interrupt_get_priority, + .local0_vtable.interrupt_set_priority = + __metal_driver_sifive_local_external_interrupt_set_priority, + .local0_vtable.command_request = + __metal_driver_sifive_local_external_command_request, +}; + +#endif + +typedef int no_empty_translation_units; diff --git a/arch/riscv32/fe310/src/freedom-metal/src/drivers/sifive_pl2cache0.c b/arch/riscv32/fe310/src/freedom-metal/src/drivers/sifive_pl2cache0.c new file mode 100644 index 00000000..2fc43b5f --- /dev/null +++ b/arch/riscv32/fe310/src/freedom-metal/src/drivers/sifive_pl2cache0.c @@ -0,0 +1,214 @@ +/* Copyright 2021 SiFive, Inc */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include + +#ifdef METAL_SIFIVE_PL2CACHE0 + +#include +#include +#include +#include + +/* Macros to access memory mapped registers */ +#define REGW(_x_) *(volatile uint32_t *)(pl2cache_base[hartid] + (_x_)) + +#define REGD(_x_) *(volatile uint64_t *)(pl2cache_base[hartid] + (_x_)) + +/* Macros to specify register bit shift */ +#define REG_SHIFT_4 4 +#define REG_SHIFT_8 8 +#define REG_SHIFT_16 16 +#define REG_SHIFT_24 24 + +#define SIFIVE_PL2CACHE0_BYTE_MASK 0xFFUL + +/* Array of base addresses with HART IDs as the index */ +unsigned long pl2cache_base[] = METAL_SIFIVE_PL2CACHE0_BASE_ADDR; + +void sifive_pl2cache0_set_cleanEvictenale_bit(bool val) { + sifive_pl2cache0_configbits tmp; + int hartid; + __asm__ volatile("csrr %0, mhartid" : "=r"(hartid)); + + tmp.w = REGW(METAL_SIFIVE_PL2CACHE0_CONFIGBITS); + tmp.b.cleanEvictEnable = val; + REGW(METAL_SIFIVE_PL2CACHE0_CONFIGBITS) = tmp.w; +} + +void sifive_pl2cache0_init(void) { +#ifndef METAL_SIFIVE_CCACHE0 + // (L3 CACHE) CCACHE0 is not present + sifive_pl2cache0_set_cleanEvictenale_bit(0); +#else + sifive_pl2cache0_set_cleanEvictenale_bit(1); +#endif /* METAL_SIFIVE_CCACHE0 */ +} + +void sifive_pl2cache0_get_config(sifive_pl2cache0_config *config) { + if (config) /* Check for NULL */ + { + uint32_t val; + int hartid; + __asm__ volatile("csrr %0, mhartid" : "=r"(hartid)); + + val = REGW(METAL_SIFIVE_PL2CACHE0_CONFIG); + + /* Populate cache configuration data */ + config->num_bank = (val & SIFIVE_PL2CACHE0_BYTE_MASK); + config->num_ways = ((val >> REG_SHIFT_8) & SIFIVE_PL2CACHE0_BYTE_MASK); + /* no. of sets, block size is 2's power of register value + (2 << (value-1)) */ + config->num_sets = + 2 << (((val >> REG_SHIFT_16) & SIFIVE_PL2CACHE0_BYTE_MASK) - 1); + config->block_size = + 2 << (((val >> REG_SHIFT_24) & SIFIVE_PL2CACHE0_BYTE_MASK) - 1); + } +} + +uint32_t sifive_pl2cache0_get_enabled_ways(void) { + sifive_pl2cache0_config config = {0, 0, 0, 0}; + sifive_pl2cache0_get_config(&config); + return config.num_ways; +} + +int sifive_pl2cache0_set_enabled_ways(uint32_t ways) { + sifive_pl2cache0_config config = {0, 0, 0, 0}; + sifive_pl2cache0_get_config(&config); + return ways <= config.num_ways ? 0 : -1; +} + +void sifive_pl2cache0_inject_ecc_error(uint32_t bitindex, + sifive_pl2cache0_ecc_errtarget_t target, + sifive_pl2cache0_ecc_errdirection_t dir, + sifive_pl2cache0_ecc_errtype_t type) { + int hartid; + __asm__ volatile("csrr %0, mhartid" : "=r"(hartid)); + + /* Induce ECC error at given bit index and location */ + REGW(METAL_SIFIVE_PL2CACHE0_ECCINJECTERROR) = + (uint32_t)(((target & 0x03) << (REG_SHIFT_16 + 0)) | + ((dir & 0x01) << (REG_SHIFT_16 + 2u)) | + ((type & 0x01) << (REG_SHIFT_16 + 3u)) | (bitindex & 0xFF)); +} + +void sifive_pl2cache0_flush(uintptr_t flush_addr) { + int hartid; + __asm__ volatile("csrr %0, mhartid" : "=r"(hartid)); + + /* Block memory access until operation completed */ + __asm volatile("fence rw, io" : : : "memory"); + +#if __riscv_xlen == 32 + REGW(METAL_SIFIVE_PL2CACHE0_CFLUSH64) = flush_addr; + REGW(METAL_SIFIVE_PL2CACHE0_CFLUSH64 + sizeof(uint32_t)) = 0x13u << 24u; +#else + REGD(METAL_SIFIVE_PL2CACHE0_CFLUSH64) = flush_addr | (0x13ull << 24u); +#endif + + __asm volatile("fence io, rw" : : : "memory"); +} + +uint32_t sifive_pl2cache0_get_flush_count(void) { + int hartid; + __asm__ volatile("csrr %0, mhartid" : "=r"(hartid)); + + return REGW(METAL_SIFIVE_PL2CACHE0_FLUSHCOUNT) & 0xFFu; +} + +void sifive_pl2cache0_set_pmevent_selector(uint32_t counter, uint64_t mask) { +#if METAL_SIFIVE_PL2CACHE0_PERFMON_COUNTERS > 0 + if (counter < METAL_SIFIVE_PL2CACHE0_PERFMON_COUNTERS) { + int hartid; + __asm__ volatile("csrr %0, mhartid" : "=r"(hartid)); + + /* Set event selector for specified L2 event counter */ + REGD(METAL_SIFIVE_PL2CACHE0_PMEVENTSELECT0 + counter * 8) = mask; + } +#endif + return; +} + +uint64_t sifive_pl2cache0_get_pmevent_selector(uint32_t counter) { + uint64_t val = 0; + +#if METAL_SIFIVE_PL2CACHE0_PERFMON_COUNTERS > 0 + if (counter < METAL_SIFIVE_PL2CACHE0_PERFMON_COUNTERS) { + int hartid; + __asm__ volatile("csrr %0, mhartid" : "=r"(hartid)); + + /* Get event selector for specified L2 event counter */ + val = REGD(METAL_SIFIVE_PL2CACHE0_PMEVENTSELECT0 + counter * 8); + } +#endif + return val; +} + +void sifive_pl2cache0_clr_pmevent_counter(uint32_t counter) { + +#if METAL_SIFIVE_PL2CACHE0_PERFMON_COUNTERS > 0 + if (counter < METAL_SIFIVE_PL2CACHE0_PERFMON_COUNTERS) { + int hartid; + __asm__ volatile("csrr %0, mhartid" : "=r"(hartid)); + + /* Clear specified L2 event counter */ + REGD(METAL_SIFIVE_PL2CACHE0_PMEVENTCOUNTER0 + counter * 8) = 0; + } +#endif + return; +} + +uint64_t sifive_pl2cache0_get_pmevent_counter(uint32_t counter) { +#if __riscv_xlen == 32 + uint32_t vh = 0, vh1 = 0, vl = 0; +#else + uint64_t val = 0; +#endif +#if METAL_SIFIVE_PL2CACHE0_PERFMON_COUNTERS > 0 + if (counter < METAL_SIFIVE_PL2CACHE0_PERFMON_COUNTERS) { + int hartid; + __asm__ volatile("csrr %0, mhartid" : "=r"(hartid)); + + /* Set counter register offset */ + counter *= 8; + +#if __riscv_xlen == 32 + do { + vh = REGW(METAL_SIFIVE_PL2CACHE0_PMEVENTCOUNTER0 + counter + 4); + vl = REGW(METAL_SIFIVE_PL2CACHE0_PMEVENTCOUNTER0 + counter); + vh1 = REGW(METAL_SIFIVE_PL2CACHE0_PMEVENTCOUNTER0 + counter + 4); + } while (vh != vh1); +#else + val = REGD(METAL_SIFIVE_PL2CACHE0_PMEVENTCOUNTER0 + counter); +#endif + } +#endif +#if __riscv_xlen == 32 + return ((((unsigned long long)vh) << 32) | vl); +#else + return val; +#endif +} + +void sifive_pl2cache0_set_client_filter(uint64_t mask) { + int hartid; + __asm__ volatile("csrr %0, mhartid" : "=r"(hartid)); + + /* Set clients to be excluded from performance monitoring */ + REGD(METAL_SIFIVE_PL2CACHE0_PMCLIENTFILTER) = mask; +} + +uint64_t sifive_pl2cache0_get_client_filter(void) { + uint64_t val = 0; + int hartid; + __asm__ volatile("csrr %0, mhartid" : "=r"(hartid)); + + /* Get currently active client filter mask */ + val = REGD(METAL_SIFIVE_PL2CACHE0_PMCLIENTFILTER); + + return val; +} + +#endif + +typedef int no_empty_translation_units; diff --git a/arch/riscv32/fe310/src/freedom-metal/src/drivers/sifive_prci0.c b/arch/riscv32/fe310/src/freedom-metal/src/drivers/sifive_prci0.c new file mode 100644 index 00000000..bb404e63 --- /dev/null +++ b/arch/riscv32/fe310/src/freedom-metal/src/drivers/sifive_prci0.c @@ -0,0 +1,275 @@ +/* Copyright 2021 SiFive, Inc */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include + +#if defined(METAL_SIFIVE_FU540_C000_PRCI0) || \ + defined(METAL_SIFIVE_FU740_C000_PRCI0) + +#include +#include +#include + +/* Default setting of PLL */ +#define PLL_R_default 0x1 +#define PLL_F_default 0x1F +#define PLL_Q_default 0x3 +#define PLL_RANGE_default 0x0 +#define PLL_BYPASS_default 0x1 +#define PLL_FSE_default 0x1 + +#define PLLOUT_DIV_default 0x0 +#define PLLOUT_DIV_BY_1_default 0x0 +#define PLLOUT_CLK_EN_default 0x0 + +/* Bit field of PLLCFG */ +#define PLL_R(x) (((x)&0x3F) << 0) +#define PLL_F(x) (((x)&0x1FF) << 6) +#define PLL_Q(x) (((x)&0x7) << 15) +#define PLL_RANGE(x) (((x)&0x7) << 18) +#define PLL_BYPASS(x) (((x)&0x1) << 24) +#define PLL_FSE(x) (((x)&0x1) << 25) +#define PLL_LOCK(x) (((x)&0x1) << 31) + +/* Bit field of PLLOUT */ +#define PLLOUT_DIV(x) (((x)&0x7F) << 0) +#define PLLOUT_DIV_BY_1(x) (((x)&0x1) << 8) +#define PLLOUT_CLK_EN(x) (((x)&0x1) << 31) + +/* Bit field of CORECLKSELREG (0x24) */ +#define PLL_CORECLKSEL_HFXIN 0x1 +#define PLL_CORECLKSEL_COREPLL 0x0 + +/* Bit field of CLKMUXSTATUS (0x2c) */ +#define CLKMUX_STATUS_CORECLKPLLSEL (0x1 << 0) +#define CLKMUX_STATUS_TLCLKSEL (0x1 << 1) +#define CLKMUX_STATUS_RTCXSEL (0x1 << 2) +#define CLKMUX_STATUS_DDRCTRLCLKSEL (0x1 << 3) +#define CLKMUX_STATUS_DDRPHYCLKSEL (0x1 << 4) +#define CLKMUX_STATUS_GEMGXLCLKSEL (0x1 << 6) +#define CLKMUX_STATUS_MAINMEMCLKSEL (0x1 << 7) + +/* Bit field of DEVICESRESETN (0x28) */ +#define DEVICESRESET_DDR_CTRL_RST_N(x) (((x)&0x1) << 0) +#define DEVICESRESET_DDR_AXI_RST_N(x) (((x)&0x1) << 1) +#define DEVICESRESET_DDR_AHB_RST_N(x) (((x)&0x1) << 2) +#define DEVICESRESET_DDR_PHY_RST_N(x) (((x)&0x1) << 3) +#define DEVICESRESET_PCIE_POWER_UP_RST_N(x) (((x)&0x1) << 4) +#define DEVICESRESET_GEMGXL_RST_N(x) (((x)&0x1) << 5) +#define DEVICESRESET_CLTX_RST_N(x) (((x)&0x1) << 6) + +/* Bit field of PRCI_PLLS (0xE0) */ +#define PRCIPLL_CLTXPLL (0x1 << 0) +#define PRCIPLL_GEMGXLPLL (0x1 << 1) +#define PRCIPLL_DDRPLL (0x1 << 2) +#define PRCIPLL_HFPCLKPLL (0x1 << 3) +#define PRCIPLL_DVFSCOREPLL (0x1 << 4) +#define PRCIPLL_COREPLL (0x1 << 5) + +#define PRCI_REG(x) __METAL_ACCESS_ONCE((__metal_io_u32 *)(control_base + x)) + +unsigned long __metal_driver_sifive_prci0_get_reg(struct metal_prci *prci, + unsigned long offset) { + unsigned long control_base = __metal_driver_sifive_prci0_control_base(prci); + if (!control_base) + return 0; + + return PRCI_REG(offset); +} + +unsigned long __metal_driver_sifive_prci0_set_reg(struct metal_prci *prci, + unsigned long offset, + unsigned long value) { + unsigned long control_base = __metal_driver_sifive_prci0_control_base(prci); + if (!control_base) + return 0; + + return PRCI_REG(offset) = value; +} + +#if defined(METAL_SIFIVE_FU540_C000_PRCI0) +void __metal_driver_sifive_prci0_init(void) { + struct metal_prci *prci = metal_prci_get_device(); + unsigned long control_base = __metal_driver_sifive_prci0_control_base(prci); + + if (!control_base) + return; + + /* CORE PLL */ + uint32_t coreHz; + if (PRCI_REG(METAL_SIFIVE_PRCI0_CLKMUXSTATUS) & CLKMUX_STATUS_TLCLKSEL) { + /* 500 MHz */ + coreHz = (PLL_R(0)) | (PLL_F(59)) | // 4000MHz VCO + (PLL_Q(3)) | // /8 Output divider + (PLL_RANGE(0x4)) | (PLL_BYPASS(0)) | (PLL_FSE(1)); + } else { + /* 1 GHz */ + coreHz = (PLL_R(0)) | (PLL_F(59)) | // 4000MHz VCO + (PLL_Q(2)) | // /4 Output divider + (PLL_RANGE(0x4)) | (PLL_BYPASS(0)) | (PLL_FSE(1)); + } + + PRCI_REG(METAL_SIFIVE_PRCI0_COREPLLCFG) = coreHz; + + /* wait for lock */ + while ((PRCI_REG(METAL_SIFIVE_PRCI0_COREPLLCFG) & (PLL_LOCK(1))) == 0) + ; + + uint32_t core_out = (PLLOUT_DIV(PLLOUT_DIV_default)) | + (PLLOUT_DIV_BY_1(PLLOUT_DIV_BY_1_default)) | + (PLLOUT_CLK_EN(1)); + + PRCI_REG(METAL_SIFIVE_PRCI0_COREPLLOUTDIV) = core_out; + + /* set CORECLKSELREG to select COREPLL */ + PRCI_REG(METAL_SIFIVE_PRCI0_CORECLKSELREG) = PLL_CORECLKSEL_COREPLL; + + /* GEMGXL PLL */ + uint32_t gemgxl125mhz = (PLL_R(0)) | (PLL_F(59)) | // 4000Mhz VCO + (PLL_Q(5)) | // /32 + (PLL_RANGE(0x4)) | (PLL_BYPASS(0)) | (PLL_FSE(1)); + + PRCI_REG(METAL_SIFIVE_PRCI0_GEMGXLPLLCFG) = gemgxl125mhz; + + /* wait for lock */ + while ((PRCI_REG(METAL_SIFIVE_PRCI0_GEMGXLPLLCFG) & PLL_LOCK(1)) == 0) + ; + + uint32_t gemgxlctl_out = (PLLOUT_DIV(PLLOUT_DIV_default)) | + (PLLOUT_DIV_BY_1(PLLOUT_DIV_BY_1_default)) | + (PLLOUT_CLK_EN(1)); + + PRCI_REG(METAL_SIFIVE_PRCI0_GEMGXLPLLOUTDIV) = gemgxlctl_out; + + /* release gemgxl reset */ + PRCI_REG(METAL_SIFIVE_PRCI0_DEVICESRESETN) |= DEVICESRESET_GEMGXL_RST_N(1); + + /* procmon => core clock */ + PRCI_REG(METAL_SIFIVE_PRCI0_PROCMONCFG) = 0x1 << 24; +} +#elif defined(METAL_SIFIVE_FU740_C000_PRCI0) +void __metal_driver_sifive_prci0_init(void) { + struct metal_prci *prci = metal_prci_get_device(); + unsigned long control_base = __metal_driver_sifive_prci0_control_base(prci); + + if (!control_base) + return; + + /* CORE PLL */ + uint32_t coreHz; + if (PRCI_REG(METAL_SIFIVE_PRCI0_CLKMUXSTATUS) & CLKMUX_STATUS_TLCLKSEL) { + /* 500 MHz */ + coreHz = (PLL_R(0)) | (PLL_F(76)) | // 4000MHz VCO + (PLL_Q(3)) | // /8 Output divider + (PLL_RANGE(0x3)) | // 26MHz is between 18-38MHz + (PLL_BYPASS(0)) | (PLL_FSE(1)); + } else { + /* 1 GHz */ + coreHz = (PLL_R(0)) | (PLL_F(76)) | // 4000MHz VCO + (PLL_Q(2)) | // /4 Output divider + (PLL_RANGE(0x3)) | // 26MHz is between 18-38MHz + (PLL_BYPASS(0)) | (PLL_FSE(1)); + } + + PRCI_REG(METAL_SIFIVE_PRCI0_COREPLLCFG) = coreHz; + + /* wait for lock */ + while ((PRCI_REG(METAL_SIFIVE_PRCI0_COREPLLCFG) & (PLL_LOCK(1))) == 0) + ; + + uint32_t core_out = (PLLOUT_DIV(PLLOUT_DIV_default)) | + (PLLOUT_DIV_BY_1(PLLOUT_DIV_BY_1_default)) | + (PLLOUT_CLK_EN(1)); + + PRCI_REG(METAL_SIFIVE_PRCI0_COREPLLOUTDIV) = core_out; + + /* set CORECLKSELREG to select COREPLL */ + PRCI_REG(METAL_SIFIVE_PRCI0_CORECLKSELREG) = PLL_CORECLKSEL_COREPLL; + + if (PRCI_REG(METAL_SIFIVE_PRCI0_PRCIPLLS) & PRCIPLL_HFPCLKPLL) { + /* HFPCLK PLL */ + uint32_t hfpclk250mhz = (PLL_R(0)) | (PLL_F(79)) | // 4000MHz VCO + (PLL_Q(4)) | // /16 + (PLL_RANGE(0x3)) | (PLL_BYPASS(1)) | + (PLL_FSE(1)); + + PRCI_REG(METAL_SIFIVE_PRCI0_HFPCLKPLLCFG) |= PLL_BYPASS(1); + PRCI_REG(METAL_SIFIVE_PRCI0_HFPCLKPLLCFG) = hfpclk250mhz; + PRCI_REG(METAL_SIFIVE_PRCI0_HFPCLKPLLCFG) &= ~(PLL_BYPASS(1)); + + /* wait for lock */ + while ((PRCI_REG(METAL_SIFIVE_PRCI0_HFPCLKPLLCFG) & PLL_LOCK(1)) == 0) + ; + + uint32_t hfpclkpll_out = (PLLOUT_DIV(PLLOUT_DIV_default)) | + (PLLOUT_DIV_BY_1(PLLOUT_DIV_BY_1_default)) | + (PLLOUT_CLK_EN(1)); + + PRCI_REG(METAL_SIFIVE_PRCI0_HFPCLKPLLOUTDIV) = hfpclkpll_out; + + /* switch pclk to run from HFPCLKPLL instead of the 26MHz hfclk */ + PRCI_REG(METAL_SIFIVE_PRCI0_HFPCLKPLLSEL) = 0; + } else if (PRCI_REG(METAL_SIFIVE_PRCI0_PRCIPLLS) & PRCIPLL_CLTXPLL) { + /* CLTX PLL */ + uint32_t cltx250mhz = (PLL_R(0)) | (PLL_F(79)) | // 4000MHz VCO + (PLL_Q(4)) | // /16 + (PLL_RANGE(0x3)) | (PLL_BYPASS(1)) | (PLL_FSE(1)); + + PRCI_REG(METAL_SIFIVE_PRCI0_CLTXPLLCFG) |= PLL_BYPASS(1); + PRCI_REG(METAL_SIFIVE_PRCI0_CLTXPLLCFG) = cltx250mhz; + PRCI_REG(METAL_SIFIVE_PRCI0_CLTXPLLCFG) &= ~(PLL_BYPASS(1)); + + /* wait for lock */ + while ((PRCI_REG(METAL_SIFIVE_PRCI0_CLTXPLLCFG) & PLL_LOCK(1)) == 0) + ; + + uint32_t cltxctl_out = (PLLOUT_DIV(PLLOUT_DIV_default)) | + (PLLOUT_DIV_BY_1(PLLOUT_DIV_BY_1_default)) | + (PLLOUT_CLK_EN(1)); + + PRCI_REG(METAL_SIFIVE_PRCI0_CLTXPLLOUTDIV) = cltxctl_out; + } + + /* GEMGXL PLL */ + uint32_t gemgxl125mhz = (PLL_R(0)) | (PLL_F(76)) | // 4000Mhz VCO + (PLL_Q(5)) | // /32 + (PLL_RANGE(0x3)) | (PLL_BYPASS(1)) | (PLL_FSE(1)); + + PRCI_REG(METAL_SIFIVE_PRCI0_GEMGXLPLLCFG) |= PLL_BYPASS(1); + PRCI_REG(METAL_SIFIVE_PRCI0_GEMGXLPLLCFG) = gemgxl125mhz; + PRCI_REG(METAL_SIFIVE_PRCI0_GEMGXLPLLCFG) &= ~(PLL_BYPASS(1)); + + /* wait for lock */ + while ((PRCI_REG(METAL_SIFIVE_PRCI0_GEMGXLPLLCFG) & PLL_LOCK(1)) == 0) + ; + + uint32_t gemgxlctl_out = (PLLOUT_DIV(PLLOUT_DIV_default)) | + (PLLOUT_DIV_BY_1(PLLOUT_DIV_BY_1_default)) | + (PLLOUT_CLK_EN(1)); + + PRCI_REG(METAL_SIFIVE_PRCI0_GEMGXLPLLOUTDIV) = gemgxlctl_out; + + /* release gemgxl reset */ + PRCI_REG(METAL_SIFIVE_PRCI0_DEVICESRESETN) |= DEVICESRESET_GEMGXL_RST_N(1); + + /* procmon => core clock */ + PRCI_REG(METAL_SIFIVE_PRCI0_PROCMONCFG) = 0x1 << 24; + + /* release Chiplink reset */ + PRCI_REG(METAL_SIFIVE_PRCI0_DEVICESRESETN) |= DEVICESRESET_CLTX_RST_N(1); +} +#endif + +METAL_CONSTRUCTOR(metal_sifive_prci_init) { + __metal_driver_sifive_prci0_init(); +} + +__METAL_DEFINE_VTABLE(__metal_driver_vtable_sifive_prci0) = { + .prci.get_reg = __metal_driver_sifive_prci0_get_reg, + .prci.set_reg = __metal_driver_sifive_prci0_set_reg, +}; + +#endif /* METAL_SIFIVE_FU540_C000_PRCI0 || METAL_SIFIVE_FU740_C000_PRCI0 */ + +/* From 545bbbf ("Fix empty translation unit warning") ? */ +typedef int no_empty_translation_units; diff --git a/arch/riscv32/fe310/src/freedom-metal/src/drivers/sifive_pwm0.c b/arch/riscv32/fe310/src/freedom-metal/src/drivers/sifive_pwm0.c new file mode 100644 index 00000000..415d4682 --- /dev/null +++ b/arch/riscv32/fe310/src/freedom-metal/src/drivers/sifive_pwm0.c @@ -0,0 +1,340 @@ +/* Copyright 2020 SiFive, Inc */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include + +#ifdef METAL_SIFIVE_PWM0 +#include +#include +#include +#include +#include +#include +#include +#include + +/* Register fields */ +#define METAL_PWMCFG_STICKY (1UL << 8) +#define METAL_PWMCFG_ZEROCMP (1UL << 9) +#define METAL_PWMCFG_DEGLITCH (1UL << 10) +#define METAL_PWMCFG_ENALWAYS (1UL << 12) +#define METAL_PWMCFG_ENONESHOT (1UL << 13) +#define METAL_PWMCFG_CMPCENTER(x) (1UL << (16 + x)) +#define METAL_PWMCFG_CMPIP(x) (1UL << (28 + x)) +#define METAL_SIFIVE_PWM0_PWMCMP(x) (METAL_SIFIVE_PWM0_PWMCMP0 + (x * 4)) + +/* Macros to access registers */ +#define METAL_PWM_REG(offset) ((base + offset)) +#define METAL_PWM_REGW(offset) \ + (__METAL_ACCESS_ONCE((__metal_io_u32 *)METAL_PWM_REG(offset))) + +/* Macro to get PWM compare count */ +#define METAL_PWM_GETCMPVAL(duty) (duty * pwm->count_val) / 100U +/* Max duty cycle value */ +#define METAL_PWM_MAXDUTY 100UL +/* Max pre-scalar value */ +#define METAL_PWM_MAXPRESCAL 15UL + +/* Check endianess */ +#if __BYTE_ORDER__ != __ORDER_LITTLE_ENDIAN__ +#error *** Unsupported endianess *** +#endif + +#if (METAL_MAX_PWM0_NCMP > METAL_MAX_PWM_CHANNELS) +#error *** METAL_MAX_PWM_CHANNELS exceeded *** +#endif + +/* Return values */ +#define METAL_PWM_RET_OK 0 +#define METAL_PWM_RET_ERR -1 + +static void pre_rate_change_callback(void *priv) { + struct metal_pwm *gpwm = priv; + /* Disable active PWM instance. */ + gpwm->vtable->stop(gpwm, 0); +} + +static void post_rate_change_callback(void *priv) { + struct __metal_driver_sifive_pwm0 *pwm = priv; + struct metal_pwm *gpwm = priv; + unsigned long base = __metal_driver_sifive_pwm0_control_base(gpwm); + unsigned int cmp_count = __metal_driver_sifive_pwm0_comparator_count(gpwm); + unsigned int idx = 0; + unsigned int duty; + + /* Check if PWM frequency was set */ + if (pwm->freq != 0) { + /* Set frequency after clock rate change */ + gpwm->vtable->set_freq(gpwm, 0, pwm->freq); + + /* Set duty cycle after clock rate change */ + while (++idx < cmp_count) { + duty = pwm->duty[idx]; + if (duty != 0) { + METAL_PWM_REGW(METAL_SIFIVE_PWM0_PWMCMP(idx)) = + METAL_PWM_GETCMPVAL(duty); + } + } + } +} + +static int __metal_driver_sifive_pwm0_enable(struct metal_pwm *gpwm) { + struct __metal_driver_sifive_gpio0 *pinmux = + __metal_driver_sifive_pwm0_pinmux(gpwm); + unsigned long base = __metal_driver_sifive_pwm0_control_base(gpwm); + struct __metal_driver_sifive_pwm0 *pwm = (void *)gpwm; + int ret = METAL_PWM_RET_ERR; + + if (base != 0) { + + if ((pinmux != NULL) && (gpwm != NULL)) { + /* Configure PWM I/O pins */ + long pinmux_output_selector = + __metal_driver_sifive_pwm0_pinmux_output_selector(gpwm); + long pinmux_source_selector = + __metal_driver_sifive_pwm0_pinmux_source_selector(gpwm); + + pinmux->gpio.vtable->enable_io((struct metal_gpio *)pinmux, + pinmux_output_selector, + pinmux_source_selector); + } + + /* Initialize default values */ + pwm->max_count = + (1UL << __metal_driver_sifive_pwm0_compare_width(gpwm)) - 1; + pwm->freq = 0; + pwm->count_val = 0; + METAL_PWM_REGW(METAL_SIFIVE_PWM0_PWMCFG) = 0; + ret = METAL_PWM_RET_OK; + } + return ret; +} + +static int __metal_driver_sifive_pwm0_disable(struct metal_pwm *gpwm) { + struct __metal_driver_sifive_gpio0 *pinmux = + __metal_driver_sifive_pwm0_pinmux(gpwm); + int ret = METAL_PWM_RET_ERR; + + if (gpwm != NULL) { + + if (pinmux != NULL) { + /* Disable PWM I/O pins */ + long pinmux_source_selector = + __metal_driver_sifive_pwm0_pinmux_source_selector(gpwm); + pinmux->gpio.vtable->disable_io((struct metal_gpio *)pinmux, + pinmux_source_selector); + } + + ret = METAL_PWM_RET_OK; + } + return ret; +} + +static int __metal_driver_sifive_pwm0_set_freq(struct metal_pwm *gpwm, + unsigned int idx, + unsigned int freq) { + struct metal_clock *clock = __metal_driver_sifive_pwm0_clock(gpwm); + unsigned long base = __metal_driver_sifive_pwm0_control_base(gpwm); + unsigned int cmp_count = __metal_driver_sifive_pwm0_comparator_count(gpwm); + struct __metal_driver_sifive_pwm0 *pwm = (void *)gpwm; + unsigned int clock_rate; + unsigned int count; + unsigned int prescale = 0; + int ret = METAL_PWM_RET_ERR; + + if ((clock != NULL) && (gpwm != NULL) && (idx < cmp_count)) { + clock_rate = clock->vtable->get_rate_hz(clock); + /* Register clock rate change call-backs */ + if (pwm->freq == 0) { + pwm->pre_rate_change_callback.callback = &pre_rate_change_callback; + pwm->pre_rate_change_callback.priv = pwm; + metal_clock_register_pre_rate_change_callback( + clock, &(pwm->pre_rate_change_callback)); + + pwm->post_rate_change_callback.callback = + &post_rate_change_callback; + pwm->post_rate_change_callback.priv = pwm; + metal_clock_register_post_rate_change_callback( + clock, &(pwm->post_rate_change_callback)); + } + + /* Calculate count value for given PWM frequency */ + do { + count = (clock_rate / (1UL << prescale)) / freq; + } while ((count > pwm->max_count) && + (prescale++ < METAL_PWM_MAXPRESCAL)); + + pwm->freq = (clock_rate / (1UL << prescale)) / count; + pwm->count_val = --count; + + /* Update values into registers */ + METAL_PWM_REGW(METAL_SIFIVE_PWM0_PWMCMP0) = count; + METAL_PWM_REGW(METAL_SIFIVE_PWM0_PWMCFG) |= (prescale & 0x0FUL); + ret = METAL_PWM_RET_OK; + +#if defined(METAL_PWM_DEBUG) + printf("PWM requested freq:%u set freq:%u \n", freq, pwm->freq); + printf("CPU Clk:%u Prescale:%u Count:%u \n", clock_rate, prescale, + count); +#endif + } + return ret; +} + +static int +__metal_driver_sifive_pwm0_set_duty(struct metal_pwm *gpwm, unsigned int idx, + unsigned int duty, + metal_pwm_phase_correct_t phase_corr) { + struct __metal_driver_sifive_pwm0 *pwm = (void *)gpwm; + unsigned long base = __metal_driver_sifive_pwm0_control_base(gpwm); + unsigned int cmp_count = __metal_driver_sifive_pwm0_comparator_count(gpwm); + int ret = METAL_PWM_RET_ERR; + + /* Check if duty value is within limits, duty cycle cannot be set for + * PWMCMP0 */ + if ((idx > 0) && (idx < cmp_count) && (duty <= METAL_PWM_MAXDUTY)) { + /* Calculate PWM compare count value for given duty cycle */ + METAL_PWM_REGW(METAL_SIFIVE_PWM0_PWMCMP(idx)) = + METAL_PWM_GETCMPVAL(duty); + pwm->duty[idx] = duty; + + /* Enable / Disable phase correct PWM mode */ + if (phase_corr == METAL_PWM_PHASE_CORRECT_ENABLE) { + METAL_PWM_REGW(METAL_SIFIVE_PWM0_PWMCFG) |= + METAL_PWMCFG_CMPCENTER(idx); + } else { + METAL_PWM_REGW(METAL_SIFIVE_PWM0_PWMCFG) &= + ~METAL_PWMCFG_CMPCENTER(idx); + } + ret = METAL_PWM_RET_OK; + } + return ret; +} + +static unsigned int __metal_driver_sifive_pwm0_get_duty(struct metal_pwm *gpwm, + unsigned int idx) { + struct __metal_driver_sifive_pwm0 *pwm = (void *)gpwm; + unsigned int cmp_count = __metal_driver_sifive_pwm0_comparator_count(gpwm); + unsigned int duty = 0; + + /* Check for valid parameters and get configured duty cycle value */ + if ((pwm != NULL) && (idx > 0) && (idx < cmp_count)) { + duty = pwm->duty[idx]; + } + return duty; +} + +static unsigned int __metal_driver_sifive_pwm0_get_freq(struct metal_pwm *gpwm, + unsigned int idx) { + struct __metal_driver_sifive_pwm0 *pwm = (void *)gpwm; + unsigned int freq = 0; + + (void)idx; /* Unused parameter, no support for per channel frequency */ + + /* Check for valid parameters and get configured PWM frequency value */ + if (pwm != NULL) { + freq = pwm->freq; + } + return freq; +} + +static int __metal_driver_sifive_pwm0_trigger(struct metal_pwm *gpwm, + unsigned int idx, + metal_pwm_run_mode_t mode) { + unsigned long base = __metal_driver_sifive_pwm0_control_base(gpwm); + int ret = METAL_PWM_RET_ERR; + + (void)idx; /* Unused parameter,for later use */ + + if (base != 0) { + /* Configure for requested PWM run mode */ + if (mode == METAL_PWM_CONTINUOUS) { + METAL_PWM_REGW(METAL_SIFIVE_PWM0_PWMCFG) |= METAL_PWMCFG_DEGLITCH | + METAL_PWMCFG_ZEROCMP | + METAL_PWMCFG_ENALWAYS; + } else { + METAL_PWM_REGW(METAL_SIFIVE_PWM0_PWMCFG) |= METAL_PWMCFG_DEGLITCH | + METAL_PWMCFG_ZEROCMP | + METAL_PWMCFG_ENONESHOT; + } + ret = METAL_PWM_RET_OK; + } + return ret; +} + +static int __metal_driver_sifive_pwm0_stop(struct metal_pwm *gpwm, + unsigned int idx) { + unsigned long base = __metal_driver_sifive_pwm0_control_base(gpwm); + int ret = METAL_PWM_RET_ERR; + + (void)idx; /* Unused parameter,for later use */ + + if (base != 0) { + /* Disable always running mode */ + METAL_PWM_REGW(METAL_SIFIVE_PWM0_PWMCFG) &= ~METAL_PWMCFG_ENALWAYS; + ret = METAL_PWM_RET_OK; + } + return ret; +} + +static int +__metal_driver_sifive_pwm0_cfg_interrupt(struct metal_pwm *gpwm, + metal_pwm_interrupt_t flag) { + unsigned long base = __metal_driver_sifive_pwm0_control_base(gpwm); + int ret = METAL_PWM_RET_ERR; + + if (base != 0) { + if (flag == METAL_PWM_INTERRUPT_ENABLE) { + /* Enable sticky bit, to make sure interrupts are not forgotten */ + METAL_PWM_REGW(METAL_SIFIVE_PWM0_PWMCFG) |= METAL_PWMCFG_STICKY; + } else { + METAL_PWM_REGW(METAL_SIFIVE_PWM0_PWMCFG) &= ~METAL_PWMCFG_STICKY; + } + ret = METAL_PWM_RET_OK; + } + return ret; +} + +static int __metal_driver_sifive_pwm0_clr_interrupt(struct metal_pwm *gpwm, + unsigned int idx) { + unsigned long base = __metal_driver_sifive_pwm0_control_base(gpwm); + unsigned int cmp_count = __metal_driver_sifive_pwm0_comparator_count(gpwm); + int ret = METAL_PWM_RET_ERR; + + if ((base != 0) && (idx < cmp_count)) { + /* Clear interrupt pending bit for given PWM comparator */ + METAL_PWM_REGW(METAL_SIFIVE_PWM0_PWMCFG) &= ~METAL_PWMCFG_CMPIP(idx); + ret = METAL_PWM_RET_OK; + } + return ret; +} + +static struct metal_interrupt * +__metal_driver_sifive_pwm0_interrupt_controller(struct metal_pwm *gpwm) { + return __metal_driver_sifive_pwm0_interrupt_parent(gpwm); +} + +static int __metal_driver_sifive_pwm0_interrupt_id(struct metal_pwm *gpwm, + unsigned int idx) { + return __metal_driver_sifive_pwm0_interrupt_lines(gpwm, idx); +} + +__METAL_DEFINE_VTABLE(__metal_driver_vtable_sifive_pwm0) = { + .pwm.enable = __metal_driver_sifive_pwm0_enable, + .pwm.disable = __metal_driver_sifive_pwm0_disable, + .pwm.set_duty = __metal_driver_sifive_pwm0_set_duty, + .pwm.set_freq = __metal_driver_sifive_pwm0_set_freq, + .pwm.get_duty = __metal_driver_sifive_pwm0_get_duty, + .pwm.get_freq = __metal_driver_sifive_pwm0_get_freq, + .pwm.trigger = __metal_driver_sifive_pwm0_trigger, + .pwm.stop = __metal_driver_sifive_pwm0_stop, + .pwm.cfg_interrupt = __metal_driver_sifive_pwm0_cfg_interrupt, + .pwm.clr_interrupt = __metal_driver_sifive_pwm0_clr_interrupt, + .pwm.get_interrupt_controller = + __metal_driver_sifive_pwm0_interrupt_controller, + .pwm.get_interrupt_id = __metal_driver_sifive_pwm0_interrupt_id, +}; + +#endif /* METAL_SIFIVE_PWM0 */ + +typedef int no_empty_translation_units; diff --git a/arch/riscv32/fe310/src/freedom-metal/src/drivers/sifive_remapper2.c b/arch/riscv32/fe310/src/freedom-metal/src/drivers/sifive_remapper2.c new file mode 100644 index 00000000..32c440b6 --- /dev/null +++ b/arch/riscv32/fe310/src/freedom-metal/src/drivers/sifive_remapper2.c @@ -0,0 +1,243 @@ +/* Copyright 2021 SiFive, Inc */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include + +#ifdef METAL_SIFIVE_REMAPPER2 + +#include +#include +#include +#include +#include + +#define METAL_SIFIVE_REMAPPER2_VALID_REGS_NUM 7 + +#define METAL_SIFIVE_REMAPPER_ENABLE_KEY 0x51f15e + +#define METAL_SIFIVE_REMAPPER2_FROM_REG_OFFSET(idx) \ + (METAL_SIFIVE_REMAPPER2_FROM_BASE + idx * 16) +#define METAL_SIFIVE_REMAPPER2_TO_REG_OFFSET(idx) \ + (METAL_SIFIVE_REMAPPER2_FROM_BASE + idx * 16 + 8) + +/* Macros to access registers */ +#define REMAPPER_REG(offset) (((unsigned long)base + offset)) +#define REMAPPER_REGW(offset) \ + (__METAL_ACCESS_ONCE((__metal_io_u32 *)REMAPPER_REG(offset))) + +static void +__metal_driver_sifive_remapper_set_key_state(struct metal_remapper *remapper) { + unsigned long base = __metal_driver_sifive_remapper2_base(remapper); + REMAPPER_REGW(METAL_SIFIVE_REMAPPER2_KEY) = + METAL_SIFIVE_REMAPPER_ENABLE_KEY; +} + +int __metal_driver_sifive_remapper_enable_remap(struct metal_remapper *remapper, + int idx) { + unsigned long base = __metal_driver_sifive_remapper2_base(remapper); + int reg_idx = idx / 32; + int bit_offset = idx % 32; + + __metal_driver_sifive_remapper_set_key_state(remapper); + REMAPPER_REGW(METAL_SIFIVE_REMAPPER2_VALID_BASE + reg_idx * 4) |= + (1 << bit_offset); + __METAL_IO_FENCE(o, rw); + + return 0; +} + +int __metal_driver_sifive_remapper_disable_remap( + struct metal_remapper *remapper, int idx) { + unsigned long base = __metal_driver_sifive_remapper2_base(remapper); + int reg_idx = idx / 32; + int bit_offset = idx % 32; + + __metal_driver_sifive_remapper_set_key_state(remapper); + REMAPPER_REGW(METAL_SIFIVE_REMAPPER2_VALID_BASE + reg_idx * 4) &= + ~(1 << bit_offset); + __METAL_IO_FENCE(o, rw); + + return 0; +} + +int __metal_driver_sifive_remapper_enable_remaps( + struct metal_remapper *remapper, int idxs[], int num_idxs) { + unsigned long base = __metal_driver_sifive_remapper2_base(remapper); + uint32_t enables[METAL_SIFIVE_REMAPPER2_VALID_REGS_NUM] = {0}; + + for (int i = 0; i < num_idxs; i++) { + int reg_idx = idxs[i] / 32; + int bit_offset = idxs[i] % 32; + enables[reg_idx] |= (1 << bit_offset); + } + + for (int i = 0; i < METAL_SIFIVE_REMAPPER2_VALID_REGS_NUM; i++) { + uint32_t remappervalid = + REMAPPER_REGW(METAL_SIFIVE_REMAPPER2_VALID_BASE + i * 4); + __metal_driver_sifive_remapper_set_key_state(remapper); + REMAPPER_REGW(METAL_SIFIVE_REMAPPER2_VALID_BASE + i * 4) = + remappervalid | enables[i]; + } + + return 0; +} + +int __metal_driver_sifive_remapper_disable_remaps( + struct metal_remapper *remapper, int idxs[], int num_idxs) { + unsigned long base = __metal_driver_sifive_remapper2_base(remapper); + uint32_t disables[METAL_SIFIVE_REMAPPER2_VALID_REGS_NUM] = {0}; + + for (int i = 0; i < num_idxs; i++) { + int reg_idx = idxs[i] / 32; + int bit_offset = idxs[i] % 32; + disables[reg_idx] |= (1 << bit_offset); + } + + for (int i = 0; i < METAL_SIFIVE_REMAPPER2_VALID_REGS_NUM; i++) { + uint32_t remappervalid = + REMAPPER_REGW(METAL_SIFIVE_REMAPPER2_VALID_BASE + i * 4); + __metal_driver_sifive_remapper_set_key_state(remapper); + REMAPPER_REGW(METAL_SIFIVE_REMAPPER2_VALID_BASE + i * 4) = + remappervalid & ~disables[i]; + } + + return 0; +} + +uint32_t +__metal_driver_sifive_remapper_get_valid(struct metal_remapper *remapper, + int idx) { + if (idx < 0 || idx >= METAL_SIFIVE_REMAPPER2_VALID_REGS_NUM) { + return 0; + } + + unsigned long base = __metal_driver_sifive_remapper2_base(remapper); + return REMAPPER_REGW(METAL_SIFIVE_REMAPPER2_VALID_BASE + idx * 4); +} + +int __metal_driver_sifive_remapper_set_valid(struct metal_remapper *remapper, + int idx, uint32_t val) { + if (idx < 0 || idx >= METAL_SIFIVE_REMAPPER2_VALID_REGS_NUM) { + return 1; + } + + unsigned long base = __metal_driver_sifive_remapper2_base(remapper); + __metal_driver_sifive_remapper_set_key_state(remapper); + REMAPPER_REGW(METAL_SIFIVE_REMAPPER2_VALID_BASE + idx * 4) = val; + + return 0; +} + +int __metal_driver_sifive_remapper_flush(struct metal_remapper *remapper) { + unsigned long base = __metal_driver_sifive_remapper2_base(remapper); + __metal_driver_sifive_remapper_set_key_state(remapper); + REMAPPER_REGW(METAL_SIFIVE_REMAPPER2_FLUSH) = 1; + __METAL_IO_FENCE(o, rw); + + return 0; +} + +uint64_t __metal_driver_sifive_remapper_get_from_region_base( + struct metal_remapper *remapper) { + return __metal_driver_sifive_remapper2_from_region_base(remapper); +} + +uint64_t __metal_driver_sifive_remapper_get_from_region_size( + struct metal_remapper *remapper) { + return __metal_driver_sifive_remapper2_from_region_size(remapper); +} + +uint64_t __metal_driver_sifive_remapper_get_to_region_base( + struct metal_remapper *remapper) { + return __metal_driver_sifive_remapper2_to_region_base(remapper); +} + +uint64_t __metal_driver_sifive_remapper_get_to_region_size( + struct metal_remapper *remapper) { + return __metal_driver_sifive_remapper2_to_region_size(remapper); +} + +uint64_t __metal_driver_sifive_remapper_get_max_from_entry_region_size( + struct metal_remapper *remapper) { + return (1ULL << __metal_driver_sifive_remapper2_max_from_entry_addr_width( + remapper)); +} + +uint32_t +__metal_driver_sifive_remapper_get_version(struct metal_remapper *remapper) { + unsigned long base = __metal_driver_sifive_remapper2_base(remapper); + return REMAPPER_REGW(METAL_SIFIVE_REMAPPER2_VERSION); +} + +uint32_t +__metal_driver_sifive_remapper_get_entries(struct metal_remapper *remapper) { + unsigned long base = __metal_driver_sifive_remapper2_base(remapper); + return REMAPPER_REGW(METAL_SIFIVE_REMAPPER2_ENTRIES); +} + +int __metal_driver_sifive_remapper_set_remap( + struct metal_remapper *remapper, struct metal_remapper_entry *entry) { + unsigned long base = __metal_driver_sifive_remapper2_base(remapper); + __metal_driver_sifive_remapper_set_key_state(remapper); + REMAPPER_REGW(METAL_SIFIVE_REMAPPER2_FROM_REG_OFFSET(entry->idx)) = + entry->from_addr; + __metal_driver_sifive_remapper_set_key_state(remapper); + REMAPPER_REGW(METAL_SIFIVE_REMAPPER2_TO_REG_OFFSET(entry->idx)) = + entry->to_addr; + __METAL_IO_FENCE(o, rw); + + return 0; +} + +int __metal_driver_sifive_remapper_set_remaps( + struct metal_remapper *remapper, struct metal_remapper_entry *entries[], + int num_entries) { + for (int i = 0; i < num_entries; i++) { + __metal_driver_sifive_remapper_set_remap(remapper, entries[i]); + } + + return 0; +} + +uint64_t +__metal_driver_sifive_remapper_get_from(struct metal_remapper *remapper, + int idx) { + unsigned long base = __metal_driver_sifive_remapper2_base(remapper); + return REMAPPER_REGW(METAL_SIFIVE_REMAPPER2_FROM_REG_OFFSET(idx)); +} + +uint64_t __metal_driver_sifive_remapper_get_to(struct metal_remapper *remapper, + int idx) { + unsigned long base = __metal_driver_sifive_remapper2_base(remapper); + return REMAPPER_REGW(METAL_SIFIVE_REMAPPER2_TO_REG_OFFSET(idx)); +} + +__METAL_DEFINE_VTABLE(__metal_driver_vtable_sifive_remapper2) = { + .remapper.enable_remap = __metal_driver_sifive_remapper_enable_remap, + .remapper.disable_remap = __metal_driver_sifive_remapper_disable_remap, + .remapper.enable_remaps = __metal_driver_sifive_remapper_enable_remaps, + .remapper.disable_remaps = __metal_driver_sifive_remapper_disable_remaps, + .remapper.get_valid = __metal_driver_sifive_remapper_get_valid, + .remapper.set_valid = __metal_driver_sifive_remapper_set_valid, + .remapper.flush = __metal_driver_sifive_remapper_flush, + .remapper.get_from_region_base = + __metal_driver_sifive_remapper_get_from_region_base, + .remapper.get_from_region_size = + __metal_driver_sifive_remapper_get_from_region_size, + .remapper.get_to_region_base = + __metal_driver_sifive_remapper_get_to_region_base, + .remapper.get_to_region_size = + __metal_driver_sifive_remapper_get_to_region_size, + .remapper.get_max_from_entry_region_size = + __metal_driver_sifive_remapper_get_max_from_entry_region_size, + .remapper.get_version = __metal_driver_sifive_remapper_get_version, + .remapper.get_entries = __metal_driver_sifive_remapper_get_entries, + .remapper.set_remap = __metal_driver_sifive_remapper_set_remap, + .remapper.set_remaps = __metal_driver_sifive_remapper_set_remaps, + .remapper.get_from = __metal_driver_sifive_remapper_get_from, + .remapper.get_to = __metal_driver_sifive_remapper_get_to, +}; + +#endif /* METAL_SIFIVE_REMAPPER2 */ + +typedef int no_empty_translation_units; diff --git a/arch/riscv32/fe310/src/freedom-metal/src/drivers/sifive_rtc0.c b/arch/riscv32/fe310/src/freedom-metal/src/drivers/sifive_rtc0.c new file mode 100644 index 00000000..d9c06d34 --- /dev/null +++ b/arch/riscv32/fe310/src/freedom-metal/src/drivers/sifive_rtc0.c @@ -0,0 +1,137 @@ +/* Copyright 2019 SiFive, Inc */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include + +#ifdef METAL_SIFIVE_RTC0 + +#include +#include + +#include + +/* RTCCFG */ +#define METAL_RTCCFG_RTCSCALE_MASK 0xF +#define METAL_RTCCFG_ENALWAYS (1 << 12) +#define METAL_RTCCFG_IP0 (1 << 28) + +/* RTCCMP0 */ +#define METAL_RTCCMP0_MAX UINT32_MAX + +#define RTC_REG(base, offset) (((unsigned long)base + offset)) +#define RTC_REGW(base, offset) \ + (__METAL_ACCESS_ONCE((__metal_io_u32 *)RTC_REG(base, offset))) + +uint64_t +__metal_driver_sifive_rtc0_get_rate(const struct metal_rtc *const rtc) { + const struct metal_clock *const clock = + __metal_driver_sifive_rtc0_clock(rtc); + return metal_clock_get_rate_hz(clock); +} + +uint64_t __metal_driver_sifive_rtc0_set_rate(const struct metal_rtc *const rtc, + const uint64_t rate) { + const struct metal_clock *const clock = + __metal_driver_sifive_rtc0_clock(rtc); + return metal_clock_get_rate_hz(clock); +} + +uint64_t +__metal_driver_sifive_rtc0_get_compare(const struct metal_rtc *const rtc) { + const uint64_t base = __metal_driver_sifive_rtc0_control_base(rtc); + + const uint32_t shift = + RTC_REGW(base, METAL_SIFIVE_RTC0_RTCCFG) & METAL_RTCCFG_RTCSCALE_MASK; + + return ((uint64_t)RTC_REGW(base, METAL_SIFIVE_RTC0_RTCCMP0) << shift); +} + +uint64_t +__metal_driver_sifive_rtc0_set_compare(const struct metal_rtc *const rtc, + const uint64_t compare) { + const uint64_t base = __metal_driver_sifive_rtc0_control_base(rtc); + + /* Determine the bit shift and shifted value to store in + * rtccmp0/rtccfg.scale */ + uint32_t shift = 0; + uint64_t comp_shifted = compare; + while (comp_shifted > METAL_RTCCMP0_MAX) { + shift += 1; + comp_shifted = comp_shifted >> shift; + } + + /* Set the value of rtccfg.scale */ + uint32_t cfg = RTC_REGW(base, METAL_SIFIVE_RTC0_RTCCFG); + cfg &= ~(METAL_RTCCFG_RTCSCALE_MASK); + cfg |= (METAL_RTCCFG_RTCSCALE_MASK & shift); + RTC_REGW(base, METAL_SIFIVE_RTC0_RTCCFG) = cfg; + + /* Set the value of rtccmp0 */ + RTC_REGW(base, METAL_SIFIVE_RTC0_RTCCMP0) = (uint32_t)comp_shifted; + + return __metal_driver_sifive_rtc0_get_compare(rtc); +} + +uint64_t +__metal_driver_sifive_rtc0_get_count(const struct metal_rtc *const rtc) { + const uint64_t base = __metal_driver_sifive_rtc0_control_base(rtc); + + uint64_t count = RTC_REGW(base, METAL_SIFIVE_RTC0_RTCCOUNTHI); + count <<= 32; + count |= RTC_REGW(base, METAL_SIFIVE_RTC0_RTCCOUNTLO); + + return count; +} + +uint64_t __metal_driver_sifive_rtc0_set_count(const struct metal_rtc *const rtc, + const uint64_t count) { + const uint64_t base = __metal_driver_sifive_rtc0_control_base(rtc); + + RTC_REGW(base, METAL_SIFIVE_RTC0_RTCCOUNTHI) = (UINT_MAX & (count >> 32)); + RTC_REGW(base, METAL_SIFIVE_RTC0_RTCCOUNTLO) = (UINT_MAX & count); + + return __metal_driver_sifive_rtc0_get_count(rtc); +} + +int __metal_driver_sifive_rtc0_run(const struct metal_rtc *const rtc, + const enum metal_rtc_run_option option) { + const uint64_t base = __metal_driver_sifive_rtc0_control_base(rtc); + + switch (option) { + default: + case METAL_RTC_STOP: + RTC_REGW(base, METAL_SIFIVE_RTC0_RTCCFG) &= ~(METAL_RTCCFG_ENALWAYS); + break; + case METAL_RTC_RUN: + RTC_REGW(base, METAL_SIFIVE_RTC0_RTCCFG) |= METAL_RTCCFG_ENALWAYS; + break; + } + + return 0; +} + +struct metal_interrupt * +__metal_driver_sifive_rtc0_get_interrupt(const struct metal_rtc *const rtc) { + return __metal_driver_sifive_rtc0_interrupt_parent(rtc); +} + +int __metal_driver_sifive_rtc0_get_interrupt_id( + const struct metal_rtc *const rtc) { + return __metal_driver_sifive_rtc0_interrupt_line(rtc); +} + +__METAL_DEFINE_VTABLE(__metal_driver_vtable_sifive_rtc0) = { + .rtc.get_rate = __metal_driver_sifive_rtc0_get_rate, + .rtc.set_rate = __metal_driver_sifive_rtc0_set_rate, + .rtc.get_compare = __metal_driver_sifive_rtc0_get_compare, + .rtc.set_compare = __metal_driver_sifive_rtc0_set_compare, + .rtc.get_count = __metal_driver_sifive_rtc0_get_count, + .rtc.set_count = __metal_driver_sifive_rtc0_set_count, + .rtc.run = __metal_driver_sifive_rtc0_run, + .rtc.get_interrupt = __metal_driver_sifive_rtc0_get_interrupt, + .rtc.get_interrupt_id = __metal_driver_sifive_rtc0_get_interrupt_id, +}; + +#endif + +typedef int no_empty_translation_units; diff --git a/arch/riscv32/fe310/src/freedom-metal/src/drivers/sifive_simuart0.c b/arch/riscv32/fe310/src/freedom-metal/src/drivers/sifive_simuart0.c new file mode 100644 index 00000000..7a3e1b65 --- /dev/null +++ b/arch/riscv32/fe310/src/freedom-metal/src/drivers/sifive_simuart0.c @@ -0,0 +1,79 @@ +/* Copyright 2020 SiFive, Inc */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include + +#ifdef METAL_SIFIVE_SIMUART0 + +#include +#include +#include + +/* TXDATA Fields */ +#define SIMUART_TXEN (1 << 0) + +#define SIMUART_REG(offset) (((unsigned long)control_base + offset)) +#define SIMUART_REGB(offset) \ + (__METAL_ACCESS_ONCE((__metal_io_u8 *)SIMUART_REG(offset))) +#define SIMUART_REGW(offset) \ + (__METAL_ACCESS_ONCE((__metal_io_u32 *)SIMUART_REG(offset))) + +struct metal_interrupt * +__metal_driver_sifive_simuart0_interrupt_controller(struct metal_uart *uart) { + return __metal_driver_sifive_simuart0_interrupt_parent(uart); +} + +int __metal_driver_sifive_simuart0_get_interrupt_id(struct metal_uart *uart) { + return (__metal_driver_sifive_simuart0_interrupt_line(uart) + + METAL_INTERRUPT_ID_GL0); +} + +int __metal_driver_sifive_simuart0_putc(struct metal_uart *uart, int c) { + long control_base = __metal_driver_sifive_simuart0_control_base(uart); + + SIMUART_REGW(METAL_SIFIVE_SIMUART0_TXDATA) = c; + return 0; +} + +int __metal_driver_sifive_simuart0_getc(struct metal_uart *uart, int *c) { + return 0; +} + +int __metal_driver_sifive_simuart0_get_baud_rate(struct metal_uart *guart) { + struct __metal_driver_sifive_simuart0 *uart = (void *)guart; + return uart->baud_rate; +} + +int __metal_driver_sifive_simuart0_set_baud_rate(struct metal_uart *guart, + int baud_rate) { + struct __metal_driver_sifive_simuart0 *uart = (void *)guart; + long control_base = __metal_driver_sifive_simuart0_control_base(guart); + struct metal_clock *clock = __metal_driver_sifive_simuart0_clock(guart); + + uart->baud_rate = baud_rate; + + if (clock != NULL) { + long clock_rate = clock->vtable->get_rate_hz(clock); + SIMUART_REGW(METAL_SIFIVE_SIMUART0_DIV) = clock_rate / baud_rate - 1; + SIMUART_REGW(METAL_SIFIVE_SIMUART0_TXCTRL) |= SIMUART_TXEN; + } + return 0; +} + +void __metal_driver_sifive_simuart0_init(struct metal_uart *guart, + int baud_rate) {} + +__METAL_DEFINE_VTABLE(__metal_driver_vtable_sifive_simuart0) = { + .uart.init = __metal_driver_sifive_simuart0_init, + .uart.putc = __metal_driver_sifive_simuart0_putc, + .uart.getc = __metal_driver_sifive_simuart0_getc, + .uart.get_baud_rate = __metal_driver_sifive_simuart0_get_baud_rate, + .uart.set_baud_rate = __metal_driver_sifive_simuart0_set_baud_rate, + .uart.controller_interrupt = + __metal_driver_sifive_simuart0_interrupt_controller, + .uart.get_interrupt_id = __metal_driver_sifive_simuart0_get_interrupt_id, +}; + +#endif /* METAL_SIFIVE_SIMUART0 */ + +typedef int no_empty_translation_units; diff --git a/arch/riscv32/fe310/src/freedom-metal/src/drivers/sifive_spi0.c b/arch/riscv32/fe310/src/freedom-metal/src/drivers/sifive_spi0.c new file mode 100644 index 00000000..34f8e0a5 --- /dev/null +++ b/arch/riscv32/fe310/src/freedom-metal/src/drivers/sifive_spi0.c @@ -0,0 +1,422 @@ +/* Copyright 2018 SiFive, Inc */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include + +#ifdef METAL_SIFIVE_SPI0 +#include +#include +#include +#include +#include + +/* Register fields */ +#define METAL_SPI_SCKDIV_MASK 0xFFF + +#define METAL_SPI_SCKMODE_PHA_SHIFT 0 +#define METAL_SPI_SCKMODE_POL_SHIFT 1 + +#define METAL_SPI_CSMODE_MASK 3 +#define METAL_SPI_CSMODE_AUTO 0 +#define METAL_SPI_CSMODE_HOLD 2 +#define METAL_SPI_CSMODE_OFF 3 + +#define METAL_SPI_PROTO_MASK 3 +#define METAL_SPI_PROTO_SINGLE 0 +#define METAL_SPI_PROTO_DUAL 1 +#define METAL_SPI_PROTO_QUAD 2 + +#define METAL_SPI_ENDIAN_LSB 4 + +#define METAL_SPI_DISABLE_RX 8 + +#define METAL_SPI_FRAME_LEN_SHIFT 16 +#define METAL_SPI_FRAME_LEN_MASK (0xF << METAL_SPI_FRAME_LEN_SHIFT) + +#define METAL_SPI_TXDATA_FULL (1 << 31) +#define METAL_SPI_RXDATA_EMPTY (1 << 31) +#define METAL_SPI_TXMARK_MASK 7 +#define METAL_SPI_TXWM 1 +#define METAL_SPI_TXRXDATA_MASK (0xFF) + +#define METAL_SPI_INTERVAL_SHIFT 16 + +#define METAL_SPI_CONTROL_IO 0 +#define METAL_SPI_CONTROL_MAPPED 1 + +#define METAL_SPI_REG(offset) (((unsigned long)control_base + offset)) +#define METAL_SPI_REGB(offset) \ + (__METAL_ACCESS_ONCE((__metal_io_u8 *)METAL_SPI_REG(offset))) +#define METAL_SPI_REGW(offset) \ + (__METAL_ACCESS_ONCE((__metal_io_u32 *)METAL_SPI_REG(offset))) + +#define METAL_SPI_RXDATA_TIMEOUT 1 + +static int configure_spi(struct __metal_driver_sifive_spi0 *spi, + struct metal_spi_config *config) { + long control_base = + __metal_driver_sifive_spi0_control_base((struct metal_spi *)spi); + /* Set protocol */ + METAL_SPI_REGW(METAL_SIFIVE_SPI0_FMT) &= ~(METAL_SPI_PROTO_MASK); + switch (config->protocol) { + case METAL_SPI_SINGLE: + METAL_SPI_REGW(METAL_SIFIVE_SPI0_FMT) |= METAL_SPI_PROTO_SINGLE; + break; + case METAL_SPI_DUAL: + if (config->multi_wire == MULTI_WIRE_ALL) + METAL_SPI_REGW(METAL_SIFIVE_SPI0_FMT) |= METAL_SPI_PROTO_DUAL; + else + METAL_SPI_REGW(METAL_SIFIVE_SPI0_FMT) |= METAL_SPI_PROTO_SINGLE; + break; + case METAL_SPI_QUAD: + if (config->multi_wire == MULTI_WIRE_ALL) + METAL_SPI_REGW(METAL_SIFIVE_SPI0_FMT) |= METAL_SPI_PROTO_QUAD; + else + METAL_SPI_REGW(METAL_SIFIVE_SPI0_FMT) |= METAL_SPI_PROTO_SINGLE; + break; + default: + /* Unsupported value */ + return -1; + } + + /* Set Polarity */ + if (config->polarity) { + METAL_SPI_REGW(METAL_SIFIVE_SPI0_SCKMODE) |= + (1 << METAL_SPI_SCKMODE_POL_SHIFT); + } else { + METAL_SPI_REGW(METAL_SIFIVE_SPI0_SCKMODE) &= + ~(1 << METAL_SPI_SCKMODE_POL_SHIFT); + } + + /* Set Phase */ + if (config->phase) { + METAL_SPI_REGW(METAL_SIFIVE_SPI0_SCKMODE) |= + (1 << METAL_SPI_SCKMODE_PHA_SHIFT); + } else { + METAL_SPI_REGW(METAL_SIFIVE_SPI0_SCKMODE) &= + ~(1 << METAL_SPI_SCKMODE_PHA_SHIFT); + } + + /* Set Endianness */ + if (config->little_endian) { + METAL_SPI_REGW(METAL_SIFIVE_SPI0_FMT) |= METAL_SPI_ENDIAN_LSB; + } else { + METAL_SPI_REGW(METAL_SIFIVE_SPI0_FMT) &= ~(METAL_SPI_ENDIAN_LSB); + } + + /* Always populate receive FIFO */ + METAL_SPI_REGW(METAL_SIFIVE_SPI0_FMT) &= ~(METAL_SPI_DISABLE_RX); + + /* Set CS Active */ + if (config->cs_active_high) { + METAL_SPI_REGW(METAL_SIFIVE_SPI0_CSDEF) = 0; + } else { + METAL_SPI_REGW(METAL_SIFIVE_SPI0_CSDEF) = 1; + } + + /* Set frame length */ + if ((METAL_SPI_REGW(METAL_SIFIVE_SPI0_FMT) & METAL_SPI_FRAME_LEN_MASK) != + (8 << METAL_SPI_FRAME_LEN_SHIFT)) { + METAL_SPI_REGW(METAL_SIFIVE_SPI0_FMT) &= ~(METAL_SPI_FRAME_LEN_MASK); + METAL_SPI_REGW(METAL_SIFIVE_SPI0_FMT) |= + (8 << METAL_SPI_FRAME_LEN_SHIFT); + } + + /* Set CS line */ + METAL_SPI_REGW(METAL_SIFIVE_SPI0_CSID) = config->csid; + + /* Toggle off memory-mapped SPI flash mode, toggle on programmable IO mode + * It seems that with this line uncommented, the debugger cannot have access + * to the chip at all because it assumes the chip is in memory-mapped mode. + * I have to compile the code with this line commented and launch gdb, + * reset cores, reset $pc, set *((int *) 0x20004060) = 0, (set the flash + * interface control register to programmable I/O mode) and then continue + * Alternative, comment out the "flash" line in openocd.cfg */ + METAL_SPI_REGW(METAL_SIFIVE_SPI0_FCTRL) = METAL_SPI_CONTROL_IO; + + return 0; +} + +static void spi_mode_switch(struct __metal_driver_sifive_spi0 *spi, + struct metal_spi_config *config, + unsigned int trans_stage) { + long control_base = + __metal_driver_sifive_spi0_control_base((struct metal_spi *)spi); + + if (config->multi_wire == trans_stage) { + METAL_SPI_REGW(METAL_SIFIVE_SPI0_FMT) &= ~(METAL_SPI_PROTO_MASK); + switch (config->protocol) { + case METAL_SPI_DUAL: + METAL_SPI_REGW(METAL_SIFIVE_SPI0_FMT) |= METAL_SPI_PROTO_DUAL; + break; + case METAL_SPI_QUAD: + METAL_SPI_REGW(METAL_SIFIVE_SPI0_FMT) |= METAL_SPI_PROTO_QUAD; + break; + default: + /* Unsupported value */ + return; + } + } +} + +int __metal_driver_sifive_spi0_transfer(struct metal_spi *gspi, + struct metal_spi_config *config, + size_t len, char *tx_buf, + char *rx_buf) { + struct __metal_driver_sifive_spi0 *spi = (void *)gspi; + long control_base = __metal_driver_sifive_spi0_control_base(gspi); + int rc = 0; + size_t i = 0; + + rc = configure_spi(spi, config); + if (rc != 0) { + return rc; + } + + /* Hold the chip select line for all len transferred */ + METAL_SPI_REGW(METAL_SIFIVE_SPI0_CSMODE) &= ~(METAL_SPI_CSMODE_MASK); + METAL_SPI_REGW(METAL_SIFIVE_SPI0_CSMODE) |= METAL_SPI_CSMODE_HOLD; + + unsigned long rxdata; + + /* Declare time_t variables to break out of infinite while loop */ + time_t endwait; + + for (i = 0; i < config->cmd_num; i++) { + + while (METAL_SPI_REGW(METAL_SIFIVE_SPI0_TXDATA) & METAL_SPI_TXDATA_FULL) + ; + + if (tx_buf) { + METAL_SPI_REGB(METAL_SIFIVE_SPI0_TXDATA) = tx_buf[i]; + } else { + METAL_SPI_REGB(METAL_SIFIVE_SPI0_TXDATA) = 0; + } + + endwait = metal_time() + METAL_SPI_RXDATA_TIMEOUT; + + while ((rxdata = METAL_SPI_REGW(METAL_SIFIVE_SPI0_RXDATA)) & + METAL_SPI_RXDATA_EMPTY) { + if (metal_time() > endwait) { + METAL_SPI_REGW(METAL_SIFIVE_SPI0_CSMODE) &= + ~(METAL_SPI_CSMODE_MASK); + + return 1; + } + } + + if (rx_buf) { + rx_buf[i] = (char)(rxdata & METAL_SPI_TXRXDATA_MASK); + } + } + + /* switch to Dual/Quad mode */ + spi_mode_switch(spi, config, MULTI_WIRE_ADDR_DATA); + + /* Send Addr data */ + for (; i < (config->cmd_num + config->addr_num); i++) { + + while (METAL_SPI_REGW(METAL_SIFIVE_SPI0_TXDATA) & METAL_SPI_TXDATA_FULL) + ; + + if (tx_buf) { + METAL_SPI_REGB(METAL_SIFIVE_SPI0_TXDATA) = tx_buf[i]; + } else { + METAL_SPI_REGB(METAL_SIFIVE_SPI0_TXDATA) = 0; + } + + endwait = metal_time() + METAL_SPI_RXDATA_TIMEOUT; + + while ((rxdata = METAL_SPI_REGW(METAL_SIFIVE_SPI0_RXDATA)) & + METAL_SPI_RXDATA_EMPTY) { + if (metal_time() > endwait) { + METAL_SPI_REGW(METAL_SIFIVE_SPI0_CSMODE) &= + ~(METAL_SPI_CSMODE_MASK); + + return 1; + } + } + + if (rx_buf) { + rx_buf[i] = (char)(rxdata & METAL_SPI_TXRXDATA_MASK); + } + } + + /* Send Dummy data */ + for (; i < (config->cmd_num + config->addr_num + config->dummy_num); i++) { + + while (METAL_SPI_REGW(METAL_SIFIVE_SPI0_TXDATA) & METAL_SPI_TXDATA_FULL) + ; + + if (tx_buf) { + METAL_SPI_REGB(METAL_SIFIVE_SPI0_TXDATA) = tx_buf[i]; + } else { + METAL_SPI_REGB(METAL_SIFIVE_SPI0_TXDATA) = 0; + } + + endwait = metal_time() + METAL_SPI_RXDATA_TIMEOUT; + + while ((rxdata = METAL_SPI_REGW(METAL_SIFIVE_SPI0_RXDATA)) & + METAL_SPI_RXDATA_EMPTY) { + if (metal_time() > endwait) { + METAL_SPI_REGW(METAL_SIFIVE_SPI0_CSMODE) &= + ~(METAL_SPI_CSMODE_MASK); + return 1; + } + } + if (rx_buf) { + rx_buf[i] = (char)(rxdata & METAL_SPI_TXRXDATA_MASK); + } + } + + /* switch to Dual/Quad mode */ + spi_mode_switch(spi, config, MULTI_WIRE_DATA_ONLY); + + for (; i < len; i++) { + /* Master send bytes to the slave */ + + /* Wait for TXFIFO to not be full */ + while (METAL_SPI_REGW(METAL_SIFIVE_SPI0_TXDATA) & METAL_SPI_TXDATA_FULL) + ; + + /* Transfer byte by modifying the least significant byte in the TXDATA + * register */ + if (tx_buf) { + METAL_SPI_REGB(METAL_SIFIVE_SPI0_TXDATA) = tx_buf[i]; + } else { + /* Transfer a 0 byte if the sending buffer is NULL */ + METAL_SPI_REGB(METAL_SIFIVE_SPI0_TXDATA) = 0; + } + + /* Master receives bytes from the RX FIFO */ + + /* Wait for RXFIFO to not be empty, but break the nested loops if + * timeout this timeout method needs refining, preferably taking into + * account the device specs */ + endwait = metal_time() + METAL_SPI_RXDATA_TIMEOUT; + + while ((rxdata = METAL_SPI_REGW(METAL_SIFIVE_SPI0_RXDATA)) & + METAL_SPI_RXDATA_EMPTY) { + if (metal_time() > endwait) { + /* If timeout, deassert the CS */ + METAL_SPI_REGW(METAL_SIFIVE_SPI0_CSMODE) &= + ~(METAL_SPI_CSMODE_MASK); + + /* If timeout, return error code 1 immediately */ + return 1; + } + } + + /* Only store the dequeued byte if the receive_buffer is not NULL */ + if (rx_buf) { + rx_buf[i] = (char)(rxdata & METAL_SPI_TXRXDATA_MASK); + } + } + + /* On the last byte, set CSMODE to auto so that the chip select transitions + * back to high The reason that CS pin is not deasserted after transmitting + * out the byte buffer is timing. The code on the host side likely executes + * faster than the ability of FIFO to send out bytes. After the host + * iterates through the array, fifo is likely not cleared yet. If host + * deasserts the CS pin immediately, the following bytes in the output FIFO + * will not be sent consecutively. + * There needs to be a better way to handle this. */ + METAL_SPI_REGW(METAL_SIFIVE_SPI0_CSMODE) &= ~(METAL_SPI_CSMODE_MASK); + + return 0; +} + +int __metal_driver_sifive_spi0_get_baud_rate(struct metal_spi *gspi) { + struct __metal_driver_sifive_spi0 *spi = (void *)gspi; + return spi->baud_rate; +} + +int __metal_driver_sifive_spi0_set_baud_rate(struct metal_spi *gspi, + int baud_rate) { + long control_base = __metal_driver_sifive_spi0_control_base(gspi); + struct metal_clock *clock = __metal_driver_sifive_spi0_clock(gspi); + struct __metal_driver_sifive_spi0 *spi = (void *)gspi; + + spi->baud_rate = baud_rate; + + if (clock != NULL) { + long clock_rate = clock->vtable->get_rate_hz(clock); + + /* Calculate divider */ + long div = (clock_rate / (2 * baud_rate)) - 1; + + if (div > METAL_SPI_SCKDIV_MASK) { + /* The requested baud rate is lower than we can support at + * the current clock rate */ + return -1; + } + + /* Set divider */ + METAL_SPI_REGW(METAL_SIFIVE_SPI0_SCKDIV) &= ~METAL_SPI_SCKDIV_MASK; + METAL_SPI_REGW(METAL_SIFIVE_SPI0_SCKDIV) |= + (div & METAL_SPI_SCKDIV_MASK); + } + + return 0; +} + +static void pre_rate_change_callback_func(void *priv) { + long control_base = + __metal_driver_sifive_spi0_control_base((struct metal_spi *)priv); + + /* Detect when the TXDATA is empty by setting the transmit watermark count + * to one and waiting until an interrupt is pending (indicating an empty + * TXFIFO) */ + METAL_SPI_REGW(METAL_SIFIVE_SPI0_TXMARK) &= ~(METAL_SPI_TXMARK_MASK); + METAL_SPI_REGW(METAL_SIFIVE_SPI0_TXMARK) |= (METAL_SPI_TXMARK_MASK & 1); + + while ((METAL_SPI_REGW(METAL_SIFIVE_SPI0_IP) & METAL_SPI_TXWM) == 0) + ; +} + +static void post_rate_change_callback_func(void *priv) { + struct __metal_driver_sifive_spi0 *spi = priv; + metal_spi_set_baud_rate(&spi->spi, spi->baud_rate); +} + +void __metal_driver_sifive_spi0_init(struct metal_spi *gspi, int baud_rate) { + struct __metal_driver_sifive_spi0 *spi = (void *)(gspi); + struct metal_clock *clock = __metal_driver_sifive_spi0_clock(gspi); + struct __metal_driver_sifive_gpio0 *pinmux = + __metal_driver_sifive_spi0_pinmux(gspi); + + if (clock != NULL) { + spi->pre_rate_change_callback.callback = &pre_rate_change_callback_func; + spi->pre_rate_change_callback.priv = spi; + metal_clock_register_pre_rate_change_callback( + clock, &(spi->pre_rate_change_callback)); + + spi->post_rate_change_callback.callback = + &post_rate_change_callback_func; + spi->post_rate_change_callback.priv = spi; + metal_clock_register_post_rate_change_callback( + clock, &(spi->post_rate_change_callback)); + } + + metal_spi_set_baud_rate(&(spi->spi), baud_rate); + + if (pinmux != NULL) { + long pinmux_output_selector = + __metal_driver_sifive_spi0_pinmux_output_selector(gspi); + long pinmux_source_selector = + __metal_driver_sifive_spi0_pinmux_source_selector(gspi); + pinmux->gpio.vtable->enable_io((struct metal_gpio *)pinmux, + pinmux_output_selector, + pinmux_source_selector); + } +} + +__METAL_DEFINE_VTABLE(__metal_driver_vtable_sifive_spi0) = { + .spi.init = __metal_driver_sifive_spi0_init, + .spi.transfer = __metal_driver_sifive_spi0_transfer, + .spi.get_baud_rate = __metal_driver_sifive_spi0_get_baud_rate, + .spi.set_baud_rate = __metal_driver_sifive_spi0_set_baud_rate, +}; +#endif /* METAL_SIFIVE_SPI0 */ + +typedef int no_empty_translation_units; diff --git a/arch/riscv32/fe310/src/freedom-metal/src/drivers/sifive_test0.c b/arch/riscv32/fe310/src/freedom-metal/src/drivers/sifive_test0.c new file mode 100644 index 00000000..c21d0968 --- /dev/null +++ b/arch/riscv32/fe310/src/freedom-metal/src/drivers/sifive_test0.c @@ -0,0 +1,32 @@ +/* Copyright 2018 SiFive, Inc */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include + +#ifdef METAL_SIFIVE_TEST0 + +#include + +#include + +#include +#include + +void __metal_driver_sifive_test0_exit(const struct __metal_shutdown *sd, + int code) __attribute__((noreturn)); +void __metal_driver_sifive_test0_exit(const struct __metal_shutdown *sd, + int code) { + long base = __metal_driver_sifive_test0_base(sd); + uint32_t out = (code << 16) + (code == 0 ? 0x5555 : 0x3333); + while (1) { + __METAL_ACCESS_ONCE(( + __metal_io_u32 *)(base + METAL_SIFIVE_TEST0_FINISHER_OFFSET)) = out; + } +} + +__METAL_DEFINE_VTABLE(__metal_driver_vtable_sifive_test0) = { + .shutdown.exit = &__metal_driver_sifive_test0_exit, +}; +#endif /* METAL_SIFIVE_TEST0 */ + +typedef int no_empty_translation_units; diff --git a/arch/riscv32/fe310/src/freedom-metal/src/drivers/sifive_trace.c b/arch/riscv32/fe310/src/freedom-metal/src/drivers/sifive_trace.c new file mode 100644 index 00000000..6b82e0f3 --- /dev/null +++ b/arch/riscv32/fe310/src/freedom-metal/src/drivers/sifive_trace.c @@ -0,0 +1,96 @@ +/* Copyright 2019 SiFive, Inc */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include + +#ifdef METAL_SIFIVE_TRACE + +#include +#include + +#define TRACE_REG(offset) (((unsigned long)base + (offset))) +#define TRACE_REG8(offset) \ + (__METAL_ACCESS_ONCE((__metal_io_u8 *)TRACE_REG(offset))) +#define TRACE_REG16(offset) \ + (__METAL_ACCESS_ONCE((__metal_io_u16 *)TRACE_REG(offset))) +#define TRACE_REG32(offset) \ + (__METAL_ACCESS_ONCE((__metal_io_u32 *)TRACE_REG(offset))) + +static void write_itc_uint32(struct metal_uart *trace, uint32_t data) { + long base = __metal_driver_sifive_trace_base(trace); + + TRACE_REG32(METAL_SIFIVE_TRACE_ITCSTIMULUS) = data; +} + +static void write_itc_uint16(struct metal_uart *trace, uint16_t data) { + long base = __metal_driver_sifive_trace_base(trace); + + TRACE_REG16(METAL_SIFIVE_TRACE_ITCSTIMULUS + 2) = data; +} + +static void write_itc_uint8(struct metal_uart *trace, uint8_t data) { + long base = __metal_driver_sifive_trace_base(trace); + + TRACE_REG8(METAL_SIFIVE_TRACE_ITCSTIMULUS + 3) = data; +} + +int __metal_driver_sifive_trace_putc(struct metal_uart *trace, int c) { + static uint32_t buffer = 0; + static int bytes_in_buffer = 0; + + buffer |= (((uint32_t)c) << (bytes_in_buffer * 8)); + + bytes_in_buffer += 1; + + if (bytes_in_buffer >= 4) { + write_itc_uint32(trace, buffer); + + buffer = 0; + bytes_in_buffer = 0; + } else if (((char)c == '\n') || ((char)c == '\r')) { // partial write + switch (bytes_in_buffer) { + case 3: // do a full word write + write_itc_uint16(trace, (uint16_t)(buffer)); + write_itc_uint8(trace, (uint8_t)(buffer >> 16)); + break; + case 2: // do a 16 bit write + write_itc_uint16(trace, (uint16_t)buffer); + break; + case 1: // do a 1 byte write + write_itc_uint8(trace, (uint8_t)buffer); + break; + } + + buffer = 0; + bytes_in_buffer = 0; + } + + return c; +} + +void __metal_driver_sifive_trace_init(struct metal_uart *trace, int baud_rate) { + // The only init we do here is to make sure ITC 0 is enabled. It is up to + // Freedom Studio or other mechanisms to make sure tracing is enabled. If we + // try to enable tracing here, it will likely conflict with Freedom Studio, + // and they will just fight with each other. + + long base = __metal_driver_sifive_trace_base(trace); + + TRACE_REG32(METAL_SIFIVE_TRACE_ITCTRACEENABLE) |= 0x00000001; +} + +__METAL_DEFINE_VTABLE(__metal_driver_vtable_sifive_trace) = { + .uart.init = __metal_driver_sifive_trace_init, + .uart.putc = __metal_driver_sifive_trace_putc, + .uart.getc = NULL, + + .uart.get_baud_rate = NULL, + .uart.set_baud_rate = NULL, + + .uart.controller_interrupt = NULL, + .uart.get_interrupt_id = NULL, +}; + +#endif /* METAL_SIFIVE_TRACE */ + +typedef int no_empty_translation_units; diff --git a/arch/riscv32/fe310/src/freedom-metal/src/drivers/sifive_uart0.c b/arch/riscv32/fe310/src/freedom-metal/src/drivers/sifive_uart0.c new file mode 100644 index 00000000..8d263441 --- /dev/null +++ b/arch/riscv32/fe310/src/freedom-metal/src/drivers/sifive_uart0.c @@ -0,0 +1,244 @@ +/* Copyright 2018 SiFive, Inc */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include + +#ifdef METAL_SIFIVE_UART0 + +#include +#include + +/* TXDATA Fields */ +#define UART_TXEN (1 << 0) +#define UART_TXFULL (1 << 31) + +/* RXDATA Fields */ +#define UART_RXEN (1 << 0) +#define UART_RXEMPTY (1 << 31) + +/* TXCTRL Fields */ +#define UART_NSTOP (1 << 1) +#define UART_TXCNT(count) ((0x7 & count) << 16) + +/* RXCTRL Fields */ +#define UART_RXCNT(count) ((0x7 & count) << 16) + +/* IP Fields */ +#define UART_TXWM (1 << 0) +#define UART_RXWM (1 << 1) + +#define UART_REG(offset) (((unsigned long)control_base + offset)) +#define UART_REGB(offset) \ + (__METAL_ACCESS_ONCE((__metal_io_u8 *)UART_REG(offset))) +#define UART_REGW(offset) \ + (__METAL_ACCESS_ONCE((__metal_io_u32 *)UART_REG(offset))) + +struct metal_interrupt * +__metal_driver_sifive_uart0_interrupt_controller(struct metal_uart *uart) { + return __metal_driver_sifive_uart0_interrupt_parent(uart); +} + +int __metal_driver_sifive_uart0_get_interrupt_id(struct metal_uart *uart) { + return __metal_driver_sifive_uart0_interrupt_line(uart); +} + +int __metal_driver_sifive_uart0_tx_interrupt_enable(struct metal_uart *uart) { + long control_base = __metal_driver_sifive_uart0_control_base(uart); + + UART_REGW(METAL_SIFIVE_UART0_IE) |= UART_TXWM; + return 0; +} + +int __metal_driver_sifive_uart0_tx_interrupt_disable(struct metal_uart *uart) { + long control_base = __metal_driver_sifive_uart0_control_base(uart); + + UART_REGW(METAL_SIFIVE_UART0_IE) &= ~UART_TXWM; + return 0; +} + +int __metal_driver_sifive_uart0_rx_interrupt_enable(struct metal_uart *uart) { + long control_base = __metal_driver_sifive_uart0_control_base(uart); + + UART_REGW(METAL_SIFIVE_UART0_IE) |= UART_RXWM; + return 0; +} + +int __metal_driver_sifive_uart0_rx_interrupt_disable(struct metal_uart *uart) { + long control_base = __metal_driver_sifive_uart0_control_base(uart); + + UART_REGW(METAL_SIFIVE_UART0_IE) &= ~UART_RXWM; + return 0; +} + +int __metal_driver_sifive_uart0_txready(struct metal_uart *uart) { + long control_base = __metal_driver_sifive_uart0_control_base(uart); + + return !!((UART_REGW(METAL_SIFIVE_UART0_TXDATA) & UART_TXFULL)); +} + +int __metal_driver_sifive_uart0_set_tx_watermark(struct metal_uart *uart, + size_t level) { + long control_base = __metal_driver_sifive_uart0_control_base(uart); + + UART_REGW(METAL_SIFIVE_UART0_TXCTRL) |= UART_TXCNT(level); + return 0; +} + +size_t __metal_driver_sifive_uart0_get_tx_watermark(struct metal_uart *uart) { + long control_base = __metal_driver_sifive_uart0_control_base(uart); + + return ((UART_REGW(METAL_SIFIVE_UART0_TXCTRL) >> 16) & 0x7); +} + +int __metal_driver_sifive_uart0_set_rx_watermark(struct metal_uart *uart, + size_t level) { + long control_base = __metal_driver_sifive_uart0_control_base(uart); + + UART_REGW(METAL_SIFIVE_UART0_RXCTRL) |= UART_RXCNT(level); + return 0; +} + +size_t __metal_driver_sifive_uart0_get_rx_watermark(struct metal_uart *uart) { + long control_base = __metal_driver_sifive_uart0_control_base(uart); + + return ((UART_REGW(METAL_SIFIVE_UART0_RXCTRL) >> 16) & 0x7); +} + +int __metal_driver_sifive_uart0_putc(struct metal_uart *uart, int c) { + long control_base = __metal_driver_sifive_uart0_control_base(uart); + + while (__metal_driver_sifive_uart0_txready(uart) != 0) { + /* wait */ + } + UART_REGW(METAL_SIFIVE_UART0_TXDATA) = c; + return 0; +} + +int __metal_driver_sifive_uart0_getc(struct metal_uart *uart, int *c) { + uint32_t ch; + long control_base = __metal_driver_sifive_uart0_control_base(uart); + /* No seperate status register, we get status and the byte at same time */ + ch = UART_REGW(METAL_SIFIVE_UART0_RXDATA); + ; + if (ch & UART_RXEMPTY) { + *c = -1; /* aka: EOF in most of the world */ + } else { + *c = ch & 0x0ff; + } + return 0; +} + +int __metal_driver_sifive_uart0_get_baud_rate(struct metal_uart *guart) { + struct __metal_driver_sifive_uart0 *uart = (void *)guart; + return uart->baud_rate; +} + +int __metal_driver_sifive_uart0_set_baud_rate(struct metal_uart *guart, + int baud_rate) { + struct __metal_driver_sifive_uart0 *uart = (void *)guart; + long control_base = __metal_driver_sifive_uart0_control_base(guart); + struct metal_clock *clock = __metal_driver_sifive_uart0_clock(guart); + + uart->baud_rate = baud_rate; + + if (clock != NULL) { + long clock_rate = clock->vtable->get_rate_hz(clock); + UART_REGW(METAL_SIFIVE_UART0_DIV) = clock_rate / baud_rate - 1; + UART_REGW(METAL_SIFIVE_UART0_TXCTRL) |= UART_TXEN; + UART_REGW(METAL_SIFIVE_UART0_RXCTRL) |= UART_RXEN; + } + return 0; +} + +static void pre_rate_change_callback_func(void *priv) { + struct __metal_driver_sifive_uart0 *uart = priv; + long control_base = + __metal_driver_sifive_uart0_control_base((struct metal_uart *)priv); + struct metal_clock *clock = + __metal_driver_sifive_uart0_clock((struct metal_uart *)priv); + + /* Detect when the TXDATA is empty by setting the transmit watermark count + * to one and waiting until an interrupt is pending */ + + UART_REGW(METAL_SIFIVE_UART0_TXCTRL) &= ~(UART_TXCNT(0x7)); + UART_REGW(METAL_SIFIVE_UART0_TXCTRL) |= UART_TXCNT(1); + + while ((UART_REGW(METAL_SIFIVE_UART0_IP) & UART_TXWM) == 0) + ; + + /* When the TXDATA clears, the UART is still shifting out the last byte. + * Calculate the time we must drain to finish transmitting and then wait + * that long. */ + + long bits_per_symbol = + (UART_REGW(METAL_SIFIVE_UART0_TXCTRL) & (1 << 1)) ? 9 : 10; + long clk_freq = clock->vtable->get_rate_hz(clock); + long cycles_to_wait = bits_per_symbol * clk_freq / uart->baud_rate; + + for (volatile long x = 0; x < cycles_to_wait; x++) + __asm__("nop"); +} + +static void post_rate_change_callback_func(void *priv) { + struct __metal_driver_sifive_uart0 *uart = priv; + metal_uart_set_baud_rate(&uart->uart, uart->baud_rate); +} + +void __metal_driver_sifive_uart0_init(struct metal_uart *guart, int baud_rate) { + struct __metal_driver_sifive_uart0 *uart = (void *)(guart); + struct metal_clock *clock = __metal_driver_sifive_uart0_clock(guart); + struct __metal_driver_sifive_gpio0 *pinmux = + __metal_driver_sifive_uart0_pinmux(guart); + + if (clock != NULL) { + uart->pre_rate_change_callback.callback = + &pre_rate_change_callback_func; + uart->pre_rate_change_callback.priv = guart; + metal_clock_register_pre_rate_change_callback( + clock, &(uart->pre_rate_change_callback)); + + uart->post_rate_change_callback.callback = + &post_rate_change_callback_func; + uart->post_rate_change_callback.priv = guart; + metal_clock_register_post_rate_change_callback( + clock, &(uart->post_rate_change_callback)); + } + + metal_uart_set_baud_rate(&(uart->uart), baud_rate); + + if (pinmux != NULL) { + long pinmux_output_selector = + __metal_driver_sifive_uart0_pinmux_output_selector(guart); + long pinmux_source_selector = + __metal_driver_sifive_uart0_pinmux_source_selector(guart); + pinmux->gpio.vtable->enable_io((struct metal_gpio *)pinmux, + pinmux_output_selector, + pinmux_source_selector); + } +} + +__METAL_DEFINE_VTABLE(__metal_driver_vtable_sifive_uart0) = { + .uart.init = __metal_driver_sifive_uart0_init, + .uart.putc = __metal_driver_sifive_uart0_putc, + .uart.getc = __metal_driver_sifive_uart0_getc, + .uart.txready = __metal_driver_sifive_uart0_txready, + .uart.get_baud_rate = __metal_driver_sifive_uart0_get_baud_rate, + .uart.set_baud_rate = __metal_driver_sifive_uart0_set_baud_rate, + .uart.controller_interrupt = + __metal_driver_sifive_uart0_interrupt_controller, + .uart.get_interrupt_id = __metal_driver_sifive_uart0_get_interrupt_id, + .uart.tx_interrupt_enable = __metal_driver_sifive_uart0_tx_interrupt_enable, + .uart.tx_interrupt_disable = + __metal_driver_sifive_uart0_tx_interrupt_disable, + .uart.rx_interrupt_enable = __metal_driver_sifive_uart0_rx_interrupt_enable, + .uart.rx_interrupt_disable = + __metal_driver_sifive_uart0_rx_interrupt_disable, + .uart.set_tx_watermark = __metal_driver_sifive_uart0_set_tx_watermark, + .uart.get_tx_watermark = __metal_driver_sifive_uart0_get_tx_watermark, + .uart.set_rx_watermark = __metal_driver_sifive_uart0_set_rx_watermark, + .uart.get_rx_watermark = __metal_driver_sifive_uart0_get_rx_watermark, +}; + +#endif /* METAL_SIFIVE_UART0 */ + +typedef int no_empty_translation_units; diff --git a/arch/riscv32/fe310/src/freedom-metal/src/drivers/sifive_wdog0.c b/arch/riscv32/fe310/src/freedom-metal/src/drivers/sifive_wdog0.c new file mode 100644 index 00000000..4f37b7eb --- /dev/null +++ b/arch/riscv32/fe310/src/freedom-metal/src/drivers/sifive_wdog0.c @@ -0,0 +1,238 @@ +/* Copyright 2019 SiFive, Inc */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include + +#ifdef METAL_SIFIVE_WDOG0 + +#include +#include + +#include + +/* WDOGCFG */ +#define METAL_WDOGCFG_SCALE_MASK 7 +#define METAL_WDOGCFG_RSTEN (1 << 8) +#define METAL_WDOGCFG_ZEROCMP (1 << 9) +#define METAL_WDOGCFG_ENALWAYS (1 << 12) +#define METAL_WDOGCFG_COREAWAKE (1 << 13) +#define METAL_WDOGCFG_IP (1 << 28) + +/* WDOGCMP */ +#define METAL_WDOGCMP_MASK 0xFFFF + +#define WDOG_REG(base, offset) (((unsigned long)base + offset)) +#define WDOG_REGB(base, offset) \ + (__METAL_ACCESS_ONCE((__metal_io_u8 *)WDOG_REG(base, offset))) +#define WDOG_REGW(base, offset) \ + (__METAL_ACCESS_ONCE((__metal_io_u32 *)WDOG_REG(base, offset))) + +/* All writes to watchdog registers must be precedded by a write of + * a magic number to WDOGKEY */ +#define WDOG_UNLOCK(base) \ + (WDOG_REGW(base, METAL_SIFIVE_WDOG0_WDOGKEY) = METAL_SIFIVE_WDOG0_MAGIC_KEY) + +/* Unlock the watchdog and then perform a register access */ +#define WDOG_UNLOCK_REGW(base, offset) \ + WDOG_UNLOCK(base); \ + WDOG_REGW(base, offset) + +int __metal_driver_sifive_wdog0_feed(const struct metal_watchdog *const wdog) { + const uintptr_t base = + (uintptr_t)__metal_driver_sifive_wdog0_control_base(wdog); + + WDOG_UNLOCK_REGW(base, METAL_SIFIVE_WDOG0_WDOGFEED) = + METAL_SIFIVE_WDOG0_MAGIC_FOOD; + + return 0; +} + +long int +__metal_driver_sifive_wdog0_get_rate(const struct metal_watchdog *const wdog) { + const uintptr_t base = + (uintptr_t)__metal_driver_sifive_wdog0_control_base(wdog); + const struct metal_clock *const clock = + __metal_driver_sifive_wdog0_clock(wdog); + + const long int clock_rate = metal_clock_get_rate_hz(clock); + + if (clock_rate == 0) + return -1; + + const unsigned int scale = (WDOG_REGW(base, METAL_SIFIVE_WDOG0_WDOGCFG) & + METAL_WDOGCFG_SCALE_MASK); + + return clock_rate / (1 << scale); +} + +long int +__metal_driver_sifive_wdog0_set_rate(const struct metal_watchdog *const wdog, + const long int rate) { + const uintptr_t base = + (uintptr_t)__metal_driver_sifive_wdog0_control_base(wdog); + const struct metal_clock *const clock = + __metal_driver_sifive_wdog0_clock(wdog); + + const long int clock_rate = metal_clock_get_rate_hz(clock); + + if (rate >= clock_rate) { + /* We can't scale the rate above the driving clock. Clear the scale + * field and return the driving clock rate */ + WDOG_UNLOCK_REGW(base, METAL_SIFIVE_WDOG0_WDOGCFG) &= + ~(METAL_WDOGCFG_SCALE_MASK); + return clock_rate; + } + + /* Look for the closest scale value */ + long min_diff = LONG_MAX; + unsigned int min_scale = 0; + for (int i = 0; i < METAL_WDOGCFG_SCALE_MASK; i++) { + const long int new_rate = clock_rate / (1 << i); + + long int diff = rate - new_rate; + if (diff < 0) + diff *= -1; + + if (diff < min_diff) { + min_diff = diff; + min_scale = i; + } + } + + WDOG_UNLOCK_REGW(base, METAL_SIFIVE_WDOG0_WDOGCFG) &= + ~(METAL_WDOGCFG_SCALE_MASK); + WDOG_UNLOCK_REGW(base, METAL_SIFIVE_WDOG0_WDOGCFG) |= + (METAL_WDOGCFG_SCALE_MASK & min_scale); + + return clock_rate / (1 << min_scale); +} + +long int __metal_driver_sifive_wdog0_get_timeout( + const struct metal_watchdog *const wdog) { + const uintptr_t base = + (uintptr_t)__metal_driver_sifive_wdog0_control_base(wdog); + + return (WDOG_REGW(base, METAL_SIFIVE_WDOG0_WDOGCMP) & METAL_WDOGCMP_MASK); +} + +long int +__metal_driver_sifive_wdog0_set_timeout(const struct metal_watchdog *const wdog, + const long int timeout) { + const uintptr_t base = + (uintptr_t)__metal_driver_sifive_wdog0_control_base(wdog); + + /* Cap the timeout at the max value */ + const long int set_timeout = + timeout > METAL_WDOGCMP_MASK ? METAL_WDOGCMP_MASK : timeout; + + /* If we edit the timeout value in-place by masking the compare value to 0 + * and then writing it, we cause a spurious interrupt because the compare + * value is temporarily 0. Instead, read the value into a local variable, + * modify it there, and then write the whole register back */ + uint32_t wdogcmp = WDOG_REGW(base, METAL_SIFIVE_WDOG0_WDOGCMP); + + wdogcmp &= ~(METAL_WDOGCMP_MASK); + wdogcmp |= set_timeout; + + WDOG_UNLOCK_REGW(base, METAL_SIFIVE_WDOG0_WDOGCMP) = wdogcmp; + + return set_timeout; +} + +int __metal_driver_sifive_wdog0_set_result( + const struct metal_watchdog *const wdog, + const enum metal_watchdog_result result) { + const uintptr_t base = + (uintptr_t)__metal_driver_sifive_wdog0_control_base(wdog); + + /* Turn off reset enable and counter reset */ + WDOG_UNLOCK_REGW(base, METAL_SIFIVE_WDOG0_WDOGCFG) &= + ~(METAL_WDOGCFG_RSTEN | METAL_WDOGCFG_ZEROCMP); + + switch (result) { + default: + case METAL_WATCHDOG_NO_RESULT: + break; + case METAL_WATCHDOG_INTERRUPT: + /* Reset counter to zero after match */ + WDOG_UNLOCK_REGW(base, METAL_SIFIVE_WDOG0_WDOGCFG) |= + METAL_WDOGCFG_ZEROCMP; + break; + case METAL_WATCHDOG_FULL_RESET: + WDOG_UNLOCK_REGW(base, METAL_SIFIVE_WDOG0_WDOGCFG) |= + METAL_WDOGCFG_RSTEN; + break; + } + + return 0; +} + +int __metal_driver_sifive_wdog0_run( + const struct metal_watchdog *const wdog, + const enum metal_watchdog_run_option option) { + const uintptr_t base = + (uintptr_t)__metal_driver_sifive_wdog0_control_base(wdog); + + WDOG_UNLOCK_REGW(base, METAL_SIFIVE_WDOG0_WDOGCFG) &= + ~(METAL_WDOGCFG_ENALWAYS | METAL_WDOGCFG_COREAWAKE); + + switch (option) { + default: + case METAL_WATCHDOG_STOP: + break; + case METAL_WATCHDOG_RUN_ALWAYS: + /* Feed the watchdog before starting to reset counter */ + __metal_driver_sifive_wdog0_feed(wdog); + + WDOG_UNLOCK_REGW(base, METAL_SIFIVE_WDOG0_WDOGCFG) |= + METAL_WDOGCFG_ENALWAYS; + break; + case METAL_WATCHDOG_RUN_AWAKE: + /* Feed the watchdog before starting to reset counter */ + __metal_driver_sifive_wdog0_feed(wdog); + + WDOG_UNLOCK_REGW(base, METAL_SIFIVE_WDOG0_WDOGCFG) |= + METAL_WDOGCFG_COREAWAKE; + break; + } + + return 0; +} + +struct metal_interrupt *__metal_driver_sifive_wdog0_get_interrupt( + const struct metal_watchdog *const wdog) { + return __metal_driver_sifive_wdog0_interrupt_parent(wdog); +} + +int __metal_driver_sifive_wdog0_get_interrupt_id( + const struct metal_watchdog *const wdog) { + return __metal_driver_sifive_wdog0_interrupt_line(wdog); +} + +int __metal_driver_sifive_wdog0_clear_interrupt( + const struct metal_watchdog *const wdog) { + const uintptr_t base = + (uintptr_t)__metal_driver_sifive_wdog0_control_base(wdog); + + /* Clear the interrupt pending bit */ + WDOG_UNLOCK_REGW(base, METAL_SIFIVE_WDOG0_WDOGCFG) &= ~(METAL_WDOGCFG_IP); + + return 0; +} + +__METAL_DEFINE_VTABLE(__metal_driver_vtable_sifive_wdog0) = { + .watchdog.feed = __metal_driver_sifive_wdog0_feed, + .watchdog.get_rate = __metal_driver_sifive_wdog0_get_rate, + .watchdog.set_rate = __metal_driver_sifive_wdog0_set_rate, + .watchdog.get_timeout = __metal_driver_sifive_wdog0_get_timeout, + .watchdog.set_timeout = __metal_driver_sifive_wdog0_set_timeout, + .watchdog.set_result = __metal_driver_sifive_wdog0_set_result, + .watchdog.run = __metal_driver_sifive_wdog0_run, + .watchdog.get_interrupt = __metal_driver_sifive_wdog0_get_interrupt, + .watchdog.get_interrupt_id = __metal_driver_sifive_wdog0_get_interrupt_id, + .watchdog.clear_interrupt = __metal_driver_sifive_wdog0_clear_interrupt, +}; + +#endif /* METAL_SIFIVE_WDOG0 */ + +typedef int no_empty_translation_units; diff --git a/arch/riscv32/fe310/src/freedom-metal/src/drivers/ucb_htif0.c b/arch/riscv32/fe310/src/freedom-metal/src/drivers/ucb_htif0.c new file mode 100644 index 00000000..417acbf0 --- /dev/null +++ b/arch/riscv32/fe310/src/freedom-metal/src/drivers/ucb_htif0.c @@ -0,0 +1,128 @@ +/* Copyright 2018 SiFive, Inc */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include + +#ifdef METAL_UCB_HTIF0 + +#include +#include +#include +#include + +#define FINISHER_OFFSET 0 + +volatile uint64_t fromhost __attribute__((aligned(4096))); +volatile uint64_t tohost __attribute__((aligned(4096))); + +#if __riscv_xlen == 64 +#define TOHOST_CMD(dev, cmd, payload) \ + (((uint64_t)(dev) << 56) | ((uint64_t)(cmd) << 48) | (uint64_t)(payload)) +#else +#define TOHOST_CMD(dev, cmd, payload) \ + ({ \ + if ((dev) || (cmd)) \ + __builtin_trap(); \ + (payload); \ + }) +#endif +#define FROMHOST_DEV(fromhost_value) ((uint64_t)(fromhost_value) >> 56) +#define FROMHOST_CMD(fromhost_value) ((uint64_t)(fromhost_value) << 8 >> 56) +#define FROMHOST_DATA(fromhost_value) ((uint64_t)(fromhost_value) << 16 >> 16) + +static void __check_fromhost() { + uint64_t fh = fromhost; + if (!fh) + return; + fromhost = 0; +} + +static void __set_tohost(uintptr_t dev, uintptr_t cmd, uintptr_t data) { + while (tohost) + __check_fromhost(); + tohost = TOHOST_CMD(dev, cmd, data); +} + +static void do_tohost_fromhost(uintptr_t dev, uintptr_t cmd, uintptr_t data) { + __set_tohost(dev, cmd, data); + + while (1) { + uint64_t fh = fromhost; + if (fh) { + if (FROMHOST_DEV(fh) == dev && FROMHOST_CMD(fh) == cmd) { + fromhost = 0; + break; + } + __check_fromhost(); + } + } +} + +void __metal_driver_ucb_htif0_init(struct metal_uart *uart, int baud_rate) {} + +void __metal_driver_ucb_htif0_exit(const struct __metal_shutdown *sd, + int code) { + volatile uint64_t magic_mem[8]; + magic_mem[0] = 93; // SYS_exit + magic_mem[1] = code; + magic_mem[2] = 0; + magic_mem[3] = 0; + + do_tohost_fromhost(0, 0, (uintptr_t)magic_mem); + + while (1) { + // loop forever + } +} + +int __metal_driver_ucb_htif0_putc(struct metal_uart *htif, int c) { + volatile uint64_t magic_mem[8]; + magic_mem[0] = 64; // SYS_write + magic_mem[1] = 1; + magic_mem[2] = (uintptr_t)&c; + magic_mem[3] = 1; + + do_tohost_fromhost(0, 0, (uintptr_t)magic_mem); + + return 0; +} + +int __metal_driver_ucb_htif0_getc(struct metal_uart *htif, int *c) { + return -1; +} + +int __metal_driver_ucb_htif0_get_baud_rate(struct metal_uart *guart) { + return 0; +} + +int __metal_driver_ucb_htif0_set_baud_rate(struct metal_uart *guart, + int baud_rate) { + return 0; +} + +struct metal_interrupt * +__metal_driver_ucb_htif0_interrupt_controller(struct metal_uart *uart) { + return NULL; +} + +int __metal_driver_ucb_htif0_get_interrupt_id(struct metal_uart *uart) { + return -1; +} + +__METAL_DEFINE_VTABLE(__metal_driver_vtable_ucb_htif0_shutdown) = { + .shutdown.exit = &__metal_driver_ucb_htif0_exit, +}; + +__METAL_DEFINE_VTABLE(__metal_driver_vtable_ucb_htif0_uart) = { + .uart.init = __metal_driver_ucb_htif0_init, + .uart.putc = __metal_driver_ucb_htif0_putc, + .uart.getc = __metal_driver_ucb_htif0_getc, + .uart.get_baud_rate = __metal_driver_ucb_htif0_get_baud_rate, + .uart.set_baud_rate = __metal_driver_ucb_htif0_set_baud_rate, + .uart.controller_interrupt = __metal_driver_ucb_htif0_interrupt_controller, + .uart.get_interrupt_id = __metal_driver_ucb_htif0_get_interrupt_id, +}; + +#endif /* METAL_UCB_HTIF0 */ + +typedef int no_empty_translation_units; diff --git a/arch/riscv32/fe310/src/freedom-metal/src/entry.S b/arch/riscv32/fe310/src/freedom-metal/src/entry.S new file mode 100644 index 00000000..6a9c282c --- /dev/null +++ b/arch/riscv32/fe310/src/freedom-metal/src/entry.S @@ -0,0 +1,129 @@ +/* Copyright 2018 SiFive, Inc */ +/* SPDX-License-Identifier: Apache-2.0 */ + +/* This code executes before _start, which is contained inside the C library. + * In embedded systems we want to ensure that _enter, which contains the first + * code to be executed, can be loaded at a specific address. To enable this + * feature we provide the '.text.metal.init.enter' section, which is + * defined to have the first address being where execution should start. */ +.section .text.metal.init.enter +.global _enter +_enter: + .cfi_startproc + + /* Inform the debugger that there is nowhere to backtrace past _enter. */ + .cfi_undefined ra + + /* The absolute first thing that must happen is configuring the global + * pointer register, which must be done with relaxation disabled because + * it's not valid to obtain the address of any symbol without GP + * configured. The C environment might go ahead and do this again, but + * that's safe as it's a fixed register. */ +.option push +.option norelax +#ifdef __riscv_cmodel_compact +1: + auipc a0, %pcrel_hi(__global_pointer__) + addi a0, a0, %pcrel_lo(1b) + ld gp, 0(a0) + add gp, gp, a0 +#else + la gp, __global_pointer$ +#endif +.option pop + + /* trap over the chicken bit register clearing, aloe & fe310 dont have it */ + la t0, 1f + csrw mtvec, t0 + /* chicken bit is enable if core are sifive series. */ + la t0, __metal_chicken_bit + beqz t0, 1f + /* If set, always clear the feature disable register for all cores series */ + csrwi 0x7C1, 0 +.align 4 +1: + /* Set up a simple trap vector to catch anything that goes wrong early in + * the boot process. */ + la t0, early_trap_vector + csrw mtvec, t0 + + /* There may be pre-initialization routines inside the MBI code that run in + * C, so here we set up a C environment. First we set up a stack pointer, + * which is left as a weak reference in order to allow initialization + * routines that do not need a stack to be set up to transparently be + * called. */ + .weak __metal_stack_pointer + la sp, __metal_stack_pointer + + /* The METAL is designed for a bare-metal environment and therefore is expected + * to define its own stack pointer. We also align the stack pointer here + * because the only RISC-V ABI that's currently defined, mandates 16-byte + * stack alignment. */ + + bne sp, zero, 1f +#ifdef __riscv_cmodel_compact + lla.gprel sp, _sp +#else + la sp, _sp +#endif +1: + /* Increment by hartid number of stack sizes */ + csrr a0, mhartid + li t0, 0 + la t1, __stack_size +1: + andi sp, sp, -16 + beq t0, a0, 1f + add sp, sp, t1 + addi t0, t0, 1 + j 1b +1: + + /* Check for an initialization routine and call it if one exists, otherwise + * just skip over the call entirely. Note that __metal_initialize isn't + * actually a full C function, as it doesn't end up with the .bss or .data + * segments having been initialized. This is done to avoid putting a + * burden on systems that can be initialized without having a C environment + * set up. */ + la ra, __metal_before_start + beqz ra, 1f + jalr ra +1: + + /* At this point we can enter the C runtime's startup file. The arguments + * to this function are designed to match those provided to the SEE, just + * so we don't have to write another ABI. */ + csrr a0, mhartid + li a1, 0 + li a2, 0 + call _start + + /* If we've made it back here then there's probably something wrong. We + * allow the METAL to register a handler here. */ + .weak __metal_after_main + la ra, __metal_after_main + beqz ra, 1f + jalr ra +1: + + /* If that handler returns then there's not a whole lot we can do. Just + * try to make some noise. */ + la t0, 1f + csrw mtvec, t0 +1: + lw t1, 0(x0) + j 1b + + .cfi_endproc + +/* The GCC port might not emit a __register_frame_info symbol, which eventually + * results in a weak undefined reference that eventually causes crash when it + * is dereference early in boot. We really shouldn't need to put this here, + * but to deal with what I think is probably a bug in the linker script I'm + * going to leave this in for now. At least it's fairly cheap :) */ +.weak __register_frame_info +.section .text.metal.init.__register_frame_info +__register_frame_info: + .cfi_startproc + ret + .cfi_endproc diff --git a/arch/riscv32/fe310/src/freedom-metal/src/gpio.c b/arch/riscv32/fe310/src/freedom-metal/src/gpio.c new file mode 100644 index 00000000..83859063 --- /dev/null +++ b/arch/riscv32/fe310/src/freedom-metal/src/gpio.c @@ -0,0 +1,40 @@ +/* Copyright 2019 SiFive, Inc */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include +#include + +extern __inline__ int metal_gpio_disable_input(struct metal_gpio *gpio, + int pin); +extern __inline__ int metal_gpio_enable_input(struct metal_gpio *gpio, int pin); +extern __inline__ int metal_gpio_enable_output(struct metal_gpio *gpio, + int pin); +extern __inline__ int metal_gpio_disable_output(struct metal_gpio *gpio, + int pin); +extern __inline__ int metal_gpio_get_output_pin(struct metal_gpio *gpio, + int pin); +extern __inline__ int metal_gpio_get_input_pin(struct metal_gpio *gpio, + int pin); +extern __inline__ int metal_gpio_set_pin(struct metal_gpio *, int pin, + int value); +extern __inline__ int metal_gpio_clear_pin(struct metal_gpio *, int pin); +extern __inline__ int metal_gpio_toggle_pin(struct metal_gpio *, int pin); +extern __inline__ int metal_gpio_enable_pinmux(struct metal_gpio *, int pin, + int io_function); +extern __inline__ int metal_gpio_disable_pinmux(struct metal_gpio *, int pin); +extern __inline__ struct metal_interrupt * +metal_gpio_interrupt_controller(struct metal_gpio *gpio); +extern __inline__ int metal_gpio_get_interrupt_id(struct metal_gpio *gpio, + int pin); +extern __inline__ int metal_gpio_config_interrupt(struct metal_gpio *gpio, + int pin, int intr_type); +extern __inline__ int metal_gpio_clear_interrupt(struct metal_gpio *gpio, + int pin, int intr_type); + +struct metal_gpio *metal_gpio_get_device(unsigned int device_num) { + if (device_num > __MEE_DT_MAX_GPIOS) { + return NULL; + } + + return (struct metal_gpio *)__metal_gpio_table[device_num]; +} diff --git a/arch/riscv32/fe310/src/freedom-metal/src/hpm.c b/arch/riscv32/fe310/src/freedom-metal/src/hpm.c new file mode 100644 index 00000000..2e0bc051 --- /dev/null +++ b/arch/riscv32/fe310/src/freedom-metal/src/hpm.c @@ -0,0 +1,345 @@ +/* Copyright 2020 SiFive, Inc */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include +#include +#include + +/* Macro to generate code within a switch case */ +#define METAL_HPM_HANDLE_SWITCH(m) \ + m(3) m(4) m(5) m(6) m(7) m(8) m(9) m(10) m(11) m(12) m(13) m(14) m(15) \ + m(16) m(17) m(18) m(19) m(20) m(21) m(22) m(23) m(24) m(25) m(26) \ + m(27) m(28) m(29) m(30) m(31) + +/* Macro to set values into event selector register */ +#define METAL_HPM_SET_EVENT_REG(x) \ + case METAL_HPM_COUNTER_##x: \ + __asm__ __volatile__("csrr %0, mhpmevent" #x : "=r"(val)); \ + val &= ~bitmask; \ + val |= bitmask; \ + __asm__ __volatile__("csrw mhpmevent" #x ", %0" : : "r"(val)); \ + break; + +/* Macro to set values into event selector register */ +#define METAL_HPM_CLR_EVENT_REG(x) \ + case METAL_HPM_COUNTER_##x: \ + __asm__ __volatile__("csrr %0, mhpmevent" #x : "=r"(val)); \ + val &= ~bitmask; \ + __asm__ __volatile__("csrw mhpmevent" #x ", %0" : : "r"(val)); \ + break; + +/* Macro to get values from event selector register */ +#define METAL_HPM_GET_EVENT_REG(x) \ + case METAL_HPM_COUNTER_##x: \ + __asm__ __volatile__("csrr %0, mhpmevent" #x : "=r"(val)); \ + break; + +/* Macro to read HW performance monitor counter values */ +#if __riscv_xlen == 32 +#define METAL_HPM_GET_COUNT_REG(x) \ + case METAL_HPM_COUNTER_##x: \ + do { \ + __asm__ __volatile__("csrr %0, mhpmcounter" #x "h" : "=r"(vh)); \ + __asm__ __volatile__("csrr %0, mhpmcounter" #x : "=r"(vl)); \ + __asm__ __volatile__("csrr %0, mhpmcounter" #x "h" : "=r"(vh1)); \ + } while (vh != vh1); \ + break; +#else +#define METAL_HPM_GET_COUNT_REG(x) \ + case METAL_HPM_COUNTER_##x: \ + __asm__ __volatile__("csrr %0, mhpmcounter" #x : "=r"(vl)); \ + break; +#endif + +/* Macro to clear HW performance monitor counter values */ +#if __riscv_xlen == 32 +#define METAL_HPM_CLR_COUNT_REG(x) \ + case METAL_HPM_COUNTER_##x: \ + __asm__ __volatile__("csrw mhpmcounter" #x "h, zero"); \ + __asm__ __volatile__("csrw mhpmcounter" #x ", zero"); \ + __asm__ __volatile__("csrw mhpmcounter" #x "h, zero"); \ + break; +#else +#define METAL_HPM_CLR_COUNT_REG(x) \ + case METAL_HPM_COUNTER_##x: \ + __asm__ __volatile__("csrw mhpmcounter" #x ", zero"); \ + break; +#endif + +/* Max available counters */ +#define METAL_HPM_COUNT_MAX 32 + +/* Macro to check for instruction trap */ +#define MCAUSE_ILLEGAL_INST 0x02 + +/* Return codes */ +#define METAL_HPM_RET_OK 0 +#define METAL_HPM_RET_NOK 1 + +int metal_hpm_init(struct metal_cpu *gcpu) { + struct __metal_driver_cpu *cpu = (void *)gcpu; + + /* Check if counters are initialized or pointer is NULL */ + if ((gcpu) && (cpu->hpm_count == 0)) { + metal_hpm_counter n; + + /* Count number of available hardware performance counters */ + cpu->hpm_count = METAL_HPM_COUNT_MAX; + + /* mcycle, mtime and minstret counters are always available */ + for (n = METAL_HPM_COUNTER_3; n < METAL_HPM_COUNTER_31; n++) { + metal_hpm_set_event(gcpu, n, 0xFFFFFFFF); + + if (metal_hpm_get_event(gcpu, n) == 0) { + break; + } + } + cpu->hpm_count = n; + + /* TODO: mcountinhibit csr is not yet accessible. + * As per latest RiscV privileged spec v1.11, + * mcountinhibit controls which of the counters increment. + * Unused counters can be disabled to reduce power consumption. */ + /* Keep all counters disabled, enable them later on as needed. */ + /* __asm__ __volatile__("csrw mcountinhibit, zero"); */ + + /* Clear all counters */ + for (unsigned int i = 0; i < cpu->hpm_count; i++) { + metal_hpm_clr_event(gcpu, i, 0xFFFFFFFF); + metal_hpm_clear_counter(gcpu, i); + } + } else { + return METAL_HPM_RET_NOK; + } + + return METAL_HPM_RET_OK; +} + +int metal_hpm_disable(struct metal_cpu *gcpu) { + struct __metal_driver_cpu *cpu = (void *)gcpu; + uintptr_t temp = 0, val = 0; + + /* Check if pointer is NULL */ + if (gcpu) { + /* Disable counter access */ + __asm__ __volatile__("la %0, 1f \n\t" + "csrr %1, mtvec \n\t" + "csrw mtvec, %0 \n\t" + "csrw mcounteren, zero \n\t" + ".align 4 \n\t" + "1: \n\t" + "csrw mtvec, %1 \n\t" + : "+r"(val), "+r"(temp)); + + /* TODO: Disable all counters */ + /* __asm__ __volatile__("csrw mcountinhibit, zero"); */ + + cpu->hpm_count = 0; + } else { + return METAL_HPM_RET_NOK; + } + + return METAL_HPM_RET_OK; +} + +int metal_hpm_set_event(struct metal_cpu *gcpu, metal_hpm_counter counter, + unsigned int bitmask) { + struct __metal_driver_cpu *cpu = (void *)gcpu; + unsigned int val; + + /* Return error if counter is out of range or pointer is NULL */ + if ((gcpu) && (counter >= cpu->hpm_count)) + return METAL_HPM_RET_NOK; + + switch (counter) { + /* Set event register bit mask as requested */ + METAL_HPM_HANDLE_SWITCH(METAL_HPM_SET_EVENT_REG) + + default: + break; + } + + return METAL_HPM_RET_OK; +} + +unsigned int metal_hpm_get_event(struct metal_cpu *gcpu, + metal_hpm_counter counter) { + struct __metal_driver_cpu *cpu = (void *)gcpu; + unsigned int val = 0; + + /* Return error if counter is out of range or pointer is NULL */ + if ((gcpu) && (counter >= cpu->hpm_count)) + return METAL_HPM_RET_NOK; + + switch (counter) { + /* Read event registers */ + METAL_HPM_HANDLE_SWITCH(METAL_HPM_GET_EVENT_REG) + + default: + break; + } + + return val; +} + +int metal_hpm_clr_event(struct metal_cpu *gcpu, metal_hpm_counter counter, + unsigned int bitmask) { + struct __metal_driver_cpu *cpu = (void *)gcpu; + unsigned int val; + + /* Return error if counter is out of range or pointer is NULL */ + if ((gcpu) && (counter >= cpu->hpm_count)) + return METAL_HPM_RET_NOK; + + switch (counter) { + /* Clear event registers as requested */ + METAL_HPM_HANDLE_SWITCH(METAL_HPM_CLR_EVENT_REG) + + default: + break; + } + + return METAL_HPM_RET_OK; +} + +int metal_hpm_enable_access(struct metal_cpu *gcpu, metal_hpm_counter counter) { + struct __metal_driver_cpu *cpu = (void *)gcpu; + uintptr_t temp = 0, val = 0; + + /* Return error if counter is out of range or pointer is NULL */ + if ((gcpu) && (counter >= cpu->hpm_count)) + return METAL_HPM_RET_NOK; + + /* Set trap exit, to handle illegal instruction trap. */ + __asm__ __volatile__("la %0, 1f \n\t" + "csrr %1, mtvec \n\t" + "csrw mtvec, %0 \n\t" + "csrr %0, mcounteren \n\t" + "or %0, %0, %2 \n\t" + "csrw mcounteren, %0 \n\t" + ".align 4 \n\t" + "1: \n\t" + "csrw mtvec, %1 \n\t" + : "+r"(val), "+r"(temp) + : "r"(1 << counter)); + + return METAL_HPM_RET_OK; +} + +int metal_hpm_disable_access(struct metal_cpu *gcpu, + metal_hpm_counter counter) { + struct __metal_driver_cpu *cpu = (void *)gcpu; + uintptr_t temp = 0, val = 0; + + /* Return error if counter is out of range or pointer is NULL */ + if ((gcpu) && (counter >= cpu->hpm_count)) + return METAL_HPM_RET_NOK; + + /* Set trap exit, to handle illegal instruction trap. */ + __asm__ __volatile__("la %0, 1f \n\t" + "csrr %1, mtvec \n\t" + "csrw mtvec, %0 \n\t" + "csrr %0, mcounteren \n\t" + "and %0, %0, %2 \n\t" + "csrw mcounteren, %0 \n\t" + ".align 4 \n\t" + "1: \n\t" + "csrw mtvec, %1 \n\t" + : "+r"(val), "+r"(temp) + : "r"(~(1 << counter))); + + return METAL_HPM_RET_OK; +} + +unsigned long long metal_hpm_read_counter(struct metal_cpu *gcpu, + metal_hpm_counter counter) { + struct __metal_driver_cpu *cpu = (void *)gcpu; +#if __riscv_xlen == 32 + unsigned int vh = 0, vh1 = 0, vl = 0; +#else + unsigned long long vl = 0; +#endif + + /* Return error if counter is out of range or pointer is NULL */ + if ((gcpu) && (counter >= cpu->hpm_count)) + return METAL_HPM_RET_NOK; + + switch (counter) { + case METAL_HPM_CYCLE: +#if __riscv_xlen == 32 + do { + __asm__ __volatile__("csrr %0, mcycleh" : "=r"(vh)); + __asm__ __volatile__("csrr %0, mcycle" : "=r"(vl)); + __asm__ __volatile__("csrr %0, mcycleh" : "=r"(vh1)); + } while (vh != vh1); +#else + __asm__ __volatile__("csrr %0, mcycle" : "=r"(vl)); +#endif + break; + case METAL_HPM_TIME: + /* mtime is memory mapped within CLINT block, + * Use CLINT APIs to access this register. */ + return METAL_HPM_RET_NOK; + break; + + case METAL_HPM_INSTRET: +#if __riscv_xlen == 32 + do { + __asm__ __volatile__("csrr %0, minstreth" : "=r"(vh)); + __asm__ __volatile__("csrr %0, minstret" : "=r"(vl)); + __asm__ __volatile__("csrr %0, minstreth" : "=r"(vh1)); + } while (vh != vh1); +#else + __asm__ __volatile__("csrr %0, minstret" : "=r"(vl)); +#endif + break; + METAL_HPM_HANDLE_SWITCH(METAL_HPM_GET_COUNT_REG) + + default: + break; + } + +#if __riscv_xlen == 32 + return ((((unsigned long long)vh) << 32) | vl); +#else + return vl; +#endif +} + +int metal_hpm_clear_counter(struct metal_cpu *gcpu, metal_hpm_counter counter) { + struct __metal_driver_cpu *cpu = (void *)gcpu; + /* Return error if counter is out of range or pointer is NULL */ + if ((gcpu) && (counter >= cpu->hpm_count)) + return METAL_HPM_RET_NOK; + + switch (counter) { + case METAL_HPM_CYCLE: +#if __riscv_xlen == 32 + __asm__ __volatile__("csrw mcycleh, zero"); + __asm__ __volatile__("csrw mcycle, zero"); + __asm__ __volatile__("csrw mcycleh, zero"); +#else + __asm__ __volatile__("csrw mcycle, zero"); +#endif + break; + case METAL_HPM_TIME: + /* mtime is memory mapped within CLINT block */ + return METAL_HPM_RET_NOK; + break; + case METAL_HPM_INSTRET: +#if __riscv_xlen == 32 + __asm__ __volatile__("csrw minstreth, zero"); + __asm__ __volatile__("csrw minstret, zero"); + __asm__ __volatile__("csrw minstreth, zero"); +#else + __asm__ __volatile__("csrw minstret, zero"); +#endif + break; + METAL_HPM_HANDLE_SWITCH(METAL_HPM_CLR_COUNT_REG) + + default: + break; + } + + return METAL_HPM_RET_OK; +} diff --git a/arch/riscv32/fe310/src/freedom-metal/src/i2c.c b/arch/riscv32/fe310/src/freedom-metal/src/i2c.c new file mode 100644 index 00000000..6c342aa3 --- /dev/null +++ b/arch/riscv32/fe310/src/freedom-metal/src/i2c.c @@ -0,0 +1,28 @@ +/* Copyright 2019 SiFive, Inc */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include +#include + +extern inline void metal_i2c_init(struct metal_i2c *i2c, unsigned int baud_rate, + metal_i2c_mode_t mode); +extern inline int metal_i2c_write(struct metal_i2c *i2c, unsigned int addr, + unsigned int len, unsigned char buf[], + metal_i2c_stop_bit_t stop_bit); +extern inline int metal_i2c_read(struct metal_i2c *i2c, unsigned int addr, + unsigned int len, unsigned char buf[], + metal_i2c_stop_bit_t stop_bit); +extern inline int metal_i2c_transfer(struct metal_i2c *i2c, unsigned int addr, + unsigned char txbuf[], unsigned int txlen, + unsigned char rxbuf[], unsigned int rxlen); +extern inline int metal_i2c_get_baud_rate(struct metal_i2c *i2c); +extern inline int metal_i2c_set_baud_rate(struct metal_i2c *i2c, int baud_rate); + +struct metal_i2c *metal_i2c_get_device(unsigned int device_num) { +#if __METAL_DT_MAX_I2CS > 0 + if (device_num < __METAL_DT_MAX_I2CS) { + return (struct metal_i2c *)__metal_i2c_table[device_num]; + } +#endif + return NULL; +} diff --git a/arch/riscv32/fe310/src/freedom-metal/src/init.c b/arch/riscv32/fe310/src/freedom-metal/src/init.c new file mode 100644 index 00000000..12134f7e --- /dev/null +++ b/arch/riscv32/fe310/src/freedom-metal/src/init.c @@ -0,0 +1,89 @@ +/* Copyright 2019 SiFive Inc. */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include +#include + +#include +#include + +/* + * These function pointers are created by the linker script + * in the .init_array section. The arrays defined by these + * and end points are the set of functions defined by instances + * of METAL_CONSTRUCTOR() and METAL_DESTRUCTOR(). + */ +extern metal_constructor_t metal_constructors_start; +extern metal_constructor_t metal_constructors_end; +extern metal_destructor_t metal_destructors_start; +extern metal_destructor_t metal_destructors_end; + +void metal_init(void) { + /* Make sure the constructors only run once */ + static int init_done = 0; + if (init_done) { + return; + } + init_done = 1; + +#ifdef METAL_SIFIVE_PL2CACHE0 + sifive_pl2cache0_init(); +#endif /* METAL_SIFIVE_PL2CACHE0 */ + +#ifdef METAL_SIFIVE_CCACHE0 + sifive_ccache0_init(); +#endif /* METAL_SIFIVE_CCACHE0 */ + +#ifdef METAL_SIFIVE_L2PF1 + /* Do L2 Stride Prefetcher initialization. */ + sifive_l2pf1_init(); +#endif /* METAL_SIFIVE_L2PF1 */ + + if (&metal_constructors_end <= &metal_constructors_start) { + return; + } + + metal_constructor_t *funcptr = &metal_constructors_start; + while (funcptr != &metal_constructors_end) { + metal_constructor_t func = *funcptr; + + func(); + + funcptr += 1; + } +} + +void metal_fini(void) { + /* Make sure the destructors only run once */ + static int fini_done = 0; + if (fini_done) { + return; + } + fini_done = 1; + + if (&metal_destructors_end <= &metal_destructors_start) { + return; + } + + metal_destructor_t *funcptr = &metal_destructors_start; + while (funcptr != &metal_destructors_end) { + metal_destructor_t func = *funcptr; + + func(); + + funcptr += 1; + } +} + +/* + * metal_init_run() and metal_fini_run() are marked weak so that users + * can redefine them for their own purposes, including to no-ops + * in the case that users don't want the metal constructors or + * destructors to run. + */ + +void metal_init_run(void) __attribute__((weak)); +void metal_init_run(void) { metal_init(); } + +void metal_fini_run(void) __attribute__((weak)); +void metal_fini_run(void) { metal_fini(); } diff --git a/arch/riscv32/fe310/src/freedom-metal/src/interrupt.c b/arch/riscv32/fe310/src/freedom-metal/src/interrupt.c new file mode 100644 index 00000000..c4a7050c --- /dev/null +++ b/arch/riscv32/fe310/src/freedom-metal/src/interrupt.c @@ -0,0 +1,111 @@ +/* Copyright 2018 SiFive, Inc */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include +#include +#include + +struct metal_interrupt * +metal_interrupt_get_controller(metal_intr_cntrl_type cntrl, int id) { + switch (cntrl) { + case METAL_CPU_CONTROLLER: + break; + case METAL_CLINT_CONTROLLER: +#ifdef __METAL_DT_RISCV_CLINT0_HANDLE + return __METAL_DT_RISCV_CLINT0_HANDLE; +#endif + break; + case METAL_CLIC_CONTROLLER: +#ifdef __METAL_DT_SIFIVE_CLIC0_HANDLE + return __METAL_DT_SIFIVE_CLIC0_HANDLE; +#endif + break; + case METAL_PLIC_CONTROLLER: +#ifdef __METAL_DT_RISCV_PLIC0_HANDLE + return __METAL_DT_RISCV_PLIC0_HANDLE; +#endif + break; + } + return NULL; +} + +extern __inline__ void metal_interrupt_init(struct metal_interrupt *controller); + +extern __inline__ int +metal_interrupt_set_vector_mode(struct metal_interrupt *controller, + metal_vector_mode mode); +extern __inline__ metal_vector_mode +metal_interrupt_get_vector_mode(struct metal_interrupt *controller); + +extern __inline__ int +metal_interrupt_set_privilege(struct metal_interrupt *controller, + metal_intr_priv_mode mode); +extern __inline__ metal_intr_priv_mode +metal_interrupt_get_privilege(struct metal_interrupt *controller); + +extern __inline__ int +metal_interrupt_set_threshold(struct metal_interrupt *controller, + unsigned int level); +extern __inline__ unsigned int +metal_interrupt_get_threshold(struct metal_interrupt *controller); + +extern __inline__ unsigned int +metal_interrupt_get_priority(struct metal_interrupt *controller, int id); + +extern __inline__ int +metal_interrupt_set_priority(struct metal_interrupt *controller, int id, + unsigned int priority); + +extern __inline__ int +metal_interrupt_set_preemptive_level(struct metal_interrupt *controller, int id, + unsigned int level); + +extern __inline__ unsigned int +metal_interrupt_get_preemptive_level(struct metal_interrupt *controller, + int id); + +extern __inline__ int metal_interrupt_clear(struct metal_interrupt *controller, + int id); + +extern __inline__ int metal_interrupt_set(struct metal_interrupt *controller, + int id); + +extern __inline__ int +metal_interrupt_register_handler(struct metal_interrupt *controller, int id, + metal_interrupt_handler_t handler, void *priv); + +extern __inline__ int metal_interrupt_register_vector_handler( + struct metal_interrupt *controller, int id, + metal_interrupt_vector_handler_t handler, void *priv_data); + +extern __inline__ int metal_interrupt_enable(struct metal_interrupt *controller, + int id); + +extern __inline__ int +metal_interrupt_disable(struct metal_interrupt *controller, int id); + +extern __inline__ int +metal_interrupt_vector_enable(struct metal_interrupt *controller, int id); + +extern __inline__ int +metal_interrupt_vector_disable(struct metal_interrupt *controller, int id); + +extern __inline__ int +_metal_interrupt_command_request(struct metal_interrupt *controller, int cmd, + void *data); + +extern __inline__ metal_affinity +metal_interrupt_affinity_enable(struct metal_interrupt *controller, + metal_affinity bitmask, int id); + +extern __inline__ metal_affinity +metal_interrupt_affinity_disable(struct metal_interrupt *controller, + metal_affinity bitmask, int id); + +extern __inline__ metal_affinity +metal_interrupt_affinity_set_threshold(struct metal_interrupt *controller, + metal_affinity bitmask, + unsigned int level); +extern __inline__ unsigned int +metal_interrupt_affinity_get_threshold(struct metal_interrupt *controller, + int contextid); diff --git a/arch/riscv32/fe310/src/freedom-metal/src/led.c b/arch/riscv32/fe310/src/freedom-metal/src/led.c new file mode 100644 index 00000000..f48de663 --- /dev/null +++ b/arch/riscv32/fe310/src/freedom-metal/src/led.c @@ -0,0 +1,35 @@ +/* Copyright 2018 SiFive, Inc */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include +#include +#include + +struct metal_led *metal_led_get_rgb(char *label, char *color) { + int i; + struct metal_led *led; + char led_label[100]; + + if ((__METAL_DT_MAX_LEDS == 0) || (label == NULL) || (color == NULL)) { + return NULL; + } + + strcpy(led_label, label); + strcat(led_label, color); + for (i = 0; i < __METAL_DT_MAX_LEDS; i++) { + led = (struct metal_led *)__metal_led_table[i]; + if (led->vtable->led_exist(led, led_label)) { + return led; + } + } + return NULL; +} + +struct metal_led *metal_led_get(char *label) { + return metal_led_get_rgb(label, ""); +} + +extern __inline__ void metal_led_enable(struct metal_led *led); +extern __inline__ void metal_led_on(struct metal_led *led); +extern __inline__ void metal_led_off(struct metal_led *led); +extern __inline__ void metal_led_toggle(struct metal_led *led); diff --git a/arch/riscv32/fe310/src/freedom-metal/src/lock.c b/arch/riscv32/fe310/src/freedom-metal/src/lock.c new file mode 100644 index 00000000..9e04230a --- /dev/null +++ b/arch/riscv32/fe310/src/freedom-metal/src/lock.c @@ -0,0 +1,8 @@ +/* Copyright 2019 SiFive, Inc */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include + +extern __inline__ int metal_lock_init(struct metal_lock *lock); +extern __inline__ int metal_lock_take(struct metal_lock *lock); +extern __inline__ int metal_lock_give(struct metal_lock *lock); diff --git a/arch/riscv32/fe310/src/freedom-metal/src/memory.c b/arch/riscv32/fe310/src/freedom-metal/src/memory.c new file mode 100644 index 00000000..204d8baf --- /dev/null +++ b/arch/riscv32/fe310/src/freedom-metal/src/memory.c @@ -0,0 +1,34 @@ +/* Copyright 2019 SiFive, Inc */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include +#include + +struct metal_memory *metal_get_memory_from_address(const uintptr_t address) { + for (int i = 0; i < __METAL_DT_MAX_MEMORIES; i++) { + struct metal_memory *mem = __metal_memory_table[i]; + + uintptr_t lower_bound = metal_memory_get_base_address(mem); + uintptr_t upper_bound = lower_bound + (metal_memory_get_size(mem) - 1); + + if (upper_bound < lower_bound) { + /* overflow detected this memory range is ignored */ + continue; + } + + if ((address >= lower_bound) && (address <= upper_bound)) { + return mem; + } + } + + return NULL; +} + +extern __inline__ uintptr_t +metal_memory_get_base_address(const struct metal_memory *memory); +extern __inline__ size_t +metal_memory_get_size(const struct metal_memory *memory); +extern __inline__ int +metal_memory_supports_atomics(const struct metal_memory *memory); +extern __inline__ int +metal_memory_is_cachable(const struct metal_memory *memory); diff --git a/arch/riscv32/fe310/src/freedom-metal/src/pmp.c b/arch/riscv32/fe310/src/freedom-metal/src/pmp.c new file mode 100644 index 00000000..230a6c72 --- /dev/null +++ b/arch/riscv32/fe310/src/freedom-metal/src/pmp.c @@ -0,0 +1,517 @@ +/* Copyright 2018 SiFive, Inc */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include +#include +#include + +#define CONFIG_TO_INT(_config) (*((char *)&(_config))) +#define INT_TO_CONFIG(_int) (*((struct metal_pmp_config *)(char *)&(_int))) + +struct metal_pmp *metal_pmp_get_device(void) { +#ifdef __METAL_DT_PMP_HANDLE + return __METAL_DT_PMP_HANDLE; +#else + return NULL; +#endif +} + +/* This function calculates the minimum granularity from the address + * that pmpaddr takes on after writing all ones to pmpaddr when pmpcfg = 0. + * + * Detect the address granularity based on the position of the + * least-significant 1 set in the address. + * + * For example, if the value read from pmpaddr is 0x3ffffc00, the + * least-significant set bit is in bit 10 (counting from 0), resulting + * in a detected granularity of 2^(10 + 2) = 4096. + */ +static uintptr_t _get_detected_granularity(uintptr_t address) { + if (address == 0) { + return (uintptr_t)-1; + } + + /* Get the index of the least significant set bit */ + int index = 0; + while (((address >> index) & 0x1) == 0) { + index += 1; + } + + /* The granularity is equal to 2^(index + 2) bytes */ + return (1 << (index + 2)); +} + +/* This function calculates the granularity requested by the user's provided + * value for pmpaddr. + * + * Calculate the requested granularity based on the position of the + * least-significant unset bit. + * + * For example, if the requested address is 0x20009ff, the least-significant + * unset bit is at index 9 (counting from 0), resulting in a requested + * granularity of 2^(9 + 3) = 4096. + */ +static uintptr_t _get_pmpaddr_granularity(uintptr_t address) { + /* Get the index of the least significant unset bit */ + int index = 0; + while (((address >> index) & 0x1) == 1) { + index += 1; + } + + /* The granularity is equal to 2^(index + 3) bytes */ + return (1 << (index + 3)); +} + +/* Get the number of pmp regions for the given hart */ +int metal_pmp_num_regions(int hartid) { + struct metal_cpu *cpu = metal_cpu_get(hartid); + + return __metal_driver_cpu_num_pmp_regions(cpu); +} + +/* Get the number of pmp regions for the current hart */ +static unsigned int _pmp_regions() { + return metal_pmp_num_regions(metal_cpu_get_current_hartid()); +} + +void metal_pmp_init(struct metal_pmp *pmp) { + if (!pmp) { + return; + } + + struct metal_pmp_config init_config = { + .L = METAL_PMP_UNLOCKED, + .A = METAL_PMP_OFF, + .X = 0, + .W = 0, + .R = 0, + }; + + for (unsigned int i = 0; i < _pmp_regions(); i++) { + metal_pmp_set_region(pmp, i, init_config, 0); + } + + /* Detect the region granularity by writing all 1s to pmpaddr0 while + * pmpcfg0 = 0. */ + if (metal_pmp_set_address(pmp, 0, -1) != 0) { + /* Failed to detect granularity */ + return; + } + + /* Calculate the granularity based on the value that pmpaddr0 takes on */ + pmp->_granularity[metal_cpu_get_current_hartid()] = + _get_detected_granularity(metal_pmp_get_address(pmp, 0)); + + /* Clear pmpaddr0 */ + metal_pmp_set_address(pmp, 0, 0); +} + +int metal_pmp_set_region(struct metal_pmp *pmp, unsigned int region, + struct metal_pmp_config config, size_t address) { + struct metal_pmp_config old_config; + size_t old_address; + size_t cfgmask; + size_t pmpcfg; + int rc = 0; + + if (!pmp) { + /* Device handle cannot be NULL */ + return 1; + } + + if (region > _pmp_regions()) { + /* Region outside of supported range */ + return 2; + } + + if (config.A == METAL_PMP_NA4 && + pmp->_granularity[metal_cpu_get_current_hartid()] > 4) { + /* The requested granularity is too small */ + return 3; + } + + if (config.A == METAL_PMP_NAPOT && + pmp->_granularity[metal_cpu_get_current_hartid()] > + _get_pmpaddr_granularity(address)) { + /* The requested granularity is too small */ + return 3; + } + + rc = metal_pmp_get_region(pmp, region, &old_config, &old_address); + if (rc) { + /* Error reading region */ + return rc; + } + + if (old_config.L == METAL_PMP_LOCKED) { + /* Cannot modify locked region */ + return 4; + } + + /* Update the address first, because if the region is being locked we won't + * be able to modify it after we set the config */ + if (old_address != address) { + switch (region) { + case 0: + __asm__("csrw pmpaddr0, %[addr]" ::[addr] "r"(address) :); + break; + case 1: + __asm__("csrw pmpaddr1, %[addr]" ::[addr] "r"(address) :); + break; + case 2: + __asm__("csrw pmpaddr2, %[addr]" ::[addr] "r"(address) :); + break; + case 3: + __asm__("csrw pmpaddr3, %[addr]" ::[addr] "r"(address) :); + break; + case 4: + __asm__("csrw pmpaddr4, %[addr]" ::[addr] "r"(address) :); + break; + case 5: + __asm__("csrw pmpaddr5, %[addr]" ::[addr] "r"(address) :); + break; + case 6: + __asm__("csrw pmpaddr6, %[addr]" ::[addr] "r"(address) :); + break; + case 7: + __asm__("csrw pmpaddr7, %[addr]" ::[addr] "r"(address) :); + break; + case 8: + __asm__("csrw pmpaddr8, %[addr]" ::[addr] "r"(address) :); + break; + case 9: + __asm__("csrw pmpaddr9, %[addr]" ::[addr] "r"(address) :); + break; + case 10: + __asm__("csrw pmpaddr10, %[addr]" ::[addr] "r"(address) :); + break; + case 11: + __asm__("csrw pmpaddr11, %[addr]" ::[addr] "r"(address) :); + break; + case 12: + __asm__("csrw pmpaddr12, %[addr]" ::[addr] "r"(address) :); + break; + case 13: + __asm__("csrw pmpaddr13, %[addr]" ::[addr] "r"(address) :); + break; + case 14: + __asm__("csrw pmpaddr14, %[addr]" ::[addr] "r"(address) :); + break; + case 15: + __asm__("csrw pmpaddr15, %[addr]" ::[addr] "r"(address) :); + break; + } + } + +#if __riscv_xlen == 32 + if (CONFIG_TO_INT(old_config) != CONFIG_TO_INT(config)) { + /* Mask to clear old pmpcfg */ + cfgmask = (0xFF << (8 * (region % 4))); + pmpcfg = (CONFIG_TO_INT(config) << (8 * (region % 4))); + + switch (region / 4) { + case 0: + __asm__("csrc pmpcfg0, %[mask]" ::[mask] "r"(cfgmask) :); + + __asm__("csrs pmpcfg0, %[cfg]" ::[cfg] "r"(pmpcfg) :); + break; + case 1: + __asm__("csrc pmpcfg1, %[mask]" ::[mask] "r"(cfgmask) :); + + __asm__("csrs pmpcfg1, %[cfg]" ::[cfg] "r"(pmpcfg) :); + break; + case 2: + __asm__("csrc pmpcfg2, %[mask]" ::[mask] "r"(cfgmask) :); + + __asm__("csrs pmpcfg2, %[cfg]" ::[cfg] "r"(pmpcfg) :); + break; + case 3: + __asm__("csrc pmpcfg3, %[mask]" ::[mask] "r"(cfgmask) :); + + __asm__("csrs pmpcfg3, %[cfg]" ::[cfg] "r"(pmpcfg) :); + break; + } + } +#elif __riscv_xlen == 64 + if (CONFIG_TO_INT(old_config) != CONFIG_TO_INT(config)) { + /* Mask to clear old pmpcfg */ + cfgmask = (0xFF << (8 * (region % 8))); + pmpcfg = (CONFIG_TO_INT(config) << (8 * (region % 8))); + + switch (region / 8) { + case 0: + __asm__("csrc pmpcfg0, %[mask]" ::[mask] "r"(cfgmask) :); + + __asm__("csrs pmpcfg0, %[cfg]" ::[cfg] "r"(pmpcfg) :); + break; + case 1: + __asm__("csrc pmpcfg2, %[mask]" ::[mask] "r"(cfgmask) :); + + __asm__("csrs pmpcfg2, %[cfg]" ::[cfg] "r"(pmpcfg) :); + break; + } + } +#else +#error XLEN is not set to supported value for PMP driver +#endif + + return 0; +} + +int metal_pmp_get_region(struct metal_pmp *pmp, unsigned int region, + struct metal_pmp_config *config, size_t *address) { + size_t pmpcfg = 0; + char *pmpcfg_convert = (char *)&pmpcfg; + + if (!pmp || !config || !address) { + /* NULL pointers are invalid arguments */ + return 1; + } + + if (region > _pmp_regions()) { + /* Region outside of supported range */ + return 2; + } + +#if __riscv_xlen == 32 + switch (region / 4) { + case 0: + __asm__("csrr %[cfg], pmpcfg0" : [cfg] "=r"(pmpcfg)::); + break; + case 1: + __asm__("csrr %[cfg], pmpcfg1" : [cfg] "=r"(pmpcfg)::); + break; + case 2: + __asm__("csrr %[cfg], pmpcfg2" : [cfg] "=r"(pmpcfg)::); + break; + case 3: + __asm__("csrr %[cfg], pmpcfg3" : [cfg] "=r"(pmpcfg)::); + break; + } + + pmpcfg = (0xFF & (pmpcfg >> (8 * (region % 4)))); + +#elif __riscv_xlen == 64 + switch (region / 8) { + case 0: + __asm__("csrr %[cfg], pmpcfg0" : [cfg] "=r"(pmpcfg)::); + break; + case 1: + __asm__("csrr %[cfg], pmpcfg2" : [cfg] "=r"(pmpcfg)::); + break; + } + + pmpcfg = (0xFF & (pmpcfg >> (8 * (region % 8)))); + +#else +#error XLEN is not set to supported value for PMP driver +#endif + + *config = INT_TO_CONFIG(*pmpcfg_convert); + + switch (region) { + case 0: + __asm__("csrr %[addr], pmpaddr0" : [addr] "=r"(*address)::); + break; + case 1: + __asm__("csrr %[addr], pmpaddr1" : [addr] "=r"(*address)::); + break; + case 2: + __asm__("csrr %[addr], pmpaddr2" : [addr] "=r"(*address)::); + break; + case 3: + __asm__("csrr %[addr], pmpaddr3" : [addr] "=r"(*address)::); + break; + case 4: + __asm__("csrr %[addr], pmpaddr4" : [addr] "=r"(*address)::); + break; + case 5: + __asm__("csrr %[addr], pmpaddr5" : [addr] "=r"(*address)::); + break; + case 6: + __asm__("csrr %[addr], pmpaddr6" : [addr] "=r"(*address)::); + break; + case 7: + __asm__("csrr %[addr], pmpaddr7" : [addr] "=r"(*address)::); + break; + case 8: + __asm__("csrr %[addr], pmpaddr8" : [addr] "=r"(*address)::); + break; + case 9: + __asm__("csrr %[addr], pmpaddr9" : [addr] "=r"(*address)::); + break; + case 10: + __asm__("csrr %[addr], pmpaddr10" : [addr] "=r"(*address)::); + break; + case 11: + __asm__("csrr %[addr], pmpaddr11" : [addr] "=r"(*address)::); + break; + case 12: + __asm__("csrr %[addr], pmpaddr12" : [addr] "=r"(*address)::); + break; + case 13: + __asm__("csrr %[addr], pmpaddr13" : [addr] "=r"(*address)::); + break; + case 14: + __asm__("csrr %[addr], pmpaddr14" : [addr] "=r"(*address)::); + break; + case 15: + __asm__("csrr %[addr], pmpaddr15" : [addr] "=r"(*address)::); + break; + } + + return 0; +} + +int metal_pmp_lock(struct metal_pmp *pmp, unsigned int region) { + struct metal_pmp_config config; + size_t address; + int rc = 0; + + rc = metal_pmp_get_region(pmp, region, &config, &address); + if (rc) { + return rc; + } + + if (config.L == METAL_PMP_LOCKED) { + return 0; + } + + config.L = METAL_PMP_LOCKED; + + rc = metal_pmp_set_region(pmp, region, config, address); + + return rc; +} + +int metal_pmp_set_address(struct metal_pmp *pmp, unsigned int region, + size_t address) { + struct metal_pmp_config config; + size_t old_address; + int rc = 0; + + rc = metal_pmp_get_region(pmp, region, &config, &old_address); + if (rc) { + return rc; + } + + rc = metal_pmp_set_region(pmp, region, config, address); + + return rc; +} + +size_t metal_pmp_get_address(struct metal_pmp *pmp, unsigned int region) { + struct metal_pmp_config config; + size_t address = 0; + + metal_pmp_get_region(pmp, region, &config, &address); + + return address; +} + +int metal_pmp_set_address_mode(struct metal_pmp *pmp, unsigned int region, + enum metal_pmp_address_mode mode) { + struct metal_pmp_config config; + size_t address; + int rc = 0; + + rc = metal_pmp_get_region(pmp, region, &config, &address); + if (rc) { + return rc; + } + + config.A = mode; + + rc = metal_pmp_set_region(pmp, region, config, address); + + return rc; +} + +enum metal_pmp_address_mode metal_pmp_get_address_mode(struct metal_pmp *pmp, + unsigned int region) { + struct metal_pmp_config config; + size_t address = 0; + + metal_pmp_get_region(pmp, region, &config, &address); + + return config.A; +} + +int metal_pmp_set_executable(struct metal_pmp *pmp, unsigned int region, + int X) { + struct metal_pmp_config config; + size_t address; + int rc = 0; + + rc = metal_pmp_get_region(pmp, region, &config, &address); + if (rc) { + return rc; + } + + config.X = X; + + rc = metal_pmp_set_region(pmp, region, config, address); + + return rc; +} + +int metal_pmp_get_executable(struct metal_pmp *pmp, unsigned int region) { + struct metal_pmp_config config; + size_t address = 0; + + metal_pmp_get_region(pmp, region, &config, &address); + + return config.X; +} + +int metal_pmp_set_writeable(struct metal_pmp *pmp, unsigned int region, int W) { + struct metal_pmp_config config; + size_t address; + int rc = 0; + + rc = metal_pmp_get_region(pmp, region, &config, &address); + if (rc) { + return rc; + } + + config.W = W; + + rc = metal_pmp_set_region(pmp, region, config, address); + + return rc; +} + +int metal_pmp_get_writeable(struct metal_pmp *pmp, unsigned int region) { + struct metal_pmp_config config; + size_t address = 0; + + metal_pmp_get_region(pmp, region, &config, &address); + + return config.W; +} + +int metal_pmp_set_readable(struct metal_pmp *pmp, unsigned int region, int R) { + struct metal_pmp_config config; + size_t address; + int rc = 0; + + rc = metal_pmp_get_region(pmp, region, &config, &address); + if (rc) { + return rc; + } + + config.R = R; + + rc = metal_pmp_set_region(pmp, region, config, address); + + return rc; +} + +int metal_pmp_get_readable(struct metal_pmp *pmp, unsigned int region) { + struct metal_pmp_config config; + size_t address = 0; + + metal_pmp_get_region(pmp, region, &config, &address); + + return config.R; +} diff --git a/arch/riscv32/fe310/src/freedom-metal/src/prci.c b/arch/riscv32/fe310/src/freedom-metal/src/prci.c new file mode 100644 index 00000000..efdf8808 --- /dev/null +++ b/arch/riscv32/fe310/src/freedom-metal/src/prci.c @@ -0,0 +1,19 @@ +/* Copyright 2021 SiFive, Inc */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include +#include + +extern __inline__ unsigned long metal_prci_get_reg(struct metal_prci *prci, + unsigned long offset); +extern __inline__ unsigned long metal_prci_set_reg(struct metal_prci *prci, + unsigned long offset, + unsigned long value); + +struct metal_prci *metal_prci_get_device(void) { +#ifdef __METAL_DT_SIFIVE_PRCI0_HANDLE + return (struct metal_prci *)__METAL_DT_SIFIVE_PRCI0_HANDLE; +#else + return NULL; +#endif +} diff --git a/arch/riscv32/fe310/src/freedom-metal/src/privilege.c b/arch/riscv32/fe310/src/freedom-metal/src/privilege.c new file mode 100644 index 00000000..0919d77d --- /dev/null +++ b/arch/riscv32/fe310/src/freedom-metal/src/privilege.c @@ -0,0 +1,55 @@ +/* Copyright 2019 SiFive, Inc */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include + +#include + +#define METAL_MSTATUS_MIE_OFFSET 3 +#define METAL_MSTATUS_MPIE_OFFSET 7 +#define METAL_MSTATUS_SIE_OFFSET 1 +#define METAL_MSTATUS_SPIE_OFFSET 5 +#define METAL_MSTATUS_UIE_OFFSET 0 +#define METAL_MSTATUS_UPIE_OFFSET 4 + +#define METAL_MSTATUS_MPP_OFFSET 11 +#define METAL_MSTATUS_MPP_MASK 3 + +void metal_privilege_drop_to_mode(enum metal_privilege_mode mode, + struct metal_register_file regfile, + metal_privilege_entry_point_t entry_point) { + uintptr_t mstatus; + __asm__ volatile("csrr %0, mstatus" : "=r"(mstatus)); + + /* Set xPIE bits based on current xIE bits */ + if (mstatus & (1 << METAL_MSTATUS_MIE_OFFSET)) { + mstatus |= (1 << METAL_MSTATUS_MPIE_OFFSET); + } else { + mstatus &= ~(1 << METAL_MSTATUS_MPIE_OFFSET); + } + if (mstatus & (1 << METAL_MSTATUS_SIE_OFFSET)) { + mstatus |= (1 << METAL_MSTATUS_SPIE_OFFSET); + } else { + mstatus &= ~(1 << METAL_MSTATUS_SPIE_OFFSET); + } + if (mstatus & (1 << METAL_MSTATUS_UIE_OFFSET)) { + mstatus |= (1 << METAL_MSTATUS_UPIE_OFFSET); + } else { + mstatus &= ~(1 << METAL_MSTATUS_UPIE_OFFSET); + } + + /* Set MPP to the requested privilege mode */ + mstatus &= ~(METAL_MSTATUS_MPP_MASK << METAL_MSTATUS_MPP_OFFSET); + mstatus |= (mode << METAL_MSTATUS_MPP_OFFSET); + + __asm__ volatile("csrw mstatus, %0" ::"r"(mstatus)); + + /* Set the entry point in MEPC */ + __asm__ volatile("csrw mepc, %0" ::"r"(entry_point)); + + /* Set the register file */ + __asm__ volatile("mv ra, %0" ::"r"(regfile.ra)); + __asm__ volatile("mv sp, %0" ::"r"(regfile.sp)); + + __asm__ volatile("mret"); +} diff --git a/arch/riscv32/fe310/src/freedom-metal/src/pwm.c b/arch/riscv32/fe310/src/freedom-metal/src/pwm.c new file mode 100644 index 00000000..15e4e6ec --- /dev/null +++ b/arch/riscv32/fe310/src/freedom-metal/src/pwm.c @@ -0,0 +1,36 @@ +/* Copyright 2020 SiFive, Inc */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include +#include + +extern inline int metal_pwm_enable(struct metal_pwm *pwm); +extern inline int metal_pwm_disable(struct metal_pwm *pwm); +extern inline int metal_pwm_set_freq(struct metal_pwm *pwm, unsigned int idx, + unsigned int freq); +extern inline int metal_pwm_set_duty(struct metal_pwm *pwm, unsigned int idx, + unsigned int duty, + metal_pwm_phase_correct_t phase_corr); +extern inline unsigned int metal_pwm_get_duty(struct metal_pwm *pwm, + unsigned int idx); +extern inline unsigned int metal_pwm_get_freq(struct metal_pwm *pwm, + unsigned int idx); +extern inline int metal_pwm_trigger(struct metal_pwm *pwm, unsigned int idx, + metal_pwm_run_mode_t mode); +extern inline int metal_pwm_stop(struct metal_pwm *pwm, unsigned int idx); +extern inline int metal_pwm_cfg_interrupt(struct metal_pwm *pwm, + metal_pwm_interrupt_t flag); +extern inline int metal_pwm_clr_interrupt(struct metal_pwm *pwm, + unsigned int idx); +extern struct metal_interrupt * +metal_pwm_interrupt_controller(struct metal_pwm *pwm); +extern int metal_pwm_get_interrupt_id(struct metal_pwm *pwm, unsigned int idx); + +struct metal_pwm *metal_pwm_get_device(unsigned int device_num) { +#if __METAL_DT_MAX_PWMS > 0 + if (device_num < __METAL_DT_MAX_PWMS) { + return (struct metal_pwm *)__metal_pwm_table[device_num]; + } +#endif + return NULL; +} diff --git a/arch/riscv32/fe310/src/freedom-metal/src/remapper.c b/arch/riscv32/fe310/src/freedom-metal/src/remapper.c new file mode 100644 index 00000000..a86e8554 --- /dev/null +++ b/arch/riscv32/fe310/src/freedom-metal/src/remapper.c @@ -0,0 +1,130 @@ +/* Copyright 2021 SiFive, Inc */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include +#include + +extern int __metal_remapper_enable_remap(struct metal_remapper *remapper, + int idx); +extern int __metal_remapper_disable_remap(struct metal_remapper *remapper, + int idx); +extern int __metal_remapper_enable_remaps(struct metal_remapper *remapper, + int idxs[], int num_idxs); +extern int __metal_remapper_disable_remaps(struct metal_remapper *remapper, + int idxs[], int num_idxs); +extern uint32_t __metal_remapper_get_valid(struct metal_remapper *remapper, + int idx); +extern int __metal_remapper_set_valid(struct metal_remapper *remapper, int idx, + uint32_t val); +extern int __metal_remapper_flush(struct metal_remapper *remapper); +extern uint64_t +__metal_remapper_get_from_region_base(struct metal_remapper *remapper); +extern uint64_t +__metal_remapper_get_from_region_size(struct metal_remapper *remapper); +extern uint64_t +__metal_remapper_get_to_region_base(struct metal_remapper *remapper); +extern uint64_t +__metal_remapper_get_to_region_size(struct metal_remapper *remapper); +extern uint64_t __metal_remapper_get_max_from_entry_region_size( + struct metal_remapper *remapper); +extern uint32_t __metal_remapper_get_version(struct metal_remapper *remapper); +extern int __metal_remapper_set_version(struct metal_remapper *remapper, + uint32_t version); +extern uint32_t __metal_remapper_get_entries(struct metal_remapper *remapper); +extern int __metal_remapper_set_remap(struct metal_remapper *remapper, + struct metal_remapper_entry *entry); +extern int __metal_remapper_set_remaps(struct metal_remapper *remapper, + struct metal_remapper_entry *entries[], + int num_entries); +extern uint64_t __metal_remapper_get_from(struct metal_remapper *remapper, + int idx); +extern uint64_t __metal_remapper_get_to(struct metal_remapper *remapper, + int idx); + +struct metal_remapper *metal_remapper_get_device(void) { +#ifdef __METAL_DT_REMAPPER_HANDLE + return __METAL_DT_REMAPPER_HANDLE; +#else + return NULL; +#endif +} + +int metal_remapper_enable_remap(struct metal_remapper *remapper, int idx) { + + return __metal_remapper_enable_remap(remapper, idx); +} + +int metal_remapper_disable_remap(struct metal_remapper *remapper, int idx) { + return __metal_remapper_disable_remap(remapper, idx); +} + +int metal_remapper_enable_remaps(struct metal_remapper *remapper, int idxs[], + int num_idxs) { + return __metal_remapper_enable_remaps(remapper, idxs, num_idxs); +} + +int metal_remapper_disable_remaps(struct metal_remapper *remapper, int idxs[], + int num_idxs) { + return __metal_remapper_disable_remaps(remapper, idxs, num_idxs); +} + +uint32_t metal_remapper_get_valid(struct metal_remapper *remapper, int idx) { + return __metal_remapper_get_valid(remapper, idx); +} + +int metal_remapper_set_valid(struct metal_remapper *remapper, int idx, + uint32_t val) { + return __metal_remapper_set_valid(remapper, idx, val); +} + +int metal_remapper_flush(struct metal_remapper *remapper) { + return __metal_remapper_flush(remapper); +} + +uint64_t metal_remapper_get_from_region_base(struct metal_remapper *remapper) { + return __metal_remapper_get_from_region_base(remapper); +} + +uint64_t metal_remapper_get_from_region_size(struct metal_remapper *remapper) { + return __metal_remapper_get_from_region_size(remapper); +} + +uint64_t metal_remapper_get_to_region_base(struct metal_remapper *remapper) { + return __metal_remapper_get_to_region_base(remapper); +} + +uint64_t metal_remapper_get_to_region_size(struct metal_remapper *remapper) { + return __metal_remapper_get_to_region_size(remapper); +} + +uint64_t +metal_remapper_get_max_from_entry_region_size(struct metal_remapper *remapper) { + return __metal_remapper_get_max_from_entry_region_size(remapper); +} + +uint32_t metal_remapper_get_version(struct metal_remapper *remapper) { + return __metal_remapper_get_version(remapper); +} + +uint32_t metal_remapper_get_entries(struct metal_remapper *remapper) { + return __metal_remapper_get_entries(remapper); +} + +int metal_remapper_set_remap(struct metal_remapper *remapper, + struct metal_remapper_entry *entry) { + return __metal_remapper_set_remap(remapper, entry); +} + +int metal_remapper_set_remaps(struct metal_remapper *remapper, + struct metal_remapper_entry *entries[], + int num_entries) { + return __metal_remapper_set_remaps(remapper, entries, num_entries); +} + +uint64_t metal_remapper_get_from(struct metal_remapper *remapper, int idx) { + return __metal_remapper_get_from(remapper, idx); +} + +uint64_t metal_remapper_get_to(struct metal_remapper *remapper, int idx) { + return __metal_remapper_get_to(remapper, idx); +} diff --git a/arch/riscv32/fe310/src/freedom-metal/src/rtc.c b/arch/riscv32/fe310/src/freedom-metal/src/rtc.c new file mode 100644 index 00000000..ff43ec32 --- /dev/null +++ b/arch/riscv32/fe310/src/freedom-metal/src/rtc.c @@ -0,0 +1,31 @@ +/* Copyright 2019 SiFive, Inc. */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include +#include + +#include + +extern inline uint64_t metal_rtc_get_rate(const struct metal_rtc *const rtc); +extern inline uint64_t metal_rtc_set_rate(const struct metal_rtc *const rtc, + const uint64_t rate); +extern inline uint64_t metal_rtc_get_compare(const struct metal_rtc *const rtc); +extern inline uint64_t metal_rtc_set_compare(const struct metal_rtc *const rtc, + const uint64_t compare); +extern inline uint64_t metal_rtc_get_count(const struct metal_rtc *const rtc); +extern inline uint64_t metal_rtc_set_count(const struct metal_rtc *const rtc, + const uint64_t count); +extern inline int metal_rtc_run(const struct metal_rtc *const rtc, + const enum metal_rtc_run_option option); +extern inline struct metal_interrupt * +metal_rtc_get_interrupt(const struct metal_rtc *const rtc); +extern inline int metal_rtc_get_interrupt_id(const struct metal_rtc *const rtc); + +struct metal_rtc *metal_rtc_get_device(int index) { +#ifdef __METAL_DT_MAX_RTCS + if (index < __METAL_DT_MAX_RTCS) { + return (struct metal_rtc *)__metal_rtc_table[index]; + } +#endif + return NULL; +} diff --git a/arch/riscv32/fe310/src/freedom-metal/src/scrub.S b/arch/riscv32/fe310/src/freedom-metal/src/scrub.S new file mode 100644 index 00000000..aa8518e4 --- /dev/null +++ b/arch/riscv32/fe310/src/freedom-metal/src/scrub.S @@ -0,0 +1,141 @@ +/* Copyright 2020 SiFive, Inc */ +/* SPDX-License-Identifier: Apache-2.0 */ + +/* + * Scrub memory with zeroes + */ + +/* Keep it in metal.init section with _enter */ +.section .text.metal.init.scrub +/* Disable linker relaxation */ +.option push +.option norelax + +/* Function to zero-scrub specified memory + * a0 : start address for zero-scrub + * a1 : size memory region size in bytes + */ +.global metal_mem_scrub +.type metal_mem_scrub, @function +metal_mem_scrub: + + /* Disable machine interrupts, + restore previous mstatus value at exit */ + li a3, 8 + csrrc t1, mstatus, a3 + +#if __riscv_xlen == 32 + addi t0, x0, 4 +1: + blt a1, t0, 2f + andi a2, a0, 3 + beqz a2, 3f +2: + sb x0, 0(a0) + addi a0, a0, 1 + addi a1, a1, -1 + bgtz a1, 1b + csrw mstatus, t1 + ret +3: + sw x0, 0(a0) + addi a0, a0, 4 + addi a1, a1, -4 + bgtz a1, 1b + csrw mstatus, t1 + ret +#else + addi t0, x0, 8 +1: + blt a1, t0, 2f + andi a2, a0, 7 + beqz a2, 3f +2: + sb x0, 0(a0) + addi a0, a0, 1 + addi a1, a1, -1 + bgtz a1, 1b + csrw mstatus, t1 + ret +3: + sd x0, 0(a0) + addi a0, a0, 8 + addi a1, a1, -8 + bgtz a1, 1b + csrw mstatus, t1 + ret +#endif + +.type __metal_memory_scrub, @function +__metal_memory_scrub: +/* Zero out specified memory regions */ +1: +#if __riscv_xlen == 32 + sw x0, 0(t1) + addi t1, t1, 4 + blt t1, t2, 1b +#else + sd x0, 0(t1) + addi t1, t1, 8 + blt t1, t2, 1b +#endif + ret + +/* + * Initialize memories to zero + * This must be called before setting up any stack(s) + */ +.weak __metal_eccscrub_bit +.weak __metal_before_start +.type __metal_before_start, @function +__metal_before_start: + /* Save caller ra */ + mv s0, ra + + la t0, __metal_eccscrub_bit + beqz t0, skip_scrub + + la t0, __metal_boot_hart + csrr a5, mhartid + + /* Disable machine interrupts to be safe */ + li a3, 8 + csrc mstatus, a3 + + /* Zero out per hart stack */ + mv t1, sp + la t2, __stack_size + add t2, t2, sp + beq t1, t2, 1f + jal __metal_memory_scrub +1: + bne a5, t0, skip_scrub + + /* Zero out data segment */ +#ifdef __riscv_cmodel_compact + lla.gprel t1, metal_segment_data_target_start + lla.gprel t2, metal_segment_data_target_end +#else + la t1, metal_segment_data_target_start + la t2, metal_segment_data_target_end +#endif + beq t1, t2, 1f + jal __metal_memory_scrub +1: + /* Zero out itim memory */ +#ifdef __riscv_cmodel_compact + la.got.gprel t1, metal_segment_itim_target_start + la.got.gprel t2, metal_segment_itim_target_end +#else + la t1, metal_segment_itim_target_start + la t2, metal_segment_itim_target_end +#endif + beq t1, t2, skip_scrub + jal __metal_memory_scrub + +skip_scrub: + /* Restore caller ra */ + mv ra, s0 + ret + +.option pop diff --git a/arch/riscv32/fe310/src/freedom-metal/src/shutdown.c b/arch/riscv32/fe310/src/freedom-metal/src/shutdown.c new file mode 100644 index 00000000..eadb0fb8 --- /dev/null +++ b/arch/riscv32/fe310/src/freedom-metal/src/shutdown.c @@ -0,0 +1,22 @@ +/* Copyright 2018 SiFive, Inc */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include +#include + +extern __inline__ void __metal_shutdown_exit(const struct __metal_shutdown *sd, + int code); + +#if defined(__METAL_DT_SHUTDOWN_HANDLE) +void metal_shutdown(int code) { + __metal_shutdown_exit(__METAL_DT_SHUTDOWN_HANDLE, code); +} +#else +#pragma message( \ + "There is no defined shutdown mechanism, metal_shutdown() will spin.") +void metal_shutdown(int code) { + while (1) { + __asm__ volatile("nop"); + } +} +#endif diff --git a/arch/riscv32/fe310/src/freedom-metal/src/spi.c b/arch/riscv32/fe310/src/freedom-metal/src/spi.c new file mode 100644 index 00000000..19fcb9f0 --- /dev/null +++ b/arch/riscv32/fe310/src/freedom-metal/src/spi.c @@ -0,0 +1,24 @@ +/* Copyright 2018 SiFive, Inc */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include +#include + +extern __inline__ void metal_spi_init(struct metal_spi *spi, int baud_rate); +extern __inline__ int metal_spi_transfer(struct metal_spi *spi, + struct metal_spi_config *config, + size_t len, char *tx_buf, + char *rx_buf); +extern __inline__ int metal_spi_get_baud_rate(struct metal_spi *spi); +extern __inline__ int metal_spi_set_baud_rate(struct metal_spi *spi, + int baud_rate); + +struct metal_spi *metal_spi_get_device(unsigned int device_num) { +#if __METAL_DT_MAX_SPIS > 0 + if (device_num < __METAL_DT_MAX_SPIS) { + return (struct metal_spi *)__metal_spi_table[device_num]; + } +#endif + + return NULL; +} diff --git a/arch/riscv32/fe310/src/freedom-metal/src/switch.c b/arch/riscv32/fe310/src/freedom-metal/src/switch.c new file mode 100644 index 00000000..afae1fb8 --- /dev/null +++ b/arch/riscv32/fe310/src/freedom-metal/src/switch.c @@ -0,0 +1,26 @@ +/* Copyright 2018 SiFive, Inc */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include +#include + +struct metal_switch *metal_switch_get(char *label) { + int i; + struct metal_switch *flip; + + if ((__METAL_DT_MAX_SWITCHES == 0) || (label == NULL)) { + return NULL; + } + + for (i = 0; i < __METAL_DT_MAX_SWITCHES; i++) { + flip = (struct metal_switch *)__metal_switch_table[i]; + if (flip->vtable->switch_exist(flip, label)) { + return flip; + } + } + return NULL; +} + +extern __inline__ struct metal_interrupt * +metal_switch_interrupt_controller(struct metal_switch *flip); +extern __inline__ int metal_switch_get_interrupt_id(struct metal_switch *flip); diff --git a/arch/riscv32/fe310/src/freedom-metal/src/synchronize_harts.c b/arch/riscv32/fe310/src/freedom-metal/src/synchronize_harts.c new file mode 100644 index 00000000..b1548f9b --- /dev/null +++ b/arch/riscv32/fe310/src/freedom-metal/src/synchronize_harts.c @@ -0,0 +1,66 @@ +/* Copyright 2019 SiFive, Inc */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include +#include +#include +#include + +#define METAL_REG(base, offset) (((unsigned long)(base) + (offset))) +#define METAL_REGW(base, offset) \ + (__METAL_ACCESS_ONCE((__metal_io_u32 *)METAL_REG((base), (offset)))) +#define METAL_MSIP(base, hart) (METAL_REGW((base), 4 * (hart))) + +/* + * _synchronize_harts() is called by crt0.S to cause harts > 0 to wait for + * hart 0 to finish copying the datat section, zeroing the BSS, and running + * the libc contstructors. + */ +__attribute__((section(".init"))) void __metal_synchronize_harts() { +#if __METAL_DT_MAX_HARTS > 1 + + int hart; + __asm__ volatile("csrr %0, mhartid" : "=r"(hart)::); + + uintptr_t msip_base = 0; + + /* Get the base address of the MSIP registers */ +#ifdef __METAL_DT_RISCV_CLINT0_HANDLE + msip_base = __metal_driver_sifive_clint0_control_base( + __METAL_DT_RISCV_CLINT0_HANDLE); + msip_base += METAL_RISCV_CLINT0_MSIP_BASE; +#elif __METAL_DT_RISCV_CLIC0_HANDLE + msip_base = + __metal_driver_sifive_clic0_control_base(__METAL_DT_RISCV_CLIC0_HANDLE); + msip_base += METAL_RISCV_CLIC0_MSIP_BASE; +#else +#pragma message(No handle for CLINT or CLIC found, \ + harts may be unsynchronized after init !) +#endif + + /* Disable machine interrupts as a precaution */ + __asm__ volatile("csrc mstatus, %0" ::"r"(METAL_MSTATUS_MIE)); + + if (hart == 0) { + /* Hart 0 waits for all harts to set their MSIP bit */ + for (int i = 1; i < __METAL_DT_MAX_HARTS; i++) { + while (METAL_MSIP(msip_base, i) == 0) + ; + } + + /* Hart 0 clears everyone's MSIP bit */ + for (int i = 1; i < __METAL_DT_MAX_HARTS; i++) { + METAL_MSIP(msip_base, i) = 0; + } + } else { + /* Other harts set their MSIP bit to indicate they're ready */ + METAL_MSIP(msip_base, hart) = 1; + __asm__ volatile("fence w,rw"); + + /* Wait for hart 0 to clear the MSIP bit */ + while (METAL_MSIP(msip_base, hart) == 1) + ; + } + +#endif /* __METAL_DT_MAX_HARTS > 1 */ +} diff --git a/arch/riscv32/fe310/src/freedom-metal/src/time.c b/arch/riscv32/fe310/src/freedom-metal/src/time.c new file mode 100644 index 00000000..a40a3ced --- /dev/null +++ b/arch/riscv32/fe310/src/freedom-metal/src/time.c @@ -0,0 +1,32 @@ +/* Copyright 2019 SiFive, Inc */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include +#include + +#include + +int metal_gettimeofday(struct timeval *tp, void *tzp) { + int rv; + unsigned long long mcc, timebase; + rv = metal_timer_get_cyclecount(0, &mcc); + if (rv != 0) { + return -1; + } + rv = metal_timer_get_timebase_frequency(0, &timebase); + if (rv != 0) { + return -1; + } + tp->tv_sec = mcc / timebase; + tp->tv_usec = mcc % timebase * 1000000 / timebase; + return 0; +} + +time_t metal_time(void) { + struct timeval now; + + if (metal_gettimeofday(&now, NULL) < 0) + now.tv_sec = (time_t)-1; + + return now.tv_sec; +} diff --git a/arch/riscv32/fe310/src/freedom-metal/src/timer.c b/arch/riscv32/fe310/src/freedom-metal/src/timer.c new file mode 100644 index 00000000..8e5859aa --- /dev/null +++ b/arch/riscv32/fe310/src/freedom-metal/src/timer.c @@ -0,0 +1,83 @@ +/* Copyright 2018 SiFive, Inc */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include +#include +#include +#ifndef __SEGGER_LIBC__ +#include +#include +#endif + +#if defined(__METAL_DT_MAX_HARTS) +/* This implementation serves as a small shim that interfaces with the first + * timer on a system. */ +int metal_timer_get_cyclecount(int hartid, unsigned long long *mcc) { + struct metal_cpu *cpu = metal_cpu_get(hartid); + + if (cpu) { + *mcc = metal_cpu_get_timer(cpu); + return 0; + } + return -1; +} + +int metal_timer_get_timebase_frequency(int hartid, + unsigned long long *timebase) { + struct metal_cpu *cpu = metal_cpu_get(hartid); + + if (cpu) { + *timebase = metal_cpu_get_timebase(cpu); + return 0; + } + return -1; +} + +int metal_timer_get_machine_time(int hartid) { + struct metal_cpu *cpu = metal_cpu_get(hartid); + + if (cpu) { + return metal_cpu_get_mtime(cpu); + } + return 0; +} + +int metal_timer_set_machine_time(int hartid, unsigned long long time) { + struct metal_cpu *cpu = metal_cpu_get(hartid); + + if (cpu) { + return metal_cpu_set_mtimecmp(cpu, time); + } + return -1; +} + +#else + +/* This implementation of gettimeofday doesn't actually do anything, it's just + * there to provide a shim and return 0 so we can ensure that everything can + * link to _gettimeofday. + */ +int nop_cyclecount(int id, unsigned long long *c) + __attribute__((section(".text.metal.nop.cyclecount"))); +int nop_cyclecount(int id, unsigned long long *c) { return -1; } +int nop_timebase(unsigned long long *t) + __attribute__((section(".text.metal.nop.timebase"))); +int nop_timebase(unsigned long long *t) { return -1; } +int nop_tick(int second) __attribute__((section(".text.metal.nop.tick"))); +int nop_tick(int second) { return -1; } +int metal_timer_get_cyclecount(int hartid, unsigned long long *c) + __attribute__((weak, alias("nop_cyclecount"))) { +#pragma message( \ + "There is no default timer device, metal_timer_get_cyclecount() will always return cyclecount -1.") +} +int metal_timer_get_timebase_frequency(unsigned long long *t) + __attribute__((weak, alias("nop_timebase"))) { +#pragma message( \ + "There is no default timer device, metal_timer_get_timebase_frequency() will always return timebase -1.") +} +int metal_timer_set_tick(int second) __attribute__((weak, alias("nop_tick"))) { +#pragma message( \ + "There is no default timer device, metal_timer_set_tick) will always return -1.") +} + +#endif diff --git a/arch/riscv32/fe310/src/freedom-metal/src/trap.S b/arch/riscv32/fe310/src/freedom-metal/src/trap.S new file mode 100644 index 00000000..7d12d640 --- /dev/null +++ b/arch/riscv32/fe310/src/freedom-metal/src/trap.S @@ -0,0 +1,68 @@ +/* Copyright 2019 SiFive, Inc */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#define METAL_MSTATUS_MIE_SHIFT 8 +#define METAL_MSTATUS_MPP_M 3 +#define METAL_MSTATUS_MPP_SHIFT 11 + +#define METAL_MTVEC_MODE_MASK 3 + +/* void _metal_trap(int ecode) + * + * Trigger a machine-mode trap with exception code ecode + */ +.global _metal_trap +.type _metal_trap, @function + +_metal_trap: + + /* Store the instruction which called _metal_trap in mepc */ + addi t0, ra, -1 + csrw mepc, t0 + + /* Set mcause to the desired exception code */ + csrw mcause, a0 + + /* Read mstatus */ + csrr t0, mstatus + + /* Set MIE=0 */ + li t1, -1 + xori t1, t1, METAL_MSTATUS_MIE_SHIFT + and t0, t0, t1 + + /* Set MPP=M */ + li t1, METAL_MSTATUS_MPP_M + slli t1, t1, METAL_MSTATUS_MPP_SHIFT + or t0, t0, t1 + + /* Write mstatus */ + csrw mstatus, t0 + + /* Read mtvec */ + csrr t0, mtvec + + /* + * Mask the mtvec MODE bits + * Exceptions always jump to mtvec.BASE regradless of the vectoring mode. + */ + andi t0, t0, METAL_MTVEC_MODE_MASK + + /* Jump to mtvec */ + jr t0 + + +/* + * For sanity's sake we set up an early trap vector that just does nothing. + * If you end up here then there's a bug in the early boot code somewhere. + */ +.section .text.metal.init.trapvec +.global early_trap_vector +.align 2 +early_trap_vector: + .cfi_startproc + csrr t0, mcause + csrr t1, mepc + csrr t2, mtval + j early_trap_vector + .cfi_endproc diff --git a/arch/riscv32/fe310/src/freedom-metal/src/tty.c b/arch/riscv32/fe310/src/freedom-metal/src/tty.c new file mode 100644 index 00000000..e5ffdb26 --- /dev/null +++ b/arch/riscv32/fe310/src/freedom-metal/src/tty.c @@ -0,0 +1,52 @@ +/* Copyright 2018 SiFive, Inc */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include +#include +#include +#include + +#if defined(__METAL_DT_STDOUT_UART_HANDLE) +/* This implementation serves as a small shim that interfaces with the first + * UART on a system. */ +int metal_tty_putc(int c) { + return metal_uart_putc(__METAL_DT_STDOUT_UART_HANDLE, c); +} + +int metal_tty_getc(int *c) { + do { + metal_uart_getc(__METAL_DT_STDOUT_UART_HANDLE, c); + /* -1 means no key pressed, getc waits */ + } while (-1 == *c); + return 0; +} + +#ifndef __METAL_DT_STDOUT_UART_BAUD +#define __METAL_DT_STDOUT_UART_BAUD 115200 +#endif + +METAL_CONSTRUCTOR(metal_tty_init) { + metal_uart_init(__METAL_DT_STDOUT_UART_HANDLE, __METAL_DT_STDOUT_UART_BAUD); +} +#else +/* This implementation of putc doesn't actually do anything, it's just there to + * provide a shim that eats all the characters so we can ensure that everything + * can link to metal_tty_putc. */ +int nop_putc(int c) __attribute__((section(".text.metal.nop.putc"))); +// Use a customizable NOP hint instruction so that a post-processor parser can +// look for this instruction, and use the value in a0 as the character to be +// printed. +int nop_putc(int c) { + // The ABI states that c will be passed in a0. However, under an LTO + // (link-time-optimizer), it may choose to optimize in ways that would + // break this assumption. We want to ensure that the passed argument is + // truly in a0, for easier post-processing, and so there is a single + // 32-bit opcode to match against. + // So explicitly ensure that the argument is placed into a0 first. + __asm__ volatile("mv a0, %0; slli x0,a0,0x11" ::"r"(c)); + return -1; +} +int metal_tty_putc(int c) __attribute__((weak, alias("nop_putc"))); +#pragma message( \ + "There is no default output device, metal_tty_putc() will throw away all input.") +#endif diff --git a/arch/riscv32/fe310/src/freedom-metal/src/uart.c b/arch/riscv32/fe310/src/freedom-metal/src/uart.c new file mode 100644 index 00000000..753e5b43 --- /dev/null +++ b/arch/riscv32/fe310/src/freedom-metal/src/uart.c @@ -0,0 +1,42 @@ +/* Copyright 2018 SiFive, Inc */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include +#include + +extern __inline__ void metal_uart_init(struct metal_uart *uart, int baud_rate); +extern __inline__ int metal_uart_putc(struct metal_uart *uart, int c); +extern __inline__ int metal_uart_txready(struct metal_uart *uart); +extern __inline__ int metal_uart_getc(struct metal_uart *uart, int *c); +extern __inline__ int metal_uart_get_baud_rate(struct metal_uart *uart); +extern __inline__ int metal_uart_set_baud_rate(struct metal_uart *uart, + int baud_rate); +extern __inline__ struct metal_interrupt * +metal_uart_interrupt_controller(struct metal_uart *uart); +extern __inline__ int metal_uart_get_interrupt_id(struct metal_uart *uart); +extern __inline__ int +metal_uart_transmit_interrupt_enable(struct metal_uart *uart); +extern __inline__ int +metal_uart_transmit_interrupt_disable(struct metal_uart *uart); +extern __inline__ int +metal_uart_receive_interrupt_enable(struct metal_uart *uart); +extern __inline__ int +metal_uart_receive_interrupt_disable(struct metal_uart *uart); +extern __inline__ int metal_uart_set_transmit_watermark(struct metal_uart *uart, + size_t level); +extern __inline__ size_t +metal_uart_get_transmit_watermark(struct metal_uart *uart); +extern __inline__ int metal_uart_set_receive_watermark(struct metal_uart *uart, + size_t level); +extern __inline__ size_t +metal_uart_get_receive_watermark(struct metal_uart *uart); + +struct metal_uart *metal_uart_get_device(unsigned int device_num) { +#if __METAL_DT_MAX_UARTS > 0 + if (device_num < __METAL_DT_MAX_UARTS) { + return (struct metal_uart *)__metal_uart_table[device_num]; + } +#endif + + return NULL; +} diff --git a/arch/riscv32/fe310/src/freedom-metal/src/vector.S b/arch/riscv32/fe310/src/freedom-metal/src/vector.S new file mode 100644 index 00000000..277ae9fe --- /dev/null +++ b/arch/riscv32/fe310/src/freedom-metal/src/vector.S @@ -0,0 +1,140 @@ +/* Copyright 2019 SiFive, Inc */ +/* SPDX-License-Identifier: Apache-2.0 */ + +/* + * Jump table for CLINT vectored mode + */ +.balign 4, 0 +.weak metal_interrupt_vector_handler + +.balign 4, 0 +.weak metal_software_interrupt_vector_handler + +.balign 4, 0 +.weak metal_timer_interrupt_vector_handler + +.balign 4, 0 +.weak metal_external_interrupt_vector_handler + +.balign 4, 0 +.weak metal_lc0_interrupt_vector_handler + +.balign 4, 0 +.weak metal_lc1_interrupt_vector_handler + +.balign 4, 0 +.weak metal_lc2_interrupt_vector_handler + +.balign 4, 0 +.weak metal_lc3_interrupt_vector_handler + +.balign 4, 0 +.weak metal_lc4_interrupt_vector_handler + +.balign 4, 0 +.weak metal_lc5_interrupt_vector_handler + +.balign 4, 0 +.weak metal_lc6_interrupt_vector_handler + +.balign 4, 0 +.weak metal_lc7_interrupt_vector_handler + +.balign 4, 0 +.weak metal_lc8_interrupt_vector_handler + +.balign 4, 0 +.weak metal_lc9_interrupt_vector_handler + +.balign 4, 0 +.weak metal_lc10_interrupt_vector_handler + +.balign 4, 0 +.weak metal_lc11_interrupt_vector_handler + +.balign 4, 0 +.weak metal_lc12_interrupt_vector_handler + +.balign 4, 0 +.weak metal_lc13_interrupt_vector_handler + +.balign 4, 0 +.weak metal_lc14_interrupt_vector_handler + +.balign 4, 0 +.weak metal_lc15_interrupt_vector_handler + +#if __riscv_xlen == 32 +.balign 128, 0 +#else +.balign 256, 0 +#endif +.option norvc +.global __metal_vector_table +__metal_vector_table: +IRQ_0: + j metal_interrupt_vector_handler +IRQ_1: + j metal_interrupt_vector_handler +IRQ_2: + j metal_interrupt_vector_handler +IRQ_3: + j metal_software_interrupt_vector_handler +IRQ_4: + j metal_interrupt_vector_handler +IRQ_5: + j metal_interrupt_vector_handler +IRQ_6: + j metal_interrupt_vector_handler +IRQ_7: + j metal_timer_interrupt_vector_handler +IRQ_8: + j metal_interrupt_vector_handler +IRQ_9: + j metal_interrupt_vector_handler +IRQ_10: + j metal_interrupt_vector_handler +IRQ_11: + j metal_interrupt_vector_handler +IRQ_12: + j metal_interrupt_vector_handler +IRQ_13: + j metal_interrupt_vector_handler +IRQ_14: + j metal_interrupt_vector_handler +IRQ_15: + j metal_interrupt_vector_handler +IRQ_LC0: + j metal_lc0_interrupt_vector_handler +IRQ_LC1: + j metal_lc1_interrupt_vector_handler +IRQ_LC2: + j metal_lc2_interrupt_vector_handler +IRQ_LC3: + j metal_lc3_interrupt_vector_handler +IRQ_LC4: + j metal_lc4_interrupt_vector_handler +IRQ_LC5: + j metal_lc5_interrupt_vector_handler +IRQ_LC6: + j metal_lc6_interrupt_vector_handler +IRQ_LC7: + j metal_lc7_interrupt_vector_handler +IRQ_LC8: + j metal_lc8_interrupt_vector_handler +IRQ_LC9: + j metal_lc9_interrupt_vector_handler +IRQ_LC10: + j metal_lc10_interrupt_vector_handler +IRQ_LC11: + j metal_lc11_interrupt_vector_handler +IRQ_LC12: + j metal_lc12_interrupt_vector_handler +IRQ_LC13: + j metal_lc13_interrupt_vector_handler +IRQ_LC14: + j metal_lc14_interrupt_vector_handler +IRQ_LC15: + j metal_lc15_interrupt_vector_handler + + diff --git a/arch/riscv32/fe310/src/freedom-metal/src/watchdog.c b/arch/riscv32/fe310/src/freedom-metal/src/watchdog.c new file mode 100644 index 00000000..5e9bc496 --- /dev/null +++ b/arch/riscv32/fe310/src/freedom-metal/src/watchdog.c @@ -0,0 +1,37 @@ +/* Copyright 2019 SiFive, Inc */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include +#include + +extern inline int metal_watchdog_feed(const struct metal_watchdog *const wdog); +extern inline long int +metal_watchdog_get_rate(const struct metal_watchdog *const wdog); +extern inline long int +metal_watchdog_set_rate(const struct metal_watchdog *const wdog, + const long int rate); +extern inline long int +metal_watchdog_get_timeout(const struct metal_watchdog *const wdog); +extern inline long int +metal_watchdog_set_timeout(const struct metal_watchdog *const wdog, + const long int timeout); +extern inline int +metal_watchdog_set_result(const struct metal_watchdog *const wdog, + const enum metal_watchdog_result result); +extern inline int +metal_watchdog_run(const struct metal_watchdog *const wdog, + const enum metal_watchdog_run_option option); +extern inline struct metal_interrupt * +metal_watchdog_get_interrupt(const struct metal_watchdog *const wdog); +extern inline int +metal_watchdog_get_interrupt_id(const struct metal_watchdog *const wdog); +extern inline int +metal_watchdog_clear_interrupt(const struct metal_watchdog *const wdog); + +struct metal_watchdog *metal_watchdog_get_device(const int index) { + if (index > __METAL_DT_MAX_WDOGS) { + return NULL; + } + + return (struct metal_watchdog *)__metal_wdog_table[index]; +} diff --git a/arch/riscv32/fe310/src/metal/machine.h b/arch/riscv32/fe310/src/metal/machine.h new file mode 100644 index 00000000..8dc8300e --- /dev/null +++ b/arch/riscv32/fe310/src/metal/machine.h @@ -0,0 +1,1487 @@ +/* Copyright 2019 SiFive, Inc */ +/* SPDX-License-Identifier: Apache-2.0 */ +/* ----------------------------------- */ +/* ----------------------------------- */ + +#ifndef ASSEMBLY + +#include + +#ifdef __METAL_MACHINE_MACROS + +#ifndef MACROS_IF_METAL_H +#define MACROS_IF_METAL_H + +#define __METAL_CLINT_NUM_PARENTS 2 + +#ifndef __METAL_CLINT_NUM_PARENTS +#define __METAL_CLINT_NUM_PARENTS 0 +#endif +#define __METAL_PLIC_SUBINTERRUPTS 53 + +#define __METAL_PLIC_NUM_PARENTS 1 + +#ifndef __METAL_PLIC_SUBINTERRUPTS +#define __METAL_PLIC_SUBINTERRUPTS 0 +#endif +#ifndef __METAL_PLIC_NUM_PARENTS +#define __METAL_PLIC_NUM_PARENTS 0 +#endif +#ifndef __METAL_CLIC_SUBINTERRUPTS +#define __METAL_CLIC_SUBINTERRUPTS 0 +#endif + +#endif /* MACROS_IF_METAL_H*/ + +#else /* ! __METAL_MACHINE_MACROS */ + +#ifndef MACROS_ELSE_METAL_H +#define MACROS_ELSE_METAL_H + +#define __METAL_CLINT_2000000_INTERRUPTS 2 + +#define METAL_MAX_CLINT_INTERRUPTS 2 + +#define __METAL_CLINT_NUM_PARENTS 2 + +#define __METAL_INTERRUPT_CONTROLLER_C000000_INTERRUPTS 1 + +#define __METAL_PLIC_SUBINTERRUPTS 53 + +#define METAL_MAX_PLIC_INTERRUPTS 1 + +#define __METAL_PLIC_NUM_PARENTS 1 + +#define __METAL_CLIC_SUBINTERRUPTS 0 +#define METAL_MAX_CLIC_INTERRUPTS 0 + +#define METAL_MAX_LOCAL_EXT_INTERRUPTS 0 + +#define METAL_MAX_GLOBAL_EXT_INTERRUPTS 0 + +#define __METAL_GPIO_10012000_INTERRUPTS 32 + +#define METAL_MAX_GPIO_INTERRUPTS 32 + +#define __METAL_I2C_10016000_INTERRUPTS 1 + +#define METAL_MAX_I2C0_INTERRUPTS 1 + +#define __METAL_PWM_10015000_INTERRUPTS 4 + +#define __METAL_PWM_10025000_INTERRUPTS 4 + +#define __METAL_PWM_10035000_INTERRUPTS 4 + +#define METAL_MAX_PWM0_INTERRUPTS 4 + +#define METAL_MAX_PWM0_NCMP 4 + +#define __METAL_SERIAL_10013000_INTERRUPTS 1 + +#define __METAL_SERIAL_10023000_INTERRUPTS 1 + +#define METAL_MAX_UART_INTERRUPTS 1 + +#define METAL_MAX_SIMUART_INTERRUPTS 0 + + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* From clock@0 */ +extern struct __metal_driver_fixed_clock __metal_dt_clock_0; + +/* From clock@2 */ +extern struct __metal_driver_fixed_clock __metal_dt_clock_2; + +/* From clock@5 */ +extern struct __metal_driver_fixed_clock __metal_dt_clock_5; + +/* From clock@6 */ +extern struct __metal_driver_fixed_clock __metal_dt_clock_6; + +extern struct metal_memory __metal_dt_mem_dtim_80000000; + +extern struct metal_memory __metal_dt_mem_itim_8000000; + +extern struct metal_memory __metal_dt_mem_spi_10014000; + +extern struct metal_memory __metal_dt_mem_spi_10024000; + +extern struct metal_memory __metal_dt_mem_spi_10034000; + +/* From clint@2000000 */ +extern struct __metal_driver_riscv_clint0 __metal_dt_clint_2000000; + +/* From cpu@0 */ +extern struct __metal_driver_cpu __metal_dt_cpu_0; + +extern struct __metal_driver_riscv_cpu_intc __metal_dt_cpu_0_interrupt_controller; + +/* From interrupt_controller@c000000 */ +extern struct __metal_driver_riscv_plic0 __metal_dt_interrupt_controller_c000000; + +extern struct metal_pmp __metal_dt_pmp; + +/* From gpio@10012000 */ +extern struct __metal_driver_sifive_gpio0 __metal_dt_gpio_10012000; + +/* From led@0 */ +extern struct __metal_driver_sifive_gpio_led __metal_dt_led_0; + +/* From led@1 */ +extern struct __metal_driver_sifive_gpio_led __metal_dt_led_1; + +/* From led@2 */ +extern struct __metal_driver_sifive_gpio_led __metal_dt_led_2; + +/* From i2c@10016000 */ +extern struct __metal_driver_sifive_i2c0 __metal_dt_i2c_10016000; + +/* From pwm@10015000 */ +extern struct __metal_driver_sifive_pwm0 __metal_dt_pwm_10015000; + +/* From pwm@10025000 */ +extern struct __metal_driver_sifive_pwm0 __metal_dt_pwm_10025000; + +/* From pwm@10035000 */ +extern struct __metal_driver_sifive_pwm0 __metal_dt_pwm_10035000; + +/* From aon@10000000 */ +extern struct __metal_driver_sifive_rtc0 __metal_dt_rtc_10000000; + +/* From spi@10014000 */ +extern struct __metal_driver_sifive_spi0 __metal_dt_spi_10014000; + +/* From spi@10024000 */ +extern struct __metal_driver_sifive_spi0 __metal_dt_spi_10024000; + +/* From spi@10034000 */ +extern struct __metal_driver_sifive_spi0 __metal_dt_spi_10034000; + +/* From serial@10013000 */ +extern struct __metal_driver_sifive_uart0 __metal_dt_serial_10013000; + +/* From serial@10023000 */ +extern struct __metal_driver_sifive_uart0 __metal_dt_serial_10023000; + +/* From aon@10000000 */ +extern struct __metal_driver_sifive_wdog0 __metal_dt_aon_10000000; + +/* From clock@3 */ +extern struct __metal_driver_sifive_fe310_g000_hfrosc __metal_dt_clock_3; + +/* From clock@1 */ +extern struct __metal_driver_sifive_fe310_g000_hfxosc __metal_dt_clock_1; + +/* From clock@7 */ +extern struct __metal_driver_sifive_fe310_g000_lfrosc __metal_dt_clock_7; + +/* From clock@4 */ +extern struct __metal_driver_sifive_fe310_g000_pll __metal_dt_clock_4; + +/* From prci@10008000 */ +extern struct __metal_driver_sifive_fe310_g000_prci __metal_dt_prci_10008000; + + + +/* --------------------- fixed_clock ------------ */ +static __inline__ unsigned long __metal_driver_fixed_clock_rate(const struct metal_clock *clock) +{ + if ((uintptr_t)clock == (uintptr_t)&__metal_dt_clock_0) { + return METAL_FIXED_CLOCK_0_CLOCK_0_CLOCK_FREQUENCY; + } + else if ((uintptr_t)clock == (uintptr_t)&__metal_dt_clock_2) { + return METAL_FIXED_CLOCK_2_CLOCK_2_CLOCK_FREQUENCY; + } + else if ((uintptr_t)clock == (uintptr_t)&__metal_dt_clock_5) { + return METAL_FIXED_CLOCK_5_CLOCK_5_CLOCK_FREQUENCY; + } + else if ((uintptr_t)clock == (uintptr_t)&__metal_dt_clock_6) { + return METAL_FIXED_CLOCK_6_CLOCK_6_CLOCK_FREQUENCY; + } + else { + return 0; + } +} + + + +/* --------------------- fixed_factor_clock ------------ */ + + +/* --------------------- sifive_clint0 ------------ */ +static __inline__ unsigned long __metal_driver_sifive_clint0_control_base(struct metal_interrupt *controller) +{ + if ((uintptr_t)controller == (uintptr_t)&__metal_dt_clint_2000000) { + return METAL_RISCV_CLINT0_2000000_BASE_ADDRESS; + } + else { + return 0; + } +} + +static __inline__ unsigned long __metal_driver_sifive_clint0_control_size(struct metal_interrupt *controller) +{ + if ((uintptr_t)controller == (uintptr_t)&__metal_dt_clint_2000000) { + return METAL_RISCV_CLINT0_2000000_SIZE; + } + else { + return 0; + } +} + +static __inline__ int __metal_driver_sifive_clint0_num_interrupts(struct metal_interrupt *controller) +{ + if ((uintptr_t)controller == (uintptr_t)&__metal_dt_clint_2000000) { + return METAL_MAX_CLINT_INTERRUPTS; + } + else { + return 0; + } +} + +static __inline__ struct metal_interrupt * __metal_driver_sifive_clint0_interrupt_parents(struct metal_interrupt *controller, int idx) +{ + if (idx == 0) { + return (struct metal_interrupt *)&__metal_dt_cpu_0_interrupt_controller.controller; + } + else if (idx == 1) { + return (struct metal_interrupt *)&__metal_dt_cpu_0_interrupt_controller.controller; + } + else { + return NULL; + } +} + +static __inline__ int __metal_driver_sifive_clint0_interrupt_lines(struct metal_interrupt *controller, int idx) +{ + if (idx == 0) { + return 3; + } + else if (idx == 1) { + return 7; + } + else { + return 0; + } +} + + + +/* --------------------- cpu ------------ */ +static __inline__ int __metal_driver_cpu_hartid(struct metal_cpu *cpu) +{ + if ((uintptr_t)cpu == (uintptr_t)&__metal_dt_cpu_0) { + return 0; + } + else { + return -1; + } +} + +static __inline__ int __metal_driver_cpu_timebase(struct metal_cpu *cpu) +{ + if ((uintptr_t)cpu == (uintptr_t)&__metal_dt_cpu_0) { + return 16000000; + } + else { + return 0; + } +} + +static __inline__ struct metal_interrupt * __metal_driver_cpu_interrupt_controller(struct metal_cpu *cpu) +{ + if ((uintptr_t)cpu == (uintptr_t)&__metal_dt_cpu_0) { + return &__metal_dt_cpu_0_interrupt_controller.controller; + } + else { + return NULL; + } +} + +static __inline__ int __metal_driver_cpu_num_pmp_regions(struct metal_cpu *cpu) +{ + if ((uintptr_t)cpu == (uintptr_t)&__metal_dt_cpu_0) { + return 8; + } + else { + return 0; + } +} + +static __inline__ struct metal_buserror * __metal_driver_cpu_buserror(struct metal_cpu *cpu) +{ + if ((uintptr_t)cpu == (uintptr_t)&__metal_dt_cpu_0) { + return NULL; + } + else { + return NULL; + } +} + + + +/* --------------------- sifive_plic0 ------------ */ +static __inline__ unsigned long __metal_driver_sifive_plic0_control_base(struct metal_interrupt *controller) +{ + if ((uintptr_t)controller == (uintptr_t)&__metal_dt_interrupt_controller_c000000) { + return METAL_RISCV_PLIC0_C000000_BASE_ADDRESS; + } + else { + return 0; + } +} + +static __inline__ unsigned long __metal_driver_sifive_plic0_control_size(struct metal_interrupt *controller) +{ + if ((uintptr_t)controller == (uintptr_t)&__metal_dt_interrupt_controller_c000000) { + return METAL_RISCV_PLIC0_C000000_SIZE; + } + else { + return 0; + } +} + +static __inline__ int __metal_driver_sifive_plic0_num_interrupts(struct metal_interrupt *controller) +{ + if ((uintptr_t)controller == (uintptr_t)&__metal_dt_interrupt_controller_c000000) { + return METAL_RISCV_PLIC0_C000000_RISCV_NDEV; + } + else { + return 0; + } +} + +static __inline__ int __metal_driver_sifive_plic0_max_priority(struct metal_interrupt *controller) +{ + if ((uintptr_t)controller == (uintptr_t)&__metal_dt_interrupt_controller_c000000) { + return METAL_RISCV_PLIC0_C000000_RISCV_MAX_PRIORITY; + } + else { + return 0; + } +} + +static __inline__ struct metal_interrupt * __metal_driver_sifive_plic0_interrupt_parents(struct metal_interrupt *controller, int idx) +{ + if (idx == 0) { + return (struct metal_interrupt *)&__metal_dt_cpu_0_interrupt_controller.controller; + } + else { + return NULL; + } +} + +static __inline__ int __metal_driver_sifive_plic0_interrupt_lines(struct metal_interrupt *controller, int idx) +{ + if (idx == 0) { + return 11; + } + else { + return 0; + } +} + +static __inline__ int __metal_driver_sifive_plic0_context_ids(int hartid) +{ + if (hartid == 0) { + return 0; + } + else { + return -1; + } +} + + + +/* --------------------- sifive_buserror0 ------------ */ + + +/* --------------------- sifive_clic0 ------------ */ + + +/* --------------------- sifive_local_external_interrupts0 ------------ */ + + +/* --------------------- sifive_global_external_interrupts0 ------------ */ + + +/* --------------------- sifive_gpio0 ------------ */ +static __inline__ unsigned long __metal_driver_sifive_gpio0_base(struct metal_gpio *gpio) +{ + if ((uintptr_t)gpio == (uintptr_t)&__metal_dt_gpio_10012000) { + return METAL_SIFIVE_GPIO0_10012000_BASE_ADDRESS; + } + else { + return 0; + } +} + +static __inline__ unsigned long __metal_driver_sifive_gpio0_size(struct metal_gpio *gpio) +{ + if ((uintptr_t)gpio == (uintptr_t)&__metal_dt_gpio_10012000) { + return METAL_SIFIVE_GPIO0_10012000_SIZE; + } + else { + return 0; + } +} + +static __inline__ int __metal_driver_sifive_gpio0_num_interrupts(struct metal_gpio *gpio) +{ + if ((uintptr_t)gpio == (uintptr_t)&__metal_dt_gpio_10012000) { + return METAL_MAX_GPIO_INTERRUPTS; + } + else { + return 0; + } +} + +static __inline__ struct metal_interrupt * __metal_driver_sifive_gpio0_interrupt_parent(struct metal_gpio *gpio) +{ + if ((uintptr_t)gpio == (uintptr_t)&__metal_dt_gpio_10012000) { + return (struct metal_interrupt *)&__metal_dt_interrupt_controller_c000000.controller; + } + else { + return 0; + } +} + +static __inline__ int __metal_driver_sifive_gpio0_interrupt_lines(struct metal_gpio *gpio, int idx) +{ + if (((uintptr_t)gpio == (uintptr_t)&__metal_dt_gpio_10012000) && (idx == 0)) { + return 8; + } + else if ((((uintptr_t)gpio == (uintptr_t)&__metal_dt_gpio_10012000) && (idx == 1))) { + return 9; + } + else if ((((uintptr_t)gpio == (uintptr_t)&__metal_dt_gpio_10012000) && (idx == 2))) { + return 10; + } + else if ((((uintptr_t)gpio == (uintptr_t)&__metal_dt_gpio_10012000) && (idx == 3))) { + return 11; + } + else if ((((uintptr_t)gpio == (uintptr_t)&__metal_dt_gpio_10012000) && (idx == 4))) { + return 12; + } + else if ((((uintptr_t)gpio == (uintptr_t)&__metal_dt_gpio_10012000) && (idx == 5))) { + return 13; + } + else if ((((uintptr_t)gpio == (uintptr_t)&__metal_dt_gpio_10012000) && (idx == 6))) { + return 14; + } + else if ((((uintptr_t)gpio == (uintptr_t)&__metal_dt_gpio_10012000) && (idx == 7))) { + return 15; + } + else if ((((uintptr_t)gpio == (uintptr_t)&__metal_dt_gpio_10012000) && (idx == 8))) { + return 16; + } + else if ((((uintptr_t)gpio == (uintptr_t)&__metal_dt_gpio_10012000) && (idx == 9))) { + return 17; + } + else if ((((uintptr_t)gpio == (uintptr_t)&__metal_dt_gpio_10012000) && (idx == 10))) { + return 18; + } + else if ((((uintptr_t)gpio == (uintptr_t)&__metal_dt_gpio_10012000) && (idx == 11))) { + return 19; + } + else if ((((uintptr_t)gpio == (uintptr_t)&__metal_dt_gpio_10012000) && (idx == 12))) { + return 20; + } + else if ((((uintptr_t)gpio == (uintptr_t)&__metal_dt_gpio_10012000) && (idx == 13))) { + return 21; + } + else if ((((uintptr_t)gpio == (uintptr_t)&__metal_dt_gpio_10012000) && (idx == 14))) { + return 22; + } + else if ((((uintptr_t)gpio == (uintptr_t)&__metal_dt_gpio_10012000) && (idx == 15))) { + return 23; + } + else if ((((uintptr_t)gpio == (uintptr_t)&__metal_dt_gpio_10012000) && (idx == 16))) { + return 24; + } + else if ((((uintptr_t)gpio == (uintptr_t)&__metal_dt_gpio_10012000) && (idx == 17))) { + return 25; + } + else if ((((uintptr_t)gpio == (uintptr_t)&__metal_dt_gpio_10012000) && (idx == 18))) { + return 26; + } + else if ((((uintptr_t)gpio == (uintptr_t)&__metal_dt_gpio_10012000) && (idx == 19))) { + return 27; + } + else if ((((uintptr_t)gpio == (uintptr_t)&__metal_dt_gpio_10012000) && (idx == 20))) { + return 28; + } + else if ((((uintptr_t)gpio == (uintptr_t)&__metal_dt_gpio_10012000) && (idx == 21))) { + return 29; + } + else if ((((uintptr_t)gpio == (uintptr_t)&__metal_dt_gpio_10012000) && (idx == 22))) { + return 30; + } + else if ((((uintptr_t)gpio == (uintptr_t)&__metal_dt_gpio_10012000) && (idx == 23))) { + return 31; + } + else if ((((uintptr_t)gpio == (uintptr_t)&__metal_dt_gpio_10012000) && (idx == 24))) { + return 32; + } + else if ((((uintptr_t)gpio == (uintptr_t)&__metal_dt_gpio_10012000) && (idx == 25))) { + return 33; + } + else if ((((uintptr_t)gpio == (uintptr_t)&__metal_dt_gpio_10012000) && (idx == 26))) { + return 34; + } + else if ((((uintptr_t)gpio == (uintptr_t)&__metal_dt_gpio_10012000) && (idx == 27))) { + return 35; + } + else if ((((uintptr_t)gpio == (uintptr_t)&__metal_dt_gpio_10012000) && (idx == 28))) { + return 36; + } + else if ((((uintptr_t)gpio == (uintptr_t)&__metal_dt_gpio_10012000) && (idx == 29))) { + return 27; + } + else if ((((uintptr_t)gpio == (uintptr_t)&__metal_dt_gpio_10012000) && (idx == 30))) { + return 28; + } + else if ((((uintptr_t)gpio == (uintptr_t)&__metal_dt_gpio_10012000) && (idx == 31))) { + return 29; + } + else { + return 0; + } +} + + + +/* --------------------- sifive_gpio_button ------------ */ + + +/* --------------------- sifive_gpio_led ------------ */ +static __inline__ struct metal_gpio * __metal_driver_sifive_gpio_led_gpio(struct metal_led *led) +{ + if ((uintptr_t)led == (uintptr_t)&__metal_dt_led_0) { + return (struct metal_gpio *)&__metal_dt_gpio_10012000; + } + else if ((uintptr_t)led == (uintptr_t)&__metal_dt_led_1) { + return (struct metal_gpio *)&__metal_dt_gpio_10012000; + } + else if ((uintptr_t)led == (uintptr_t)&__metal_dt_led_2) { + return (struct metal_gpio *)&__metal_dt_gpio_10012000; + } + else { + return NULL; + } +} + +static __inline__ int __metal_driver_sifive_gpio_led_pin(struct metal_led *led) +{ + if ((uintptr_t)led == (uintptr_t)&__metal_dt_led_0) { + return 22; + } + else if ((uintptr_t)led == (uintptr_t)&__metal_dt_led_1) { + return 19; + } + else if ((uintptr_t)led == (uintptr_t)&__metal_dt_led_2) { + return 21; + } + else { + return 0; + } +} + +static __inline__ char * __metal_driver_sifive_gpio_led_label(struct metal_led *led) +{ + if ((uintptr_t)led == (uintptr_t)&__metal_dt_led_0) { + return "LD0red"; + } + else if ((uintptr_t)led == (uintptr_t)&__metal_dt_led_1) { + return "LD0green"; + } + else if ((uintptr_t)led == (uintptr_t)&__metal_dt_led_2) { + return "LD0blue"; + } + else { + return ""; + } +} + + + +/* --------------------- sifive_gpio_switch ------------ */ + + +/* --------------------- sifive_i2c0 ------------ */ +static __inline__ unsigned long __metal_driver_sifive_i2c0_control_base(struct metal_i2c *i2c) +{ + if ((uintptr_t)i2c == (uintptr_t)&__metal_dt_i2c_10016000) { + return METAL_SIFIVE_I2C0_10016000_BASE_ADDRESS; + } + else { + return 0; + } +} + +static __inline__ unsigned long __metal_driver_sifive_i2c0_control_size(struct metal_i2c *i2c) +{ + if ((uintptr_t)i2c == (uintptr_t)&__metal_dt_i2c_10016000) { + return METAL_SIFIVE_I2C0_10016000_SIZE; + } + else { + return 0; + } +} + +static __inline__ struct metal_clock * __metal_driver_sifive_i2c0_clock(struct metal_i2c *i2c) +{ + if ((uintptr_t)i2c == (uintptr_t)&__metal_dt_i2c_10016000) { + return (struct metal_clock *)&__metal_dt_clock_4.clock; + } + else { + return NULL; + } +} + +static __inline__ struct __metal_driver_sifive_gpio0 * __metal_driver_sifive_i2c0_pinmux(struct metal_i2c *i2c) +{ + if ((uintptr_t)i2c == (uintptr_t)&__metal_dt_i2c_10016000) { + return (struct __metal_driver_sifive_gpio0 *)&__metal_dt_gpio_10012000; + } + else { + return NULL; + } +} + +static __inline__ unsigned long __metal_driver_sifive_i2c0_pinmux_output_selector(struct metal_i2c *i2c) +{ + if ((uintptr_t)i2c == (uintptr_t)&__metal_dt_i2c_10016000) { + return 0; + } + else { + return 0; + } +} + +static __inline__ unsigned long __metal_driver_sifive_i2c0_pinmux_source_selector(struct metal_i2c *i2c) +{ + if ((uintptr_t)i2c == (uintptr_t)&__metal_dt_i2c_10016000) { + return 12288; + } + else { + return 0; + } +} + +static __inline__ int __metal_driver_sifive_i2c0_num_interrupts(struct metal_i2c *i2c) +{ + return METAL_MAX_I2C0_INTERRUPTS; +} + +static __inline__ struct metal_interrupt * __metal_driver_sifive_i2c0_interrupt_parent(struct metal_i2c *i2c) +{ + return (struct metal_interrupt *)&__metal_dt_interrupt_controller_c000000.controller; +} + +static __inline__ int __metal_driver_sifive_i2c0_interrupt_line(struct metal_i2c *i2c) +{ + if ((uintptr_t)i2c == (uintptr_t)&__metal_dt_i2c_10016000) { + return 52; + } + else { + return 0; + } +} + + + +/* --------------------- sifive_prci0 ------------ */ + + +/* --------------------- sifive_pwm0 ------------ */ +static __inline__ unsigned long __metal_driver_sifive_pwm0_control_base(struct metal_pwm *pwm) +{ + if ((uintptr_t)pwm == (uintptr_t)&__metal_dt_pwm_10015000) { + return METAL_SIFIVE_PWM0_10015000_BASE_ADDRESS; + } + else if ((uintptr_t)pwm == (uintptr_t)&__metal_dt_pwm_10025000) { + return METAL_SIFIVE_PWM0_10025000_BASE_ADDRESS; + } + else if ((uintptr_t)pwm == (uintptr_t)&__metal_dt_pwm_10035000) { + return METAL_SIFIVE_PWM0_10035000_BASE_ADDRESS; + } + else { + return 0; + } +} + +static __inline__ unsigned long __metal_driver_sifive_pwm0_control_size(struct metal_pwm *pwm) +{ + if ((uintptr_t)pwm == (uintptr_t)&__metal_dt_pwm_10015000) { + return METAL_SIFIVE_PWM0_10015000_SIZE; + } + else if ((uintptr_t)pwm == (uintptr_t)&__metal_dt_pwm_10025000) { + return METAL_SIFIVE_PWM0_10025000_SIZE; + } + else if ((uintptr_t)pwm == (uintptr_t)&__metal_dt_pwm_10035000) { + return METAL_SIFIVE_PWM0_10035000_SIZE; + } + else { + return 0; + } +} + +static __inline__ struct metal_clock * __metal_driver_sifive_pwm0_clock(struct metal_pwm *pwm) +{ + if ((uintptr_t)pwm == (uintptr_t)&__metal_dt_pwm_10015000) { + return (struct metal_clock *)&__metal_dt_clock_4.clock; + } + else if ((uintptr_t)pwm == (uintptr_t)&__metal_dt_pwm_10025000) { + return (struct metal_clock *)&__metal_dt_clock_4.clock; + } + else if ((uintptr_t)pwm == (uintptr_t)&__metal_dt_pwm_10035000) { + return (struct metal_clock *)&__metal_dt_clock_4.clock; + } + else { + return NULL; + } +} + +static __inline__ struct __metal_driver_sifive_gpio0 * __metal_driver_sifive_pwm0_pinmux(struct metal_pwm *pwm) +{ + if ((uintptr_t)pwm == (uintptr_t)&__metal_dt_pwm_10015000) { + return (struct __metal_driver_sifive_gpio0 *)&__metal_dt_gpio_10012000; + } + else if ((uintptr_t)pwm == (uintptr_t)&__metal_dt_pwm_10025000) { + return (struct __metal_driver_sifive_gpio0 *)&__metal_dt_gpio_10012000; + } + else if ((uintptr_t)pwm == (uintptr_t)&__metal_dt_pwm_10035000) { + return (struct __metal_driver_sifive_gpio0 *)&__metal_dt_gpio_10012000; + } + else { + return NULL; + } +} + +static __inline__ unsigned long __metal_driver_sifive_pwm0_pinmux_output_selector(struct metal_pwm *pwm) +{ + if ((uintptr_t)pwm == (uintptr_t)&__metal_dt_pwm_10015000) { + return 15; + } + else if ((uintptr_t)pwm == (uintptr_t)&__metal_dt_pwm_10025000) { + return 7864320; + } + else if ((uintptr_t)pwm == (uintptr_t)&__metal_dt_pwm_10035000) { + return 15360; + } + else { + return 0; + } +} + +static __inline__ unsigned long __metal_driver_sifive_pwm0_pinmux_source_selector(struct metal_pwm *pwm) +{ + if ((uintptr_t)pwm == (uintptr_t)&__metal_dt_pwm_10015000) { + return 15; + } + else if ((uintptr_t)pwm == (uintptr_t)&__metal_dt_pwm_10025000) { + return 7864320; + } + else if ((uintptr_t)pwm == (uintptr_t)&__metal_dt_pwm_10035000) { + return 15360; + } + else { + return 0; + } +} + +static __inline__ int __metal_driver_sifive_pwm0_num_interrupts(struct metal_pwm *pwm) +{ + if ((uintptr_t)pwm == (uintptr_t)&__metal_dt_pwm_10015000) { + return __METAL_PWM_10015000_INTERRUPTS; + } + else if ((uintptr_t)pwm == (uintptr_t)&__metal_dt_pwm_10025000) { + return __METAL_PWM_10025000_INTERRUPTS; + } + else if ((uintptr_t)pwm == (uintptr_t)&__metal_dt_pwm_10035000) { + return __METAL_PWM_10035000_INTERRUPTS; + } + else { + return 0; + } +} + +static __inline__ struct metal_interrupt * __metal_driver_sifive_pwm0_interrupt_parent(struct metal_pwm *pwm) +{ + return (struct metal_interrupt *)&__metal_dt_interrupt_controller_c000000.controller; +} + +static __inline__ int __metal_driver_sifive_pwm0_interrupt_lines(struct metal_pwm *pwm, int idx) +{ + if (((uintptr_t)pwm == (uintptr_t)&__metal_dt_pwm_10015000) && (idx == 0)) { + return 40; + } + else if ((((uintptr_t)pwm == (uintptr_t)&__metal_dt_pwm_10015000) && (idx == 1))) { + return 41; + } + else if ((((uintptr_t)pwm == (uintptr_t)&__metal_dt_pwm_10015000) && (idx == 2))) { + return 42; + } + else if ((((uintptr_t)pwm == (uintptr_t)&__metal_dt_pwm_10015000) && (idx == 3))) { + return 43; + } + else if ((((uintptr_t)pwm == (uintptr_t)&__metal_dt_pwm_10025000) && (idx == 0))) { + return 44; + } + else if ((((uintptr_t)pwm == (uintptr_t)&__metal_dt_pwm_10025000) && (idx == 1))) { + return 45; + } + else if ((((uintptr_t)pwm == (uintptr_t)&__metal_dt_pwm_10025000) && (idx == 2))) { + return 46; + } + else if ((((uintptr_t)pwm == (uintptr_t)&__metal_dt_pwm_10025000) && (idx == 3))) { + return 47; + } + else if ((((uintptr_t)pwm == (uintptr_t)&__metal_dt_pwm_10035000) && (idx == 0))) { + return 48; + } + else if ((((uintptr_t)pwm == (uintptr_t)&__metal_dt_pwm_10035000) && (idx == 1))) { + return 49; + } + else if ((((uintptr_t)pwm == (uintptr_t)&__metal_dt_pwm_10035000) && (idx == 2))) { + return 50; + } + else if ((((uintptr_t)pwm == (uintptr_t)&__metal_dt_pwm_10035000) && (idx == 3))) { + return 51; + } + else { + return 0; + } +} + +static __inline__ int __metal_driver_sifive_pwm0_compare_width(struct metal_pwm *pwm) +{ + if ((uintptr_t)pwm == (uintptr_t)&__metal_dt_pwm_10015000) { + return 8; + } + else if ((uintptr_t)pwm == (uintptr_t)&__metal_dt_pwm_10025000) { + return 16; + } + else if ((uintptr_t)pwm == (uintptr_t)&__metal_dt_pwm_10035000) { + return 16; + } + else { + return 0; + } +} + +static __inline__ int __metal_driver_sifive_pwm0_comparator_count(struct metal_pwm *pwm) +{ + if ((uintptr_t)pwm == (uintptr_t)&__metal_dt_pwm_10015000) { + return 4; + } + else if ((uintptr_t)pwm == (uintptr_t)&__metal_dt_pwm_10025000) { + return 4; + } + else if ((uintptr_t)pwm == (uintptr_t)&__metal_dt_pwm_10035000) { + return 4; + } + else { + return 0; + } +} + + + +/* --------------------- sifive_remapper2 ------------ */ + + +/* --------------------- sifive_rtc0 ------------ */ +static __inline__ unsigned long __metal_driver_sifive_rtc0_control_base(const struct metal_rtc *const rtc) +{ + if ((uintptr_t)rtc == (uintptr_t)&__metal_dt_rtc_10000000) { + return METAL_SIFIVE_AON0_10000000_BASE_ADDRESS; + } + else { + return 0; + } +} + +static __inline__ unsigned long __metal_driver_sifive_rtc0_control_size(const struct metal_rtc *const rtc) +{ + if ((uintptr_t)rtc == (uintptr_t)&__metal_dt_rtc_10000000) { + return METAL_SIFIVE_AON0_10000000_SIZE; + } + else { + return 0; + } +} + +static __inline__ struct metal_interrupt * __metal_driver_sifive_rtc0_interrupt_parent(const struct metal_rtc *const rtc) +{ + if ((uintptr_t)rtc == (uintptr_t)&__metal_dt_rtc_10000000) { + return (struct metal_interrupt *)&__metal_dt_interrupt_controller_c000000.controller; + } + else { + return 0; + } +} + +static __inline__ int __metal_driver_sifive_rtc0_interrupt_line(const struct metal_rtc *const rtc) +{ + if ((uintptr_t)rtc == (uintptr_t)&__metal_dt_rtc_10000000) { + return 2; + } + else { + return 0; + } +} + +static __inline__ struct metal_clock * __metal_driver_sifive_rtc0_clock(const struct metal_rtc *const rtc) +{ + if ((uintptr_t)rtc == (uintptr_t)&__metal_dt_rtc_10000000) { + return (struct metal_clock *)&__metal_dt_clock_7.clock; + } + else { + return 0; + } +} + + +static __inline__ unsigned long __metal_driver_sifive_spi0_control_base(struct metal_spi *spi) +{ + if ((uintptr_t)spi == (uintptr_t)&__metal_dt_spi_10014000) { + return METAL_SIFIVE_SPI0_10014000_BASE_ADDRESS; + } + else if ((uintptr_t)spi == (uintptr_t)&__metal_dt_spi_10024000) { + return METAL_SIFIVE_SPI0_10024000_BASE_ADDRESS; + } + else if ((uintptr_t)spi == (uintptr_t)&__metal_dt_spi_10034000) { + return METAL_SIFIVE_SPI0_10034000_BASE_ADDRESS; + } + else { + return 0; + } +} + +static __inline__ unsigned long __metal_driver_sifive_spi0_control_size(struct metal_spi *spi) +{ + if ((uintptr_t)spi == (uintptr_t)&__metal_dt_spi_10014000) { + return METAL_SIFIVE_SPI0_10014000_SIZE; + } + else if ((uintptr_t)spi == (uintptr_t)&__metal_dt_spi_10024000) { + return METAL_SIFIVE_SPI0_10024000_SIZE; + } + else if ((uintptr_t)spi == (uintptr_t)&__metal_dt_spi_10034000) { + return METAL_SIFIVE_SPI0_10034000_SIZE; + } + else { + return 0; + } +} + +static __inline__ struct metal_clock * __metal_driver_sifive_spi0_clock(struct metal_spi *spi) +{ + if ((uintptr_t)spi == (uintptr_t)&__metal_dt_spi_10014000) { + return (struct metal_clock *)&__metal_dt_clock_4.clock; + } + else if ((uintptr_t)spi == (uintptr_t)&__metal_dt_spi_10024000) { + return (struct metal_clock *)&__metal_dt_clock_4.clock; + } + else if ((uintptr_t)spi == (uintptr_t)&__metal_dt_spi_10034000) { + return (struct metal_clock *)&__metal_dt_clock_4.clock; + } + else { + return 0; + } +} + +static __inline__ struct __metal_driver_sifive_gpio0 * __metal_driver_sifive_spi0_pinmux(struct metal_spi *spi) +{ + if ((uintptr_t)spi == (uintptr_t)&__metal_dt_spi_10014000) { + return (struct __metal_driver_sifive_gpio0 *)&__metal_dt_gpio_10012000; + } + else if ((uintptr_t)spi == (uintptr_t)&__metal_dt_spi_10024000) { + return (struct __metal_driver_sifive_gpio0 *)&__metal_dt_gpio_10012000; + } + else if ((uintptr_t)spi == (uintptr_t)&__metal_dt_spi_10034000) { + return (struct __metal_driver_sifive_gpio0 *)&__metal_dt_gpio_10012000; + } + else { + return 0; + } +} + +static __inline__ unsigned long __metal_driver_sifive_spi0_pinmux_output_selector(struct metal_spi *spi) +{ + if ((uintptr_t)spi == (uintptr_t)&__metal_dt_spi_10014000) { + return 0; + } + else if ((uintptr_t)spi == (uintptr_t)&__metal_dt_spi_10024000) { + return 0; + } + else if ((uintptr_t)spi == (uintptr_t)&__metal_dt_spi_10034000) { + return 0; + } + else { + return 0; + } +} + +static __inline__ unsigned long __metal_driver_sifive_spi0_pinmux_source_selector(struct metal_spi *spi) +{ + if ((uintptr_t)spi == (uintptr_t)&__metal_dt_spi_10014000) { + return 0; + } + else if ((uintptr_t)spi == (uintptr_t)&__metal_dt_spi_10024000) { + return 60; + } + else if ((uintptr_t)spi == (uintptr_t)&__metal_dt_spi_10034000) { + return 4227858432; + } + else { + return 0; + } +} + + + +/* --------------------- sifive_test0 ------------ */ + + +/* --------------------- sifive_trace ------------ */ + +/* --------------------- sifive_uart0 ------------ */ +static __inline__ unsigned long __metal_driver_sifive_uart0_control_base(struct metal_uart *uart) +{ + if ((uintptr_t)uart == (uintptr_t)&__metal_dt_serial_10013000) { + return METAL_SIFIVE_UART0_10013000_BASE_ADDRESS; + } + else if ((uintptr_t)uart == (uintptr_t)&__metal_dt_serial_10023000) { + return METAL_SIFIVE_UART0_10023000_BASE_ADDRESS; + } + else { + return 0; + } +} + +static __inline__ unsigned long __metal_driver_sifive_uart0_control_size(struct metal_uart *uart) +{ + if ((uintptr_t)uart == (uintptr_t)&__metal_dt_serial_10013000) { + return METAL_SIFIVE_UART0_10013000_SIZE; + } + else if ((uintptr_t)uart == (uintptr_t)&__metal_dt_serial_10023000) { + return METAL_SIFIVE_UART0_10023000_SIZE; + } + else { + return 0; + } +} + +static __inline__ int __metal_driver_sifive_uart0_num_interrupts(struct metal_uart *uart) +{ + if ((uintptr_t)uart == (uintptr_t)&__metal_dt_serial_10013000) { + return METAL_MAX_UART_INTERRUPTS; + } + else if ((uintptr_t)uart == (uintptr_t)&__metal_dt_serial_10023000) { + return METAL_MAX_UART_INTERRUPTS; + } + else { + return 0; + } +} + +static __inline__ struct metal_interrupt * __metal_driver_sifive_uart0_interrupt_parent(struct metal_uart *uart) +{ + if ((uintptr_t)uart == (uintptr_t)&__metal_dt_serial_10013000) { + return (struct metal_interrupt *)&__metal_dt_interrupt_controller_c000000.controller; + } + else if ((uintptr_t)uart == (uintptr_t)&__metal_dt_serial_10023000) { + return (struct metal_interrupt *)&__metal_dt_interrupt_controller_c000000.controller; + } + else { + return 0; + } +} + +static __inline__ int __metal_driver_sifive_uart0_interrupt_line(struct metal_uart *uart) +{ + if ((uintptr_t)uart == (uintptr_t)&__metal_dt_serial_10013000) { + return 3; + } + else if ((uintptr_t)uart == (uintptr_t)&__metal_dt_serial_10023000) { + return 4; + } + else { + return 0; + } +} + +static __inline__ struct metal_clock * __metal_driver_sifive_uart0_clock(struct metal_uart *uart) +{ + if ((uintptr_t)uart == (uintptr_t)&__metal_dt_serial_10013000) { + return (struct metal_clock *)&__metal_dt_clock_4.clock; + } + else if ((uintptr_t)uart == (uintptr_t)&__metal_dt_serial_10023000) { + return (struct metal_clock *)&__metal_dt_clock_4.clock; + } + else { + return 0; + } +} + +static __inline__ struct __metal_driver_sifive_gpio0 * __metal_driver_sifive_uart0_pinmux(struct metal_uart *uart) +{ + if ((uintptr_t)uart == (uintptr_t)&__metal_dt_serial_10013000) { + return (struct __metal_driver_sifive_gpio0 *)&__metal_dt_gpio_10012000; + } + else if ((uintptr_t)uart == (uintptr_t)&__metal_dt_serial_10023000) { + return (struct __metal_driver_sifive_gpio0 *)&__metal_dt_gpio_10012000; + } + else { + return 0; + } +} + +static __inline__ unsigned long __metal_driver_sifive_uart0_pinmux_output_selector(struct metal_uart *uart) +{ + if ((uintptr_t)uart == (uintptr_t)&__metal_dt_serial_10013000) { + return 0; + } + else if ((uintptr_t)uart == (uintptr_t)&__metal_dt_serial_10023000) { + return 0; + } + else { + return 0; + } +} + +static __inline__ unsigned long __metal_driver_sifive_uart0_pinmux_source_selector(struct metal_uart *uart) +{ + if ((uintptr_t)uart == (uintptr_t)&__metal_dt_serial_10013000) { + return 196608; + } + else if ((uintptr_t)uart == (uintptr_t)&__metal_dt_serial_10023000) { + return 8650752; + } + else { + return 0; + } +} + + + +/* --------------------- sifive_simuart0 ------------ */ + + +/* --------------------- sifive_wdog0 ------------ */ +static __inline__ unsigned long __metal_driver_sifive_wdog0_control_base(const struct metal_watchdog *const watchdog) +{ + if ((uintptr_t)watchdog == (uintptr_t)&__metal_dt_aon_10000000) { + return METAL_SIFIVE_AON0_10000000_BASE_ADDRESS; + } + else { + return 0; + } +} + +static __inline__ unsigned long __metal_driver_sifive_wdog0_control_size(const struct metal_watchdog *const watchdog) +{ + if ((uintptr_t)watchdog == (uintptr_t)&__metal_dt_aon_10000000) { + return METAL_SIFIVE_AON0_10000000_SIZE; + } + else { + return 0; + } +} + +static __inline__ struct metal_interrupt * __metal_driver_sifive_wdog0_interrupt_parent(const struct metal_watchdog *const watchdog) +{ + if ((uintptr_t)watchdog == (uintptr_t)&__metal_dt_aon_10000000) { + return (struct metal_interrupt *)&__metal_dt_interrupt_controller_c000000.controller; + } + else { + return 0; + } +} + +static __inline__ int __metal_driver_sifive_wdog0_interrupt_line(const struct metal_watchdog *const watchdog) +{ + if ((uintptr_t)watchdog == (uintptr_t)&__metal_dt_aon_10000000) { + return 1; + } + else { + return 0; + } +} + +static __inline__ struct metal_clock * __metal_driver_sifive_wdog0_clock(const struct metal_watchdog *const watchdog) +{ + if ((uintptr_t)watchdog == (uintptr_t)&__metal_dt_aon_10000000) { + return (struct metal_clock *)&__metal_dt_clock_7.clock; + } + else { + return 0; + } +} + + + +/* --------------------- sifive_fe310_g000_hfrosc ------------ */ +static __inline__ struct metal_clock * __metal_driver_sifive_fe310_g000_hfrosc_ref(const struct metal_clock *clock) +{ + return (struct metal_clock *)&__metal_dt_clock_2.clock; +} + +static __inline__ struct __metal_driver_sifive_fe310_g000_prci * __metal_driver_sifive_fe310_g000_hfrosc_config_base(const struct metal_clock *clock) +{ + return (struct __metal_driver_sifive_fe310_g000_prci *)&__metal_dt_prci_10008000; +} + +static __inline__ const struct __metal_driver_vtable_sifive_fe310_g000_prci * __metal_driver_sifive_fe310_g000_hfrosc_config_vtable(struct metal_clock *clock) +{ + return &__metal_driver_vtable_sifive_fe310_g000_prci; +} + +static __inline__ long __metal_driver_sifive_fe310_g000_hfrosc_config_offset(const struct metal_clock *clock) +{ + return METAL_SIFIVE_FE310_G000_PRCI_HFROSCCFG; +} + + + +/* --------------------- sifive_fe310_g000_hfxosc ------------ */ +static __inline__ struct metal_clock * __metal_driver_sifive_fe310_g000_hfxosc_ref(const struct metal_clock *clock) +{ + return (struct metal_clock *)&__metal_dt_clock_0.clock; +} + +static __inline__ struct __metal_driver_sifive_fe310_g000_prci * __metal_driver_sifive_fe310_g000_hfxosc_config_base(const struct metal_clock *clock) +{ + return (struct __metal_driver_sifive_fe310_g000_prci *)&__metal_dt_prci_10008000; +} + +static __inline__ long __metal_driver_sifive_fe310_g000_hfxosc_config_offset(const struct metal_clock *clock) +{ + return METAL_SIFIVE_FE310_G000_PRCI_HFXOSCCFG; +} + + + +/* --------------------- sifive_fe310_g000_lfrosc ------------ */ +static __inline__ struct metal_clock * __metal_driver_sifive_fe310_g000_lfrosc_lfrosc(const struct metal_clock *clock) +{ + if ((uintptr_t)clock == (uintptr_t)&__metal_dt_clock_7) { + return (struct metal_clock *)&__metal_dt_clock_5.clock; + } + else { + return NULL; + } +} + +static __inline__ struct metal_clock * __metal_driver_sifive_fe310_g000_lfrosc_psdlfaltclk(const struct metal_clock *clock) +{ + if ((uintptr_t)clock == (uintptr_t)&__metal_dt_clock_7) { + return (struct metal_clock *)&__metal_dt_clock_6.clock; + } + else { + return NULL; + } +} + +static __inline__ unsigned long int __metal_driver_sifive_fe310_g000_lfrosc_config_reg(const struct metal_clock *clock) +{ + if ((uintptr_t)clock == (uintptr_t)&__metal_dt_clock_7) { + return 112; + } + else { + return 0; + } +} + +static __inline__ unsigned long int __metal_driver_sifive_fe310_g000_lfrosc_mux_reg(const struct metal_clock *clock) +{ + if ((uintptr_t)clock == (uintptr_t)&__metal_dt_clock_7) { + return 124; + } + else { + return 0; + } +} + + + +/* --------------------- sifive_fe310_g000_pll ------------ */ +static __inline__ struct metal_clock * __metal_driver_sifive_fe310_g000_pll_pllsel0(const struct metal_clock *clock) +{ + return (struct metal_clock *)&__metal_dt_clock_3.clock; +} + +static __inline__ struct metal_clock * __metal_driver_sifive_fe310_g000_pll_pllref(const struct metal_clock *clock) +{ + return (struct metal_clock *)&__metal_dt_clock_1.clock; +} + +static __inline__ struct __metal_driver_sifive_fe310_g000_prci * __metal_driver_sifive_fe310_g000_pll_divider_base(const struct metal_clock *clock) +{ + return (struct __metal_driver_sifive_fe310_g000_prci *)&__metal_dt_prci_10008000; +} + +static __inline__ long __metal_driver_sifive_fe310_g000_pll_divider_offset(const struct metal_clock *clock) +{ + return METAL_SIFIVE_FE310_G000_PRCI_PLLOUTDIV; +} + +static __inline__ struct __metal_driver_sifive_fe310_g000_prci * __metal_driver_sifive_fe310_g000_pll_config_base( ) +{ + return (struct __metal_driver_sifive_fe310_g000_prci *)&__metal_dt_prci_10008000; +} + +static __inline__ long __metal_driver_sifive_fe310_g000_pll_config_offset( ) +{ + return METAL_SIFIVE_FE310_G000_PRCI_PLLCFG; +} + +static __inline__ long __metal_driver_sifive_fe310_g000_pll_init_rate( ) +{ + return 16000000; +} + + + +/* --------------------- sifive_fe310_g000_prci ------------ */ +static __inline__ long __metal_driver_sifive_fe310_g000_prci_base( ) +{ + return METAL_SIFIVE_FE310_G000_PRCI_10008000_BASE_ADDRESS; +} + +static __inline__ long __metal_driver_sifive_fe310_g000_prci_size( ) +{ + return METAL_SIFIVE_FE310_G000_PRCI_10008000_SIZE; +} + +static __inline__ const struct __metal_driver_vtable_sifive_fe310_g000_prci * __metal_driver_sifive_fe310_g000_prci_vtable( ) +{ + return &__metal_driver_vtable_sifive_fe310_g000_prci; +} + + + +#define __METAL_DT_MAX_MEMORIES 3 + +struct metal_memory *__metal_memory_table[] __attribute__((weak)) = { + &__metal_dt_mem_dtim_80000000, + &__metal_dt_mem_itim_8000000, + &__metal_dt_mem_spi_10014000}; + +/* From serial@10013000 */ +#define __METAL_DT_STDOUT_UART_HANDLE (&__metal_dt_serial_10013000.uart) + +#define __METAL_DT_SERIAL_10013000_HANDLE (&__metal_dt_serial_10013000.uart) + +#define __METAL_DT_STDOUT_UART_BAUD 115200 + +/* From clint@2000000 */ +#define __METAL_DT_RISCV_CLINT0_HANDLE (&__metal_dt_clint_2000000.controller) + +#define __METAL_DT_CLINT_2000000_HANDLE (&__metal_dt_clint_2000000.controller) + +#define __METAL_DT_MAX_HARTS 1 + +#define __METAL_CPU_0_ICACHE_HANDLE 1 + +struct __metal_driver_cpu *__metal_cpu_table[] __attribute__((weak)) = { + &__metal_dt_cpu_0}; + +/* From interrupt_controller@c000000 */ +#define __METAL_DT_RISCV_PLIC0_HANDLE (&__metal_dt_interrupt_controller_c000000.controller) + +#define __METAL_DT_INTERRUPT_CONTROLLER_C000000_HANDLE (&__metal_dt_interrupt_controller_c000000.controller) + +#define __METAL_DT_PMP_HANDLE (&__metal_dt_pmp) + +#define __MEE_DT_MAX_GPIOS 1 + +struct __metal_driver_sifive_gpio0 *__metal_gpio_table[] __attribute__((weak)) = { + &__metal_dt_gpio_10012000}; + +#define __METAL_DT_MAX_BUTTONS 0 + +struct __metal_driver_sifive_gpio_button *__metal_button_table[] __attribute__((weak)) = { + NULL }; +#define __METAL_DT_MAX_LEDS 3 + +struct __metal_driver_sifive_gpio_led *__metal_led_table[] __attribute__((weak)) = { + &__metal_dt_led_0, + &__metal_dt_led_1, + &__metal_dt_led_2}; + +#define __METAL_DT_MAX_SWITCHES 0 + +struct __metal_driver_sifive_gpio_switch *__metal_switch_table[] __attribute__((weak)) = { + NULL }; +#define __METAL_DT_MAX_I2CS 1 + +struct __metal_driver_sifive_i2c0 *__metal_i2c_table[] __attribute__((weak)) = { + &__metal_dt_i2c_10016000}; + +#define __METAL_DT_MAX_PWMS 3 + +struct __metal_driver_sifive_pwm0 *__metal_pwm_table[] __attribute__((weak)) = { + &__metal_dt_pwm_10015000, + &__metal_dt_pwm_10025000, + &__metal_dt_pwm_10035000}; + +#define __METAL_DT_MAX_RTCS 1 + +struct __metal_driver_sifive_rtc0 *__metal_rtc_table[] __attribute__((weak)) = { + &__metal_dt_rtc_10000000}; + +#define __METAL_DT_MAX_SPIS 3 + +struct __metal_driver_sifive_spi0 *__metal_spi_table[] __attribute__((weak)) = { + &__metal_dt_spi_10014000, + &__metal_dt_spi_10024000, + &__metal_dt_spi_10034000}; + +#define __METAL_DT_MAX_UARTS 2 + +struct __metal_driver_sifive_uart0 *__metal_uart_table[] __attribute__((weak)) = { + &__metal_dt_serial_10013000, + &__metal_dt_serial_10023000}; + +#define __METAL_DT_MAX_SIMUARTS 0 + +struct __metal_driver_sifive_simuart0 *__metal_simuart_table[] __attribute__((weak)) = { + NULL }; +#define __METAL_DT_MAX_WDOGS 1 + +struct __metal_driver_sifive_wdog0 *__metal_wdog_table[] __attribute__((weak)) = { + &__metal_dt_aon_10000000}; + +/* From clock@4 */ +#define __METAL_DT_SIFIVE_FE310_G000_PLL_HANDLE (&__metal_dt_clock_4) + +#define __METAL_DT_CLOCK_4_HANDLE (&__metal_dt_clock_4) + +#endif /* MACROS_ELSE_METAL_H*/ + +#endif /* ! __METAL_MACHINE_MACROS */ + +#endif /* ! ASSEMBLY */ diff --git a/arch/riscv32/fe310/src/metal/machine/inline.h b/arch/riscv32/fe310/src/metal/machine/inline.h new file mode 100644 index 00000000..46e1cb4b --- /dev/null +++ b/arch/riscv32/fe310/src/metal/machine/inline.h @@ -0,0 +1,390 @@ +/* Copyright 2019 SiFive, Inc */ +/* SPDX-License-Identifier: Apache-2.0 */ +/* ----------------------------------- */ +/* ----------------------------------- */ + +#ifndef ASSEMBLY + +#ifndef METAL_INLINE_H +#define METAL_INLINE_H + +#include + + +/* --------------------- fixed_clock ------------ */ +extern __inline__ unsigned long __metal_driver_fixed_clock_rate(const struct metal_clock *clock); + + +/* --------------------- fixed_factor_clock ------------ */ + + +/* --------------------- sifive_clint0 ------------ */ +extern __inline__ unsigned long __metal_driver_sifive_clint0_control_base(struct metal_interrupt *controller); +extern __inline__ unsigned long __metal_driver_sifive_clint0_control_size(struct metal_interrupt *controller); +extern __inline__ int __metal_driver_sifive_clint0_num_interrupts(struct metal_interrupt *controller); +extern __inline__ struct metal_interrupt * __metal_driver_sifive_clint0_interrupt_parents(struct metal_interrupt *controller, int idx); +extern __inline__ int __metal_driver_sifive_clint0_interrupt_lines(struct metal_interrupt *controller, int idx); + + +/* --------------------- cpu ------------ */ +extern __inline__ int __metal_driver_cpu_hartid(struct metal_cpu *cpu); +extern __inline__ int __metal_driver_cpu_timebase(struct metal_cpu *cpu); +extern __inline__ struct metal_interrupt * __metal_driver_cpu_interrupt_controller(struct metal_cpu *cpu); +extern __inline__ int __metal_driver_cpu_num_pmp_regions(struct metal_cpu *cpu); +extern __inline__ struct metal_buserror * __metal_driver_cpu_buserror(struct metal_cpu *cpu); + + +/* --------------------- sifive_plic0 ------------ */ +extern __inline__ unsigned long __metal_driver_sifive_plic0_control_base(struct metal_interrupt *controller); +extern __inline__ unsigned long __metal_driver_sifive_plic0_control_size(struct metal_interrupt *controller); +extern __inline__ int __metal_driver_sifive_plic0_num_interrupts(struct metal_interrupt *controller); +extern __inline__ int __metal_driver_sifive_plic0_max_priority(struct metal_interrupt *controller); +extern __inline__ struct metal_interrupt * __metal_driver_sifive_plic0_interrupt_parents(struct metal_interrupt *controller, int idx); +extern __inline__ int __metal_driver_sifive_plic0_interrupt_lines(struct metal_interrupt *controller, int idx); +extern __inline__ int __metal_driver_sifive_plic0_context_ids(int hartid); + + +/* --------------------- sifive_buserror0 ------------ */ + + +/* --------------------- sifive_clic0 ------------ */ + + +/* --------------------- sifive_local_external_interrupts0 ------------ */ + + +/* --------------------- sifive_global_external_interrupts0 ------------ */ + + +/* --------------------- sifive_gpio0 ------------ */ +extern __inline__ unsigned long __metal_driver_sifive_gpio0_base(struct metal_gpio *gpio); +extern __inline__ unsigned long __metal_driver_sifive_gpio0_size(struct metal_gpio *gpio); +extern __inline__ int __metal_driver_sifive_gpio0_num_interrupts(struct metal_gpio *gpio); +extern __inline__ struct metal_interrupt * __metal_driver_sifive_gpio0_interrupt_parent(struct metal_gpio *gpio); +extern __inline__ int __metal_driver_sifive_gpio0_interrupt_lines(struct metal_gpio *gpio, int idx); + + +/* --------------------- sifive_gpio_button ------------ */ + + +/* --------------------- sifive_gpio_led ------------ */ +extern __inline__ struct metal_gpio * __metal_driver_sifive_gpio_led_gpio(struct metal_led *led); +extern __inline__ int __metal_driver_sifive_gpio_led_pin(struct metal_led *led); +extern __inline__ char * __metal_driver_sifive_gpio_led_label(struct metal_led *led); + + +/* --------------------- sifive_gpio_switch ------------ */ + + +/* --------------------- sifive_i2c0 ------------ */ +extern __inline__ unsigned long __metal_driver_sifive_i2c0_control_base(struct metal_i2c *i2c); +extern __inline__ unsigned long __metal_driver_sifive_i2c0_control_size(struct metal_i2c *i2c); +extern __inline__ int __metal_driver_sifive_i2c0_num_interrupts(struct metal_i2c *i2c); +extern __inline__ struct metal_interrupt * __metal_driver_sifive_i2c0_interrupt_parent(struct metal_i2c *i2c); +extern __inline__ int __metal_driver_sifive_i2c0_interrupt_line(struct metal_i2c *i2c); +extern __inline__ struct metal_clock * __metal_driver_sifive_i2c0_clock(struct metal_i2c *i2c); +extern __inline__ struct __metal_driver_sifive_gpio0 * __metal_driver_sifive_i2c0_pinmux(struct metal_i2c *i2c); +extern __inline__ unsigned long __metal_driver_sifive_i2c0_pinmux_output_selector(struct metal_i2c *i2c); +extern __inline__ unsigned long __metal_driver_sifive_i2c0_pinmux_source_selector(struct metal_i2c *i2c); + + +/* --------------------- sifive_prci0 ------------ */ + + +/* --------------------- sifive_pwm0 ------------ */ +extern __inline__ unsigned long __metal_driver_sifive_pwm0_control_base(struct metal_pwm *pwm); +extern __inline__ unsigned long __metal_driver_sifive_pwm0_control_size(struct metal_pwm *pwm); +extern __inline__ int __metal_driver_sifive_pwm0_num_interrupts(struct metal_pwm *pwm); +extern __inline__ struct metal_interrupt * __metal_driver_sifive_pwm0_interrupt_parent(struct metal_pwm *pwm); +extern __inline__ int __metal_driver_sifive_pwm0_interrupt_lines(struct metal_pwm *pwm, int idx); +extern __inline__ struct metal_clock * __metal_driver_sifive_pwm0_clock(struct metal_pwm *pwm); +extern __inline__ struct __metal_driver_sifive_gpio0 * __metal_driver_sifive_pwm0_pinmux(struct metal_pwm *pwm); +extern __inline__ unsigned long __metal_driver_sifive_pwm0_pinmux_output_selector(struct metal_pwm *pwm); +extern __inline__ unsigned long __metal_driver_sifive_pwm0_pinmux_source_selector(struct metal_pwm *pwm); +extern __inline__ int __metal_driver_sifive_pwm0_compare_width(struct metal_pwm *pwm); +extern __inline__ int __metal_driver_sifive_pwm0_comparator_count(struct metal_pwm *pwm); + + +/* --------------------- sifive_remapper2 ------------ */ + + +/* --------------------- sifive_rtc0 ------------ */ +extern __inline__ unsigned long __metal_driver_sifive_rtc0_control_base(const struct metal_rtc *const rtc); +extern __inline__ unsigned long __metal_driver_sifive_rtc0_control_size(const struct metal_rtc *const rtc); +extern __inline__ struct metal_interrupt * __metal_driver_sifive_rtc0_interrupt_parent(const struct metal_rtc *const rtc); +extern __inline__ int __metal_driver_sifive_rtc0_interrupt_line(const struct metal_rtc *const rtc); +extern __inline__ struct metal_clock * __metal_driver_sifive_rtc0_clock(const struct metal_rtc *const rtc); + + +/* --------------------- sifive_spi0 ------------ */ +extern __inline__ unsigned long __metal_driver_sifive_spi0_control_base(struct metal_spi *spi); +extern __inline__ unsigned long __metal_driver_sifive_spi0_control_size(struct metal_spi *spi); +extern __inline__ struct __metal_driver_sifive_gpio0 * __metal_driver_sifive_spi0_pinmux(struct metal_spi *spi); +extern __inline__ unsigned long __metal_driver_sifive_spi0_pinmux_output_selector(struct metal_spi *spi); +extern __inline__ unsigned long __metal_driver_sifive_spi0_pinmux_source_selector(struct metal_spi *spi); + + +/* --------------------- sifive_test0 ------------ */ + + +/* --------------------- sifive_trace ------------ */ + +/* --------------------- sifive_uart0 ------------ */ +extern __inline__ unsigned long __metal_driver_sifive_uart0_control_base(struct metal_uart *uart); +extern __inline__ unsigned long __metal_driver_sifive_uart0_control_size(struct metal_uart *uart); +extern __inline__ int __metal_driver_sifive_uart0_num_interrupts(struct metal_uart *uart); +extern __inline__ struct metal_interrupt * __metal_driver_sifive_uart0_interrupt_parent(struct metal_uart *uart); +extern __inline__ int __metal_driver_sifive_uart0_interrupt_line(struct metal_uart *uart); +extern __inline__ struct metal_clock * __metal_driver_sifive_uart0_clock(struct metal_uart *uart); +extern __inline__ struct __metal_driver_sifive_gpio0 * __metal_driver_sifive_uart0_pinmux(struct metal_uart *uart); +extern __inline__ unsigned long __metal_driver_sifive_uart0_pinmux_output_selector(struct metal_uart *uart); +extern __inline__ unsigned long __metal_driver_sifive_uart0_pinmux_source_selector(struct metal_uart *uart); + + +/* --------------------- sifive_simuart0 ------------ */ + + +/* --------------------- sifive_wdog0 ------------ */ +extern __inline__ unsigned long __metal_driver_sifive_wdog0_control_base(const struct metal_watchdog *const watchdog); +extern __inline__ unsigned long __metal_driver_sifive_wdog0_control_size(const struct metal_watchdog *const watchdog); +extern __inline__ struct metal_interrupt * __metal_driver_sifive_wdog0_interrupt_parent(const struct metal_watchdog *const watchdog); +extern __inline__ int __metal_driver_sifive_wdog0_interrupt_line(const struct metal_watchdog *const watchdog); +extern __inline__ struct metal_clock * __metal_driver_sifive_wdog0_clock(const struct metal_watchdog *const watchdog); + + +/* --------------------- sifive_fe310_g000_hfrosc ------------ */ +extern __inline__ struct metal_clock * __metal_driver_sifive_fe310_g000_hfrosc_ref(const struct metal_clock *clock); +extern __inline__ struct __metal_driver_sifive_fe310_g000_prci * __metal_driver_sifive_fe310_g000_hfrosc_config_base(const struct metal_clock *clock); +extern __inline__ const struct __metal_driver_vtable_sifive_fe310_g000_prci * __metal_driver_sifive_fe310_g000_hfrosc_config_vtable(struct metal_clock *clock); +extern __inline__ long __metal_driver_sifive_fe310_g000_hfrosc_config_offset(const struct metal_clock *clock); + + +/* --------------------- sifive_fe310_g000_hfxosc ------------ */ +extern __inline__ struct metal_clock * __metal_driver_sifive_fe310_g000_hfxosc_ref(const struct metal_clock *clock); +extern __inline__ struct __metal_driver_sifive_fe310_g000_prci * __metal_driver_sifive_fe310_g000_hfxosc_config_base(const struct metal_clock *clock); +extern __inline__ long __metal_driver_sifive_fe310_g000_hfxosc_config_offset(const struct metal_clock *clock); + + +/* --------------------- sifive_fe310_g000_lfrosc ------------ */ +extern __inline__ struct metal_clock * __metal_driver_sifive_fe310_g000_lfrosc_lfrosc(const struct metal_clock *clock); +extern __inline__ struct metal_clock * __metal_driver_sifive_fe310_g000_lfrosc_psdlfaltclk(const struct metal_clock *clock); +extern __inline__ unsigned long int __metal_driver_sifive_fe310_g000_lfrosc_config_reg(const struct metal_clock *clock); +extern __inline__ unsigned long int __metal_driver_sifive_fe310_g000_lfrosc_mux_reg(const struct metal_clock *clock); + + +/* --------------------- sifive_fe310_g000_pll ------------ */ +extern __inline__ struct metal_clock * __metal_driver_sifive_fe310_g000_pll_pllsel0(const struct metal_clock *clock); +extern __inline__ struct metal_clock * __metal_driver_sifive_fe310_g000_pll_pllref(const struct metal_clock *clock); +extern __inline__ struct __metal_driver_sifive_fe310_g000_prci * __metal_driver_sifive_fe310_g000_pll_config_base( ); +extern __inline__ long __metal_driver_sifive_fe310_g000_pll_config_offset( ); +extern __inline__ struct __metal_driver_sifive_fe310_g000_prci * __metal_driver_sifive_fe310_g000_pll_divider_base(const struct metal_clock *clock); +extern __inline__ long __metal_driver_sifive_fe310_g000_pll_divider_offset(const struct metal_clock *clock); +extern __inline__ long __metal_driver_sifive_fe310_g000_pll_init_rate( ); + + +/* --------------------- fe310_g000_prci ------------ */ +extern __inline__ long __metal_driver_sifive_fe310_g000_prci_base( ); +extern __inline__ long __metal_driver_sifive_fe310_g000_prci_size( ); +extern __inline__ const struct __metal_driver_vtable_sifive_fe310_g000_prci * __metal_driver_sifive_fe310_g000_prci_vtable( ); + + +/* From clock@0 */ +struct __metal_driver_fixed_clock __metal_dt_clock_0 = { + .clock.vtable = &__metal_driver_vtable_fixed_clock.clock, +}; + +/* From clock@2 */ +struct __metal_driver_fixed_clock __metal_dt_clock_2 = { + .clock.vtable = &__metal_driver_vtable_fixed_clock.clock, +}; + +/* From clock@5 */ +struct __metal_driver_fixed_clock __metal_dt_clock_5 = { + .clock.vtable = &__metal_driver_vtable_fixed_clock.clock, +}; + +/* From clock@6 */ +struct __metal_driver_fixed_clock __metal_dt_clock_6 = { + .clock.vtable = &__metal_driver_vtable_fixed_clock.clock, +}; + +struct metal_memory __metal_dt_mem_dtim_80000000 = { + ._base_address = 2147483648UL, + ._size = 16384UL, + ._attrs = { + .R = 1, + .W = 1, + .X = 1, + .C = 1, + .A = 1}, +}; + +struct metal_memory __metal_dt_mem_itim_8000000 = { + ._base_address = 134217728UL, + ._size = 8192UL, + ._attrs = { + .R = 1, + .W = 1, + .X = 1, + .C = 1, + .A = 1}, +}; + +struct metal_memory __metal_dt_mem_spi_10014000 = { + ._base_address = 536870912UL, + ._size = 500000UL, + ._attrs = { + .R = 1, + .W = 1, + .X = 1, + .C = 1, + .A = 1}, +}; + +struct metal_memory __metal_dt_mem_spi_10024000 = { + ._attrs = { + .R = 1, + .W = 1, + .X = 1, + .C = 1, + .A = 1}, +}; + +struct metal_memory __metal_dt_mem_spi_10034000 = { + ._attrs = { + .R = 1, + .W = 1, + .X = 1, + .C = 1, + .A = 1}, +}; + +/* From clint@2000000 */ +struct __metal_driver_riscv_clint0 __metal_dt_clint_2000000 = { + .controller.vtable = &__metal_driver_vtable_riscv_clint0.clint_vtable, + .init_done = 0, +}; + +/* From cpu@0 */ +struct __metal_driver_cpu __metal_dt_cpu_0 = { + .cpu.vtable = &__metal_driver_vtable_cpu.cpu_vtable, + .hpm_count = 0, +}; + +/* From interrupt_controller */ +struct __metal_driver_riscv_cpu_intc __metal_dt_cpu_0_interrupt_controller = { + .controller.vtable = &__metal_driver_vtable_riscv_cpu_intc.controller_vtable, + .init_done = 0, +}; + +/* From interrupt_controller@c000000 */ +struct __metal_driver_riscv_plic0 __metal_dt_interrupt_controller_c000000 = { + .controller.vtable = &__metal_driver_vtable_riscv_plic0.plic_vtable, + .init_done = 0, +}; + +struct metal_pmp __metal_dt_pmp; + +/* From gpio@10012000 */ +struct __metal_driver_sifive_gpio0 __metal_dt_gpio_10012000 = { + .gpio.vtable = &__metal_driver_vtable_sifive_gpio0.gpio, +}; + +/* From led@0 */ +struct __metal_driver_sifive_gpio_led __metal_dt_led_0 = { + .led.vtable = &__metal_driver_vtable_sifive_led.led_vtable, +}; + +/* From led@1 */ +struct __metal_driver_sifive_gpio_led __metal_dt_led_1 = { + .led.vtable = &__metal_driver_vtable_sifive_led.led_vtable, +}; + +/* From led@2 */ +struct __metal_driver_sifive_gpio_led __metal_dt_led_2 = { + .led.vtable = &__metal_driver_vtable_sifive_led.led_vtable, +}; + +/* From i2c@10016000 */ +struct __metal_driver_sifive_i2c0 __metal_dt_i2c_10016000 = { + .i2c.vtable = &__metal_driver_vtable_sifive_i2c0.i2c, +}; + +/* From pwm@10015000 */ +struct __metal_driver_sifive_pwm0 __metal_dt_pwm_10015000 = { + .pwm.vtable = &__metal_driver_vtable_sifive_pwm0.pwm, +}; + +/* From pwm@10025000 */ +struct __metal_driver_sifive_pwm0 __metal_dt_pwm_10025000 = { + .pwm.vtable = &__metal_driver_vtable_sifive_pwm0.pwm, +}; + +/* From pwm@10035000 */ +struct __metal_driver_sifive_pwm0 __metal_dt_pwm_10035000 = { + .pwm.vtable = &__metal_driver_vtable_sifive_pwm0.pwm, +}; + +/* From aon@10000000 */ +struct __metal_driver_sifive_rtc0 __metal_dt_rtc_10000000 = { + .rtc.vtable = &__metal_driver_vtable_sifive_rtc0.rtc, +}; + +/* From spi@10014000 */ +struct __metal_driver_sifive_spi0 __metal_dt_spi_10014000 = { + .spi.vtable = &__metal_driver_vtable_sifive_spi0.spi, +}; + +/* From spi@10024000 */ +struct __metal_driver_sifive_spi0 __metal_dt_spi_10024000 = { + .spi.vtable = &__metal_driver_vtable_sifive_spi0.spi, +}; + +/* From spi@10034000 */ +struct __metal_driver_sifive_spi0 __metal_dt_spi_10034000 = { + .spi.vtable = &__metal_driver_vtable_sifive_spi0.spi, +}; + +/* From serial@10013000 */ +struct __metal_driver_sifive_uart0 __metal_dt_serial_10013000 = { + .uart.vtable = &__metal_driver_vtable_sifive_uart0.uart, +}; + +/* From serial@10023000 */ +struct __metal_driver_sifive_uart0 __metal_dt_serial_10023000 = { + .uart.vtable = &__metal_driver_vtable_sifive_uart0.uart, +}; + +/* From aon@10000000 */ +struct __metal_driver_sifive_wdog0 __metal_dt_aon_10000000 = { + .watchdog.vtable = &__metal_driver_vtable_sifive_wdog0.watchdog, +}; + +/* From clock@3 */ +struct __metal_driver_sifive_fe310_g000_hfrosc __metal_dt_clock_3 = { + .clock.vtable = &__metal_driver_vtable_sifive_fe310_g000_hfrosc.clock, +}; + +/* From clock@1 */ +struct __metal_driver_sifive_fe310_g000_hfxosc __metal_dt_clock_1 = { + .clock.vtable = &__metal_driver_vtable_sifive_fe310_g000_hfxosc.clock, +}; + +/* From clock@7 */ +struct __metal_driver_sifive_fe310_g000_lfrosc __metal_dt_clock_7 = { + .clock.vtable = &__metal_driver_vtable_sifive_fe310_g000_lfrosc.clock, +}; + +/* From clock@4 */ +struct __metal_driver_sifive_fe310_g000_pll __metal_dt_clock_4 = { + .clock.vtable = &__metal_driver_vtable_sifive_fe310_g000_pll.clock, +}; + +/* From prci@10008000 */ +struct __metal_driver_sifive_fe310_g000_prci __metal_dt_prci_10008000 = { + .vtable = &__metal_driver_vtable_sifive_fe310_g000_prci, +}; + + +#endif /* METAL_INLINE_H*/ +#endif /* ! ASSEMBLY */ diff --git a/arch/riscv32/fe310/src/metal/machine/platform.h b/arch/riscv32/fe310/src/metal/machine/platform.h new file mode 100644 index 00000000..6b97e212 --- /dev/null +++ b/arch/riscv32/fe310/src/metal/machine/platform.h @@ -0,0 +1,302 @@ +/* Copyright 2019 SiFive, Inc */ +/* SPDX-License-Identifier: Apache-2.0 */ +/* ----------------------------------- */ +/* ----------------------------------- */ + +#ifndef METAL_PLATFORM_H +#define METAL_PLATFORM_H + +/* From clock@0 */ +#define METAL_FIXED_CLOCK_0_CLOCK_0_CLOCK_FREQUENCY 16000000UL + +/* From clock@2 */ +#define METAL_FIXED_CLOCK_2_CLOCK_2_CLOCK_FREQUENCY 72000000UL + +/* From clock@5 */ +#define METAL_FIXED_CLOCK_5_CLOCK_5_CLOCK_FREQUENCY 32768UL + +/* From clock@6 */ +#define METAL_FIXED_CLOCK_6_CLOCK_6_CLOCK_FREQUENCY 32768UL + +#define METAL_FIXED_CLOCK + +/* From clint@2000000 */ +#define METAL_RISCV_CLINT0_2000000_BASE_ADDRESS 33554432UL +#define METAL_RISCV_CLINT0_0_BASE_ADDRESS 33554432UL +#define METAL_RISCV_CLINT0_2000000_SIZE 65536UL +#define METAL_RISCV_CLINT0_0_SIZE 65536UL + +#define METAL_RISCV_CLINT0 +#define METAL_RISCV_CLINT0_MSIP_BASE 0UL +#define METAL_RISCV_CLINT0_MTIMECMP_BASE 16384UL +#define METAL_RISCV_CLINT0_MTIME 49144UL + +/* From interrupt_controller@c000000 */ +#define METAL_RISCV_PLIC0_C000000_BASE_ADDRESS 201326592UL +#define METAL_RISCV_PLIC0_0_BASE_ADDRESS 201326592UL +#define METAL_RISCV_PLIC0_C000000_SIZE 67108864UL +#define METAL_RISCV_PLIC0_0_SIZE 67108864UL +#define METAL_RISCV_PLIC0_C000000_RISCV_MAX_PRIORITY 7UL +#define METAL_RISCV_PLIC0_0_RISCV_MAX_PRIORITY 7UL +#define METAL_RISCV_PLIC0_C000000_RISCV_NDEV 53UL +#define METAL_RISCV_PLIC0_0_RISCV_NDEV 53UL + +#define METAL_RISCV_PLIC0 +#define METAL_RISCV_PLIC0_PRIORITY_BASE 0UL +#define METAL_RISCV_PLIC0_PENDING_BASE 4096UL +#define METAL_RISCV_PLIC0_ENABLE_BASE 8192UL +#define METAL_RISCV_PLIC0_ENABLE_PER_HART 128UL +#define METAL_RISCV_PLIC0_CONTEXT_BASE 2097152UL +#define METAL_RISCV_PLIC0_CONTEXT_PER_HART 4096UL +#define METAL_RISCV_PLIC0_CONTEXT_THRESHOLD 0UL +#define METAL_RISCV_PLIC0_CONTEXT_CLAIM 4UL + +/* From aon@10000000 */ +#define METAL_SIFIVE_AON0_10000000_BASE_ADDRESS 268435456UL +#define METAL_SIFIVE_AON0_0_BASE_ADDRESS 268435456UL +#define METAL_SIFIVE_AON0_10000000_SIZE 32768UL +#define METAL_SIFIVE_AON0_0_SIZE 32768UL + +#define METAL_SIFIVE_AON0 +#define METAL_SIFIVE_AON0_WDOGCFG 0UL +#define METAL_SIFIVE_AON0_WDOGCOUNT 8UL +#define METAL_SIFIVE_AON0_WDOGS 16UL +#define METAL_SIFIVE_AON0_WDOGFEED 24UL +#define METAL_SIFIVE_AON0_WDOGKEY 28UL +#define METAL_SIFIVE_AON0_WDOGCMP 32UL +#define METAL_SIFIVE_AON0_RTCCFG 64UL +#define METAL_SIFIVE_AON0_RTCLO 72UL +#define METAL_SIFIVE_AON0_RTCHI 72UL +#define METAL_SIFIVE_AON0_RTCS 80UL +#define METAL_SIFIVE_AON0_RTCCMP 96UL +#define METAL_SIFIVE_AON0_LFROSCCFG 112UL +#define METAL_SIFIVE_AON0_BACKUP0 128UL +#define METAL_SIFIVE_AON0_BACKUP1 132UL +#define METAL_SIFIVE_AON0_BACKUP2 136UL +#define METAL_SIFIVE_AON0_BACKUP3 140UL +#define METAL_SIFIVE_AON0_BACKUP4 144UL +#define METAL_SIFIVE_AON0_BACKUP5 148UL +#define METAL_SIFIVE_AON0_BACKUP6 152UL +#define METAL_SIFIVE_AON0_BACKUP7 152UL +#define METAL_SIFIVE_AON0_BACKUP8 160UL +#define METAL_SIFIVE_AON0_BACKUP9 164UL +#define METAL_SIFIVE_AON0_BACKUP10 168UL +#define METAL_SIFIVE_AON0_BACKUP11 172UL +#define METAL_SIFIVE_AON0_BACKUP12 176UL +#define METAL_SIFIVE_AON0_BACKUP13 180UL +#define METAL_SIFIVE_AON0_BACKUP14 184UL +#define METAL_SIFIVE_AON0_BACKUP15 188UL +#define METAL_SIFIVE_AON0_BACKUP16 192UL +#define METAL_SIFIVE_AON0_BACKUP17 196UL +#define METAL_SIFIVE_AON0_BACKUP18 200UL +#define METAL_SIFIVE_AON0_BACKUP19 204UL +#define METAL_SIFIVE_AON0_BACKUP20 208UL +#define METAL_SIFIVE_AON0_BACKUP21 212UL +#define METAL_SIFIVE_AON0_BACKUP22 216UL +#define METAL_SIFIVE_AON0_BACKUP23 220UL +#define METAL_SIFIVE_AON0_BACKUP24 224UL +#define METAL_SIFIVE_AON0_BACKUP25 228UL +#define METAL_SIFIVE_AON0_BACKUP26 232UL +#define METAL_SIFIVE_AON0_BACKUP27 236UL +#define METAL_SIFIVE_AON0_BACKUP28 240UL +#define METAL_SIFIVE_AON0_BACKUP29 244UL +#define METAL_SIFIVE_AON0_BACKUP30 248UL +#define METAL_SIFIVE_AON0_BACKUP31 252UL +#define METAL_SIFIVE_AON0_PMU_WAKEUP_BASE 256UL +#define METAL_SIFIVE_AON0_PWM_SLEEP_BASE 288UL +#define METAL_SIFIVE_AON0_PMUIE 320UL +#define METAL_SIFIVE_AON0_PMUCAUSE 324UL +#define METAL_SIFIVE_AON0_PMUSLEEP 328UL +#define METAL_SIFIVE_AON0_PMUKEY 332UL + +/* From clock@3 */ + +#define METAL_SIFIVE_FE310_G000_HFROSC + +/* From clock@1 */ + +#define METAL_SIFIVE_FE310_G000_HFXOSC + +/* From clock@7 */ + +#define METAL_SIFIVE_FE310_G000_LFROSC + +/* From prci@10008000 */ +#define METAL_SIFIVE_FE310_G000_PRCI_10008000_BASE_ADDRESS 268468224UL +#define METAL_SIFIVE_FE310_G000_PRCI_0_BASE_ADDRESS 268468224UL +#define METAL_SIFIVE_FE310_G000_PRCI_10008000_SIZE 32768UL +#define METAL_SIFIVE_FE310_G000_PRCI_0_SIZE 32768UL + +#define METAL_SIFIVE_FE310_G000_PRCI +#define METAL_SIFIVE_FE310_G000_PRCI_HFROSCCFG 0UL +#define METAL_SIFIVE_FE310_G000_PRCI_HFXOSCCFG 4UL +#define METAL_SIFIVE_FE310_G000_PRCI_PLLCFG 8UL +#define METAL_SIFIVE_FE310_G000_PRCI_PLLOUTDIV 12UL + +/* From clock@4 */ +#define METAL_SIFIVE_FE310_G000_PLL_4_CLOCK_FREQUENCY 16000000UL + +#define METAL_SIFIVE_FE310_G000_PLL + +/* From gpio@10012000 */ +#define METAL_SIFIVE_GPIO0_10012000_BASE_ADDRESS 268509184UL +#define METAL_SIFIVE_GPIO0_0_BASE_ADDRESS 268509184UL +#define METAL_SIFIVE_GPIO0_10012000_SIZE 4096UL +#define METAL_SIFIVE_GPIO0_0_SIZE 4096UL + +#define METAL_SIFIVE_GPIO0 +#define METAL_SIFIVE_GPIO0_VALUE 0UL +#define METAL_SIFIVE_GPIO0_INPUT_EN 4UL +#define METAL_SIFIVE_GPIO0_OUTPUT_EN 8UL +#define METAL_SIFIVE_GPIO0_PORT 12UL +#define METAL_SIFIVE_GPIO0_PUE 16UL +#define METAL_SIFIVE_GPIO0_DS 20UL +#define METAL_SIFIVE_GPIO0_RISE_IE 24UL +#define METAL_SIFIVE_GPIO0_RISE_IP 28UL +#define METAL_SIFIVE_GPIO0_FALL_IE 32UL +#define METAL_SIFIVE_GPIO0_FALL_IP 36UL +#define METAL_SIFIVE_GPIO0_HIGH_IE 40UL +#define METAL_SIFIVE_GPIO0_HIGH_IP 44UL +#define METAL_SIFIVE_GPIO0_LOW_IE 48UL +#define METAL_SIFIVE_GPIO0_LOW_IP 52UL +#define METAL_SIFIVE_GPIO0_IOF_EN 56UL +#define METAL_SIFIVE_GPIO0_IOF_SEL 60UL +#define METAL_SIFIVE_GPIO0_OUT_XOR 64UL + +/* From led@0 */ + +/* From led@1 */ + +/* From led@2 */ + +#define METAL_SIFIVE_GPIO_LEDS + +/* From i2c@10016000 */ +#define METAL_SIFIVE_I2C0_10016000_BASE_ADDRESS 268525568UL +#define METAL_SIFIVE_I2C0_0_BASE_ADDRESS 268525568UL +#define METAL_SIFIVE_I2C0_10016000_SIZE 4096UL +#define METAL_SIFIVE_I2C0_0_SIZE 4096UL + +#define METAL_SIFIVE_I2C0 +#define METAL_SIFIVE_I2C0_PRESCALE_LOW 0UL +#define METAL_SIFIVE_I2C0_PRESCALE_HIGH 4UL +#define METAL_SIFIVE_I2C0_CONTROL 8UL +#define METAL_SIFIVE_I2C0_TRANSMIT 12UL +#define METAL_SIFIVE_I2C0_RECEIVE 12UL +#define METAL_SIFIVE_I2C0_COMMAND 16UL +#define METAL_SIFIVE_I2C0_STATUS 16UL + +/* From pwm@10015000 */ +#define METAL_SIFIVE_PWM0_10015000_BASE_ADDRESS 268521472UL +#define METAL_SIFIVE_PWM0_0_BASE_ADDRESS 268521472UL +#define METAL_SIFIVE_PWM0_10015000_SIZE 4096UL +#define METAL_SIFIVE_PWM0_0_SIZE 4096UL + +/* From pwm@10025000 */ +#define METAL_SIFIVE_PWM0_10025000_BASE_ADDRESS 268587008UL +#define METAL_SIFIVE_PWM0_1_BASE_ADDRESS 268587008UL +#define METAL_SIFIVE_PWM0_10025000_SIZE 4096UL +#define METAL_SIFIVE_PWM0_1_SIZE 4096UL + +/* From pwm@10035000 */ +#define METAL_SIFIVE_PWM0_10035000_BASE_ADDRESS 268652544UL +#define METAL_SIFIVE_PWM0_2_BASE_ADDRESS 268652544UL +#define METAL_SIFIVE_PWM0_10035000_SIZE 4096UL +#define METAL_SIFIVE_PWM0_2_SIZE 4096UL + +#define METAL_SIFIVE_PWM0 +#define METAL_SIFIVE_PWM0_PWMCFG 0UL +#define METAL_SIFIVE_PWM0_PWMCOUNT 8UL +#define METAL_SIFIVE_PWM0_PWMS 16UL +#define METAL_SIFIVE_PWM0_PWMCMP0 32UL +#define METAL_SIFIVE_PWM0_PWMCMP1 36UL +#define METAL_SIFIVE_PWM0_PWMCMP2 40UL +#define METAL_SIFIVE_PWM0_PWMCMP3 44UL + +/* From aon@10000000 */ +#define METAL_SIFIVE_AON0_10000000_BASE_ADDRESS 268435456UL +#define METAL_SIFIVE_AON0_0_BASE_ADDRESS 268435456UL +#define METAL_SIFIVE_AON0_10000000_SIZE 32768UL +#define METAL_SIFIVE_AON0_0_SIZE 32768UL + +#define METAL_SIFIVE_RTC0 +#define METAL_SIFIVE_RTC0_RTCCFG 64UL +#define METAL_SIFIVE_RTC0_RTCCOUNTLO 72UL +#define METAL_SIFIVE_RTC0_RTCCOUNTHI 76UL +#define METAL_SIFIVE_RTC0_RTCS 80UL +#define METAL_SIFIVE_RTC0_RTCCMP0 96UL + +/* From spi@10014000 */ +#define METAL_SIFIVE_SPI0_10014000_BASE_ADDRESS 268517376UL +#define METAL_SIFIVE_SPI0_0_BASE_ADDRESS 268517376UL +#define METAL_SIFIVE_SPI0_10014000_SIZE 4096UL +#define METAL_SIFIVE_SPI0_0_SIZE 4096UL + +/* From spi@10024000 */ +#define METAL_SIFIVE_SPI0_10024000_BASE_ADDRESS 268582912UL +#define METAL_SIFIVE_SPI0_1_BASE_ADDRESS 268582912UL +#define METAL_SIFIVE_SPI0_10024000_SIZE 4096UL +#define METAL_SIFIVE_SPI0_1_SIZE 4096UL + +/* From spi@10034000 */ +#define METAL_SIFIVE_SPI0_10034000_BASE_ADDRESS 268648448UL +#define METAL_SIFIVE_SPI0_2_BASE_ADDRESS 268648448UL +#define METAL_SIFIVE_SPI0_10034000_SIZE 4096UL +#define METAL_SIFIVE_SPI0_2_SIZE 4096UL + +#define METAL_SIFIVE_SPI0 +#define METAL_SIFIVE_SPI0_SCKDIV 0UL +#define METAL_SIFIVE_SPI0_SCKMODE 4UL +#define METAL_SIFIVE_SPI0_CSID 16UL +#define METAL_SIFIVE_SPI0_CSDEF 20UL +#define METAL_SIFIVE_SPI0_CSMODE 24UL +#define METAL_SIFIVE_SPI0_DELAY0 40UL +#define METAL_SIFIVE_SPI0_DELAY1 44UL +#define METAL_SIFIVE_SPI0_FMT 64UL +#define METAL_SIFIVE_SPI0_TXDATA 72UL +#define METAL_SIFIVE_SPI0_RXDATA 76UL +#define METAL_SIFIVE_SPI0_TXMARK 80UL +#define METAL_SIFIVE_SPI0_RXMARK 84UL +#define METAL_SIFIVE_SPI0_FCTRL 96UL +#define METAL_SIFIVE_SPI0_FFMT 100UL +#define METAL_SIFIVE_SPI0_IE 112UL +#define METAL_SIFIVE_SPI0_IP 116UL + +/* From serial@10013000 */ +#define METAL_SIFIVE_UART0_10013000_BASE_ADDRESS 268513280UL +#define METAL_SIFIVE_UART0_0_BASE_ADDRESS 268513280UL +#define METAL_SIFIVE_UART0_10013000_SIZE 4096UL +#define METAL_SIFIVE_UART0_0_SIZE 4096UL + +/* From serial@10023000 */ +#define METAL_SIFIVE_UART0_10023000_BASE_ADDRESS 268578816UL +#define METAL_SIFIVE_UART0_1_BASE_ADDRESS 268578816UL +#define METAL_SIFIVE_UART0_10023000_SIZE 4096UL +#define METAL_SIFIVE_UART0_1_SIZE 4096UL + +#define METAL_SIFIVE_UART0 +#define METAL_SIFIVE_UART0_TXDATA 0UL +#define METAL_SIFIVE_UART0_RXDATA 4UL +#define METAL_SIFIVE_UART0_TXCTRL 8UL +#define METAL_SIFIVE_UART0_RXCTRL 12UL +#define METAL_SIFIVE_UART0_IE 16UL +#define METAL_SIFIVE_UART0_IP 20UL +#define METAL_SIFIVE_UART0_DIV 24UL + +/* From aon@10000000 */ +#define METAL_SIFIVE_AON0_10000000_BASE_ADDRESS 268435456UL +#define METAL_SIFIVE_AON0_0_BASE_ADDRESS 268435456UL +#define METAL_SIFIVE_AON0_10000000_SIZE 32768UL +#define METAL_SIFIVE_AON0_0_SIZE 32768UL + +#define METAL_SIFIVE_WDOG0 +#define METAL_SIFIVE_WDOG0_MAGIC_KEY 5370206UL +#define METAL_SIFIVE_WDOG0_MAGIC_FOOD 218755085UL +#define METAL_SIFIVE_WDOG0_WDOGCFG 0UL +#define METAL_SIFIVE_WDOG0_WDOGCOUNT 8UL +#define METAL_SIFIVE_WDOG0_WDOGS 16UL +#define METAL_SIFIVE_WDOG0_WDOGFEED 24UL +#define METAL_SIFIVE_WDOG0_WDOGKEY 28UL +#define METAL_SIFIVE_WDOG0_WDOGCMP 32UL + +#endif /* METAL_PLATFORM_H*/ diff --git a/arch/riscv32/fe310/src/sparkfun_redboard/Kconfig b/arch/riscv32/fe310/src/sparkfun_redboard/Kconfig new file mode 100644 index 00000000..dcb681bb --- /dev/null +++ b/arch/riscv32/fe310/src/sparkfun_redboard/Kconfig @@ -0,0 +1,9 @@ +# +# For a description of the syntax of this configuration file, +# see the file kconfig-language.txt in the docs folder of mTower repository. +# + +if PLATFORM_NUMAKER_PFM_M2351 +comment "NuMaker-PFM-M2351 Peripheral Configuration Options" + +endif # PLATFORM_NUMAKER_PFM_M2351 diff --git a/arch/riscv32/fe310/src/sparkfun_redboard/Makefile b/arch/riscv32/fe310/src/sparkfun_redboard/Makefile new file mode 100644 index 00000000..3818a9c7 --- /dev/null +++ b/arch/riscv32/fe310/src/sparkfun_redboard/Makefile @@ -0,0 +1,119 @@ +############################################################################ +# arch/riscv32/fe310/src/sparkfun_redboard/Makefile +# +# Copyright (c) 2023 Samsung Electronics Co., Ltd. All Rights Reserved. +# Author: Taras Drozdovskyi t.drozdovsky@samsung.com +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +############################################################################ + +-include $(TOPDIR)/Make.defs +-include $(TOPDIR)/.config +-include Make.defs + +MTOWER_NS = $(TOPDIR)/mtower_ns.bin + +ifeq ($(CONFIG_BOOTLOADER2),y) +BL2 := $(TOPDIR)/build/secure$(subst $(TOPDIR),,$(shell pwd))/../NuBL2/bl2.bin +ECDSA_KEYS := ../NuBL2/KeyInfo/ecdsa_keys.bin +else +BL2 := null +ECDSA_KEYS := null +endif + +# ifeq ($(CONFIG_BOOTLOADER32),y) +# BL32 := $(TOPDIR)/build/secure$(subst $(TOPDIR),,$(shell pwd))/secure/bl32.bin +# else +# BL32 := null +# endif +ifeq ($(CONFIG_BOOTLOADER33),y) +BL33 := $(TOPDIR)/build/nonsecure$(subst $(TOPDIR),,$(shell pwd))/nonsecure/bl33.bin +else +BL33 := null +endif + +mtower$(EXEEXT): +# ifeq ($(CONFIG_BOOTLOADER2),y) +# $(Q) $(MAKE) -C ../NuBL2 TOPDIR="$(TOPDIR)" NuBL2$(EXEEXT) +# endif +# ifeq ($(CONFIG_BOOTLOADER32),y) +# $(Q) $(MAKE) -C secure TOPDIR="$(TOPDIR)" mtower_s$(EXEEXT) +# endif +ifeq ($(CONFIG_BOOTLOADER33),y) + $(Q) $(MAKE) -C nonsecure TOPDIR="$(TOPDIR)" mtower_ns$(EXEEXT) +endif +# $(TOPDIR)/tools/fwinfogen $(BL2) $(BL32) $(MTOWER_S) $(ECDSA_KEYS) $(BL33) $(MTOWER_NS) +# $(Q) cp -f $(TOPDIR)/mtower_ns.bin /media/sf_Shared/ +# $(Q) cp -f $(TOPDIR)/mtower_s.bin /media/sf_Shared/ + +# $(Q) printf "$(GREEN)+-----------------------------------------------------------------------+$(NORMAL)\n" +# $(Q) printf "$(GREEN)| Secure image |$(NORMAL)\n" +# $(Q) printf "$(GREEN)+-----------------------------------------------------------------------+$(NORMAL)\n" +# $(Q) printf "$(GREEN)| Name | Start addr | End addr | Size | File name |$(NORMAL)\n" +# $(Q) printf "$(GREEN)+-----------------------------------------------------------------------+$(NORMAL)\n" +# ifeq ($(CONFIG_BOOTLOADER2),y) +# $(eval bl2_sz = `stat -c%s $(TOPDIR)/build/secure$(subst $(TOPDIR),,$(shell pwd))/../NuBL2/bl2.bin`) +# $(eval bl2_start = 0x00000000) + +# $(Q) printf "$(GREEN)| Boot loader 2 | 0x%08x | 0x%08x | %u\t| bl2.bin |$(NORMAL)\n" $(bl2_start) $$(( $(bl2_start) + $(bl2_sz) )) $(bl2_sz) +# endif + +# ifeq ($(CONFIG_BOOTLOADER32),y) +# $(eval bl32_sz = `stat -c%s $(TOPDIR)/build/secure$(subst $(TOPDIR),,$(shell pwd))/secure/bl32.bin`) +# $(eval bl32_start = $(CONFIG_START_ADDRESS_BL32)) + +# $(Q) printf "$(GREEN)| Secure handler | 0x%08x | 0x%08x | %u\t| bl32.bin |$(NORMAL)\n" $(bl32_start) $$(( $(bl32_start) + $(bl32_sz) )) $(bl32_sz) +# ifeq ($(CONFIG_BOOTLOADER2),y) +# $(Q) printf "$(GREEN)| FW info of BL32 | 0x%08x | 0x%08x | %u\t| *auto gen* |$(NORMAL)\n" 0x00038000 0x000380c0 192 +# endif + +# endif +# $(eval mtower_s_sz = `stat -c%s $(TOPDIR)/mtower_s.bin`) +# $(eval mtower_s_start = 0x00000000) +# $(Q) printf "$(GREEN)+-----------------------------------------------------------------------+$(NORMAL)\n" +# $(Q) printf "$(GREEN)| mtower_s | 0x%08x | 0x%08x | %u\t| mtower_s.bin |$(NORMAL)\n" $(mtower_s_start) $$(( $(mtower_s_start) + $(mtower_s_sz) )) $(mtower_s_sz) +# $(Q) printf "$(GREEN)+-----------------------------------------------------------------------+$(NORMAL)\n" + +# ifeq ($(CONFIG_BOOTLOADER33),y) +# $(eval bl33_sz = `stat -c%s $(TOPDIR)/build/nonsecure$(subst $(TOPDIR),,$(shell pwd))/nonsecure/bl33.bin`) +# $(eval bl33_start = $(CONFIG_START_ADDRESS_BL33)) + +# $(Q) printf "$(GREEN)+-----------------------------------------------------------------------+$(NORMAL)\n" +# $(Q) printf "$(GREEN)| Non-Secure image |$(NORMAL)\n" +# $(Q) printf "$(GREEN)+-----------------------------------------------------------------------+$(NORMAL)\n" +# $(Q) printf "$(GREEN)| Name | Start addr | End addr | Size | File name |$(NORMAL)\n" +# $(Q) printf "$(GREEN)+-----------------------------------------------------------------------+$(NORMAL)\n" +# $(Q) printf "$(GREEN)| FreeRTOS | 0x%08x | 0x%08x | %u\t| bl33.bin |$(NORMAL)\n" $(bl33_start) $$(( $(bl33_start) + $(bl33_sz) )) $(bl33_sz) +# ifeq ($(CONFIG_BOOTLOADER2),y) +# $(Q) printf "$(GREEN)| FW info of BL33 | 0x%08x | 0x%08x | %u\t| *auto gen* |$(NORMAL)\n" 0x00078000 0x000780c0 192 +# endif +# $(Q) printf "$(GREEN)+-----------------------------------------------------------------------+$(NORMAL)\n" +# $(eval mtower_ns_sz = `stat -c%s $(TOPDIR)/mtower_ns.bin`) +# $(eval mtower_ns_start = $(CONFIG_START_ADDRESS_BL33)) +# $(Q) printf "$(GREEN)| mtower_ns | 0x%08x | 0x%08x | %u\t| mtower_ns.bin |$(NORMAL)\n" $(mtower_ns_start) $$(( $(mtower_ns_start) + $(mtower_ns_sz) )) $(mtower_ns_sz) +# $(Q) printf "$(GREEN)+-----------------------------------------------------------------------+$(NORMAL)\n" + +# endif + +clean: +# $(Q) $(MAKE) -C secure TOPDIR="$(TOPDIR)" clean + $(Q) $(MAKE) -C nonsecure TOPDIR="$(TOPDIR)" clean + $(Q) rm -rf $(TOPDIR)/build +# $(Q) rm -f $(TOPDIR)/mtower_s.bin + $(Q) rm -f $(TOPDIR)/mtower_ns.bin + +distclean: clean + +flash: + $(Q) $(ECHO) -e "loadfile $(TOPDIR)/build/nonsecure/arch/riscv32/fe310/src/sparkfun_redboard/nonsecure/bl33.hex\nrnh\nexit" | JLinkExe -device FE310 -if JTAG -speed 4000 -jtagconf -1,-1 -autoconnect 1 diff --git a/arch/riscv32/fe310/src/sparkfun_redboard/nonsecure/Bridge_Freedom-metal_FreeRTOS.c b/arch/riscv32/fe310/src/sparkfun_redboard/nonsecure/Bridge_Freedom-metal_FreeRTOS.c new file mode 100644 index 00000000..dd2ccaae --- /dev/null +++ b/arch/riscv32/fe310/src/sparkfun_redboard/nonsecure/Bridge_Freedom-metal_FreeRTOS.c @@ -0,0 +1,200 @@ +/* Copyright 2019 SiFive, Inc */ +/* SPDX-License-Identifier: Apache-2.0 */ + +/* FreeRTOS kernel includes. */ +#include + +/* Freedom metal includes. */ +#include +#include + +#include +#include +#include + +static __attribute__ ((aligned(16))) StackType_t xISRStack[ configMINIMAL_STACK_SIZE + 1 ] __attribute__ ((section (".heap"))) ; +__attribute__ ((aligned(4))) uint8_t ucHeap[ configTOTAL_HEAP_SIZE ] __attribute__ ((section (".heap"))); + + +__attribute__((constructor)) static void FreeRTOS_init(void); + +#if( configENABLE_FPU == 1 ) + extern void prvSetupFPU( void ); +#endif /* configENABLE_FPU */ + +void vExceptionFaultHandler(struct metal_cpu *cpu, int ecode) +{ + const char * const pcException = "Exception cause: "; + const char *pcCauses[] = { + "0 Instruction address misaligned", + "1 Instruction access fault", + "2 Illegal instruction", + "3 Breakpoint", + "4 Load address misaligned", + "5 Load access fault", + "6 Store/AMO address misaligned", + "7 Store/AMO access fault"}; + + write( STDOUT_FILENO, pcException, strlen( pcException ) ); + write( STDOUT_FILENO, pcCauses[ecode], strlen( pcCauses[ecode] ) ); + write( STDOUT_FILENO, "\r\n", 2 ); +} +__attribute__((constructor)) static void FreeRTOS_init(void) +{ + struct metal_cpu *cpu; + struct metal_interrupt *cpu_intr; + extern BaseType_t xPortFreeRTOSInit( StackType_t xIsrStack ); + + const char * const pcErrorMsg = "No External controller\n"; + const char * const pcErrorMsg1 = "Failed to register exception handler\r\n"; + + /* Remove compiler warning about unused parameter. */ + ( void ) pcErrorMsg; + +#if ( configENABLE_FPU == 1 ) + prvSetupFPU(); +#endif /* (configENABLE_FPU == 1 ) */ + + /* + * Initilize freedom-metal interrupt managment. + * Its SHOULD be made before calling xPortFreeRTOSInit because + * the interrupt/exeception handler MUST be the freertos handler. + */ + cpu = metal_cpu_get(metal_cpu_get_current_hartid()); + if (cpu == NULL) + { + return; + } + + cpu_intr = metal_cpu_interrupt_controller(cpu); + if (cpu_intr == NULL) + { + return; + } + metal_interrupt_init(cpu_intr); + + /* Register a handler for the store access fault exception */ + for (int i = 0; i != 8; i++) + { + if (metal_cpu_exception_register(cpu, i, vExceptionFaultHandler) < 0) + { + write(STDOUT_FILENO, pcErrorMsg1, strlen(pcErrorMsg1)); + return; + } + } + +#ifdef METAL_RISCV_PLIC0 + { + struct metal_interrupt *plic; + + // Check if this target has a plic. If not gracefull exit + plic = metal_interrupt_get_controller(METAL_PLIC_CONTROLLER, 0); + if (plic == NULL) { + write( STDOUT_FILENO, pcErrorMsg, strlen( pcErrorMsg ) ); + + for( ;; ); + } + metal_interrupt_init(plic); + } +#endif + +#ifdef METAL_SIFIVE_CLIC0 + { + struct metal_interrupt *clic; + + // Check we this target has a plic. If not gracefull exit + clic = metal_interrupt_get_controller(METAL_CLIC_CONTROLLER, 0); + if (clic == NULL) { + write( STDOUT_FILENO, pcErrorMsg, strlen( pcErrorMsg ) ); + + for( ;; ); + } + metal_interrupt_init(clic); + } +#endif + + /* + * Call xPortFreeRTOSInit in order to set xISRTopStack + */ + if ( 0 != xPortFreeRTOSInit((StackType_t)&( xISRStack[ ( (configMINIMAL_STACK_SIZE - 1) & ~portBYTE_ALIGNMENT_MASK ) ] ))) { + _exit(-1); + } +} + + +void FreedomMetal_InterruptHandler( void ) +{ + int id; + void *priv; + struct __metal_driver_riscv_cpu_intc *intc; + struct __metal_driver_cpu *cpu; + portUBASE_TYPE mcause, hartid, mtvec; + + __asm__ __volatile__ ( + "csrr %0, mhartid \n" + "csrr %1, mcause \n" + "csrr %2, mtvec \n" + : "=r"(hartid), "=r"(mcause), "=r"(mtvec) + :: + ); + + cpu = __metal_cpu_table[hartid]; + + if ( cpu ) { + + intc = (struct __metal_driver_riscv_cpu_intc *) + __metal_driver_cpu_interrupt_controller((struct metal_cpu *)cpu); + id = mcause & METAL_MCAUSE_CAUSE; + + if ((id < METAL_INTERRUPT_ID_LC0) || + ((mtvec & METAL_MTVEC_MASK) == METAL_MTVEC_DIRECT)) { + priv = intc->metal_int_table[id].exint_data; + if (intc->metal_int_table[id].handler != NULL) + intc->metal_int_table[id].handler(id, priv); + goto cleanup; + } + if ((mtvec & METAL_MTVEC_MASK) == METAL_MTVEC_CLIC) { + uintptr_t mtvt; + metal_interrupt_handler_t mtvt_handler; + + __asm volatile ("csrr %0, mtvt" : "=r"(mtvt)); + priv = intc->metal_int_table[METAL_INTERRUPT_ID_SW].sub_int; + mtvt_handler = (metal_interrupt_handler_t)mtvt; + if (mtvt_handler != NULL) + mtvt_handler(id, priv); + goto cleanup; + } + } + +cleanup: + return; +} + +void FreedomMetal_ExceptionHandler( void ) +{ + int id; + portUBASE_TYPE mcause, hartid; + struct __metal_driver_riscv_cpu_intc *intc; + struct __metal_driver_cpu *cpu; + + __asm__ __volatile__ ("csrr %0, mhartid" : "=r"(hartid)); + cpu = __metal_cpu_table[hartid]; + + if ( cpu ) { + intc = (struct __metal_driver_riscv_cpu_intc *) + __metal_driver_cpu_interrupt_controller((struct metal_cpu *)cpu); + + __asm__ __volatile__ ("csrr %0, mcause" : "=r"(mcause)); + id = mcause & METAL_MCAUSE_CAUSE; + + configASSERT( id < METAL_ECALL_U_EXCEPTION_CODE ); + + if (id < METAL_ECALL_U_EXCEPTION_CODE) { + if (intc->metal_exception_table[id] != NULL) + intc->metal_exception_table[id]((struct metal_cpu *)cpu, id); + } + } + + // for( ;; ); // return is dangerous, we just got a critical exception. + return; +} diff --git a/arch/riscv32/fe310/src/sparkfun_redboard/nonsecure/Bridge_Freedom-metal_FreeRTOS.h b/arch/riscv32/fe310/src/sparkfun_redboard/nonsecure/Bridge_Freedom-metal_FreeRTOS.h new file mode 100644 index 00000000..0433aac1 --- /dev/null +++ b/arch/riscv32/fe310/src/sparkfun_redboard/nonsecure/Bridge_Freedom-metal_FreeRTOS.h @@ -0,0 +1,31 @@ +/** + * @file Bridge_Freedom-metal_FreeRTOS.h + * @author Pierre-Henry Moussay (pierre-henry.moussay@sifive.com) + * @brief Bridge_Freedom-metal_FreeRTOS.h.in template for FreeRTOS + * @version 0.1 + * @date 2020-02-26 + * + * @copyright Copyright (c) 2020 SiFive, Inc + * @copyright SPDX-License-Identifier: Apache-2.0 + * + */ + +#ifndef BRIDGE_FREEDOM_METAL_FREERTOS_H +#define BRIDGE_FREEDOM_METAL_FREERTOS_H + +#include + +#if defined(METAL_RISCV_CLINT0) +#define MTIME_CTRL_ADDR METAL_RISCV_CLINT0_0_BASE_ADDRESS +#elif defined(METAL_SIFIVE_CLIC0) +#define MTIME_CTRL_ADDR METAL_SIFIVE_CLIC0_0_BASE_ADDRESS +#endif + +#define MTIME_RATE_HZ 32768 + +#define portHANDLE_INTERRUPT FreedomMetal_InterruptHandler +#define portHANDLE_EXCEPTION FreedomMetal_ExceptionHandler + +#define portUSING_MPU_WRAPPERS 1 + +#endif /* BRIDGE_FREEDOM_METAL_FREERTOS_H */ \ No newline at end of file diff --git a/arch/riscv32/fe310/src/sparkfun_redboard/nonsecure/FreeRTOSConfig.h b/arch/riscv32/fe310/src/sparkfun_redboard/nonsecure/FreeRTOSConfig.h new file mode 100644 index 00000000..5963545c --- /dev/null +++ b/arch/riscv32/fe310/src/sparkfun_redboard/nonsecure/FreeRTOSConfig.h @@ -0,0 +1,162 @@ +/* + * FreeRTOS Kernel V10.2.0 + * Copyright (C) 2017 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * http://aws.amazon.com/freertos + * http://www.FreeRTOS.org + * + * 1 tab == 4 spaces! + */ + +#ifndef FREERTOS_CONFIG_H +#define FREERTOS_CONFIG_H + +#include "Bridge_Freedom-metal_FreeRTOS.h" + +/*----------------------------------------------------------- + * Application specific definitions. + * + * These definitions should be adjusted for your particular hardware and + * application requirements. + * + * THESE PARAMETERS ARE DESCRIBED WITHIN THE 'CONFIGURATION' SECTION OF THE + * FreeRTOS API DOCUMENTATION AVAILABLE ON THE FreeRTOS.org WEB SITE. + * + * See http://www.freertos.org/a00110.html. + *----------------------------------------------------------*/ + +#define configCLINT_BASE_ADDRESS MTIME_CTRL_ADDR +#define configUSE_PREEMPTION 1 +#define configUSE_IDLE_HOOK 0 +#define configUSE_TICK_HOOK 0 +#define configCPU_CLOCK_HZ ( MTIME_RATE_HZ ) +/* + * configTICK_RATE_HZ indicate the frequency in HZ of the Tick. + * On some CPU having a configTICK_RATE_HZ too high could induce scheduling time consuption + * to high, then the task will not have time to run. + * + * configTICK_RATE_HZ = 100 means a Tick every 10ms. + */ +#define configTICK_RATE_HZ ( ( TickType_t ) 100 ) +#define configMAX_PRIORITIES ( 7 ) + +/* + * FPU Support: If configENABLE_FPU is not set, it will be set to 1 into FreeRTOS.h due to ARMv8M ports. + * We Set this value if gcc request fpu support + */ +#ifdef __riscv_fdiv +#define configENABLE_FPU 1 +#else +#define configENABLE_FPU 0 +#endif /* __riscv_fdiv */ + +/* + * configMINIMAL_STACK_SIZE must be a value greater than the stack use by + * the minimal task + the sizeof the register saved. + * The size of the register is differrent from a core to another, e.g. on RiscV + * it could be 32 base register + 32 register for FPU and some other for the + * specific extensions. + */ +#ifdef __riscv_fdiv +#define configMINIMAL_STACK_SIZE ( ( size_t ) 288 ) +#else +#define configMINIMAL_STACK_SIZE ( ( size_t ) 236 ) +#endif /* __riscv_fdiv */ + +/* + * ucHeap buffer is defined by the application and store into section .heap with freedom metal + * so configAPPLICATION_ALLOCATED_HEAP must be set to 1 + */ +#define configAPPLICATION_ALLOCATED_HEAP 1 + +#define configTOTAL_HEAP_SIZE ( ( size_t ) 2400 * sizeof( size_t ) ) + +#define configMAX_TASK_NAME_LEN ( 16 ) +#define configUSE_TRACE_FACILITY 1 +#define configUSE_16_BIT_TICKS 0 +#define configIDLE_SHOULD_YIELD 0 +#define configUSE_MUTEXES 1 +#define configQUEUE_REGISTRY_SIZE 8 +#define configCHECK_FOR_STACK_OVERFLOW 2 +#define configUSE_RECURSIVE_MUTEXES 1 +#define configUSE_MALLOC_FAILED_HOOK 1 +#define configUSE_APPLICATION_TASK_TAG 0 +#define configUSE_COUNTING_SEMAPHORES 1 +#define configGENERATE_RUN_TIME_STATS 0 +#define configUSE_PORT_OPTIMISED_TASK_SELECTION 1 +#define configUSE_STATS_FORMATTING_FUNCTIONS 1 +#define portUSING_MPU_WRAPPERS 1 + +/************************************************************** + * Required for thread-safety of newlib sprintf, strtok, etc... + * Newlib usage into FreeRTOS could induce some unexpected issue + * For more information please read: + * http://www.nadler.com/embedded/newlibAndFreeRTOS.html + * in order to mitigate it the following define must be set to 1 + **************************************************************/ +#define configUSE_NEWLIB_REENTRANT 1 + +/* Co-routine definitions. */ +#define configUSE_CO_ROUTINES 0 +#define configMAX_CO_ROUTINE_PRIORITIES ( 2 ) + +/* Software timer definitions. SwitchC */ +#define configUSE_TIMERS 1 +#define configTIMER_TASK_PRIORITY ( configMAX_PRIORITIES - 1 ) +#define configTIMER_QUEUE_LENGTH 4 +/* + * configTIMER_TASK_STACK_DEPTH must be a value greater than 80 + the sizeof the register saved. + * The size of the register is differrent from a core to another, e.g. on RiscV + * it could be 32 base register + 32 register for FPU and some other for the + * specific extensions. + */ +#define configTIMER_TASK_STACK_DEPTH ( 256 ) + +/* Task priorities. Allow these to be overridden. */ +#ifndef uartPRIMARY_PRIORITY + #define uartPRIMARY_PRIORITY ( configMAX_PRIORITIES - 3 ) +#endif + +/* Set the following definitions to 1 to include the API function, or zero +to exclude the API function. */ +#define INCLUDE_vTaskPrioritySet 1 +#define INCLUDE_uxTaskPriorityGet 1 +#define INCLUDE_vTaskDelete 1 +#define INCLUDE_vTaskCleanUpResources 1 +#define INCLUDE_vTaskSuspend 1 +#define INCLUDE_vTaskDelayUntil 1 +#define INCLUDE_vTaskDelay 1 +#define INCLUDE_eTaskGetState 1 +#define INCLUDE_xTimerPendFunctionCall 1 +#define INCLUDE_uxTaskGetStackHighWaterMark 1 + +/* Overwrite some of the stack sizes allocated to various test and demo tasks. +Like all task stack sizes, the value is the number of words, not bytes. */ +#define bktBLOCK_TIME_TASK_STACK_SIZE 100 +#define notifyNOTIFIED_TASK_STACK_SIZE 120 +#define priSUSPENDED_RX_TASK_STACK_SIZE 90 +#define tmrTIMER_TEST_TASK_STACK_SIZE 100 +#define ebRENDESVOUS_TEST_TASK_STACK_SIZE 100 +#define ebEVENT_GROUP_SET_BITS_TEST_TASK_STACK_SIZE 115 +#define genqMUTEX_TEST_TASK_STACK_SIZE 90 +#define genqGENERIC_QUEUE_TEST_TASK_STACK_SIZE 100 +#define recmuRECURSIVE_MUTEX_TEST_TASK_STACK_SIZE 90 + +#endif /* FREERTOS_CONFIG_H */ diff --git a/arch/riscv32/fe310/src/sparkfun_redboard/nonsecure/Make.defs b/arch/riscv32/fe310/src/sparkfun_redboard/nonsecure/Make.defs new file mode 100644 index 00000000..c973850b --- /dev/null +++ b/arch/riscv32/fe310/src/sparkfun_redboard/nonsecure/Make.defs @@ -0,0 +1,120 @@ +############################################################################ +# arch/riscv32/fe310/src/sparkfun_redboard/nonsecure/Make.defs +# +# Copyright (c) 2023 Samsung Electronics Co., Ltd. All Rights Reserved. +# Author: Taras Drozdovskyi t.drozdovsky@samsung.com +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +############################################################################ + +HEAD_ASRC = + +# Common fe310 files + +CMN_ASRCS = + +CMN_CSRCS = + +# Required CHIP files + +CHIP_ASRCS_NS = ../../../../../../freertos/portable/GCC/RISC-V/portASM.S \ + ../../freedom-metal/src/vector.S \ + ../../freedom-metal/src/trap.S \ + ../../freedom-metal/src/crt0.S \ + ../../freedom-metal/src/entry.S \ + ../../freedom-metal/src/scrub.S + +CHIP_CSRCS_NS = main_ns.c \ + Bridge_Freedom-metal_FreeRTOS.c \ + printf.c + +CHIP_CSRCS_NS += ../../freedom-metal/src/atomic.c \ + ../../freedom-metal/src/cache.c \ + ../../freedom-metal/src/cpu.c \ + ../../freedom-metal/src/hpm.c \ + ../../freedom-metal/src/init.c \ + ../../freedom-metal/src/led.c \ + ../../freedom-metal/src/memory.c \ + ../../freedom-metal/src/prci.c \ + ../../freedom-metal/src/pwm.c \ + ../../freedom-metal/src/rtc.c \ + ../../freedom-metal/src/shutdown.c \ + ../../freedom-metal/src/switch.c \ + ../../freedom-metal/src/time.c \ + ../../freedom-metal/src/uart.c \ + ../../freedom-metal/src/watchdog.c \ + ../../freedom-metal/src/button.c \ + ../../freedom-metal/src/clock.c \ + ../../freedom-metal/src/gpio.c \ + ../../freedom-metal/src/i2c.c \ + ../../freedom-metal/src/interrupt.c \ + ../../freedom-metal/src/lock.c \ + ../../freedom-metal/src/pmp.c \ + ../../freedom-metal/src/privilege.c \ + ../../freedom-metal/src/remapper.c \ + ../../freedom-metal/src/spi.c \ + ../../freedom-metal/src/synchronize_harts.c \ + ../../freedom-metal/src/timer.c \ + ../../freedom-metal/src/tty.c \ + ../../freedom-metal/src/drivers/fixed-clock.c \ + ../../freedom-metal/src/drivers/riscv_plic0.c \ + ../../freedom-metal/src/drivers/sifive_fe310-g000_hfxosc.c \ + ../../freedom-metal/src/drivers/sifive_gpio0.c \ + ../../freedom-metal/src/drivers/sifive_l2pf0.c \ + ../../freedom-metal/src/drivers/sifive_pwm0.c \ + ../../freedom-metal/src/drivers/sifive_test0.c \ + ../../freedom-metal/src/drivers/fixed-factor-clock.c \ + ../../freedom-metal/src/drivers/sifive_buserror0.c \ + ../../freedom-metal/src/drivers/sifive_fe310-g000_lfrosc.c \ + ../../freedom-metal/src/drivers/sifive_gpio-buttons.c \ + ../../freedom-metal/src/drivers/sifive_l2pf1.c \ + ../../freedom-metal/src/drivers/sifive_remapper2.c \ + ../../freedom-metal/src/drivers/sifive_trace.c \ + ../../freedom-metal/src/drivers/inline.c \ + ../../freedom-metal/src/drivers/sifive_ccache0.c \ + ../../freedom-metal/src/drivers/sifive_fe310-g000_pll.c \ + ../../freedom-metal/src/drivers/sifive_gpio-leds.c \ + ../../freedom-metal/src/drivers/sifive_local-external-interrupts0.c \ + ../../freedom-metal/src/drivers/sifive_rtc0.c \ + ../../freedom-metal/src/drivers/sifive_uart0.c \ + ../../freedom-metal/src/drivers/riscv_clint0.c \ + ../../freedom-metal/src/drivers/sifive_clic0.c \ + ../../freedom-metal/src/drivers/sifive_fe310-g000_prci.c \ + ../../freedom-metal/src/drivers/sifive_gpio-switches.c \ + ../../freedom-metal/src/drivers/sifive_pl2cache0.c \ + ../../freedom-metal/src/drivers/sifive_simuart0.c \ + ../../freedom-metal/src/drivers/sifive_wdog0.c \ + ../../freedom-metal/src/drivers/riscv_cpu.c \ + ../../freedom-metal/src/drivers/sifive_fe310-g000_hfrosc.c \ + ../../freedom-metal/src/drivers/sifive_global-external-interrupts0.c \ + ../../freedom-metal/src/drivers/sifive_i2c0.c \ + ../../freedom-metal/src/drivers/sifive_prci0.c \ + ../../freedom-metal/src/drivers/sifive_spi0.c \ + ../../freedom-metal/src/drivers/ucb_htif0.c + +ifeq ($(CONFIG_APPS_HELLO_WORLD),y) +CHIP_CSRCS_NS += ../../../../../../apps/hello_world/ca/hello_world_ns.c +endif + +ifeq ($(CONFIG_APPS_AES),y) +CHIP_CSRCS_NS += ../../../../../../apps/aes/ca/aes_ns.c +endif + +ifeq ($(CONFIG_APPS_HOTP),y) +CHIP_CSRCS_NS += ../../../../../../apps/hotp/ca/hotp_ns.c +endif + +ifeq ($(CONFIG_APPS_TEST),y) +CHIP_CSRCS_NS += ../../../../../../apps/test/ca/test_ns.c +endif diff --git a/arch/riscv32/fe310/src/sparkfun_redboard/nonsecure/Makefile b/arch/riscv32/fe310/src/sparkfun_redboard/nonsecure/Makefile new file mode 100644 index 00000000..bf61a921 --- /dev/null +++ b/arch/riscv32/fe310/src/sparkfun_redboard/nonsecure/Makefile @@ -0,0 +1,94 @@ +############################################################################ +# arch/riscv32/fe310/src/sparkfun_redboard/nonsecure/Makefile +# +# Copyright (c) 2023 Samsung Electronics Co., Ltd. All Rights Reserved. +# Author: Taras Drozdovskyi t.drozdovsky@samsung.com +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +############################################################################ + +-include $(TOPDIR)/Make.defs +-include $(TOPDIR)/.config +-include Make.defs + +CFLAGS += -I../ -I./ +CFLAGS += -I../../ +CFLAGS += -I../../freedom-metal +CFLAGS += -I$(TOPDIR)/include/mtower +CFLAGS += -I$(TOPDIR)/freertos/include +CFLAGS += -I$(TOPDIR)/freertos/portable/GCC/RISC-V +CFLAGS += -I$(TOPDIR)/freertos/portable/GCC/RISC-V/chip_specific_extensions/RV32I_CLINT_no_extensions +#CFLAGS += -I$(TOPDIR)/tee_client/public +#CFLAGS += -I$(TOPDIR)/tee_client/libteec/include/freertos + +ifeq ($(CONFIG_APPS_HELLO_WORLD),y) +CFLAGS += -I$(TOPDIR)/apps/hello_world +endif + +ifeq ($(CONFIG_APPS_AES),y) +CFLAGS += -I$(TOPDIR)/apps/aes +endif + +ifeq ($(CONFIG_APPS_HOTP),y) +CFLAGS += -I$(TOPDIR)/apps/hotp +endif + +ifeq ($(CONFIG_APPS_TEST),y) +CFLAGS += -I$(TOPDIR)/apps/test +endif + +CFLAGS += -DDEBUG_PORT=$(subst ",,$(CONFIG_NONSECURE_DEBUG_UART)) + +OBJDIR = $(TOPDIR)/build/nonsecure$(subst $(TOPDIR),,$(shell pwd)) + +MTOWER_NS = $(TOPDIR)/mtower_ns$(EXEEXT) + +ASRCS_NS = $(CHIP_ASRCS_NS) $(CMN_ASRCS) +AOBJS_NS = $(addprefix $(OBJDIR)/, $(ASRCS_NS:.S=$(OBJEXT))) + +CSRCS_NS = $(CHIP_CSRCS_NS) $(CMN_CSRCS) +COBJS_NS = $(addprefix $(OBJDIR)/, $(CSRCS_NS:.c=$(OBJEXT))) + +OBJS_NS = $(AOBJS_NS) $(COBJS_NS) + +LIBPATHS = -L. -L"$(TOPDIR)/lib" + +LIBS = -lm -lc -lFreeRTOS_ns -lgcc +# -lfe310_StdDriver_ns + +$(AOBJS_NS): $(OBJDIR)/%$(OBJEXT): %.S + $(Q) mkdir -p $(OBJDIR)/$(dir $<) + @echo "AS: $<" +# $(Q) $(CC) -c $(AFLAGS) -DHeap_Size=0x00000700 -DStack_Size=0x00000B00 $< -o $@ + $(Q) $(CC) -c $(CFLAGS) $< -o $@ + +$(COBJS_NS): $(OBJDIR)/%$(OBJEXT): %.c + $(Q) mkdir -p $(OBJDIR)/$(dir $<) + @echo "CC: $<" + $(Q) $(CC) -c $(CFLAGS) $< -o $@ + +mtower_ns$(EXEEXT): $(OBJS_NS) + $(Q) $(MAKE) -C $(TOPDIR)/freertos/ TOPDIR="$(TOPDIR)" FREE_RTOS_ARCH=RISC-V libFreeRTOS_ns.a +# $(Q) $(MAKE) -C ../../freedom-metal/src/ TOPDIR="$(TOPDIR)" libfe310_StdDriver_ns.a + @echo "LD: mTower_ns$(EXEEXT)" +# $(Q) $(CC) $(CFLAGS) -Wl,--section-start=.text=$(CONFIG_START_ADDRESS_BL33) -Tmetal.freertos.lds $(OBJS_NS) $(LIBPATHS) $(LIBS) -o $(OBJDIR)/bl33.elf + $(Q) $(CC) $(CFLAGS) -nostartfiles -Wl,--defsym,__stack_size=0x300 -Wl,--defsym,__heap_size=0x100 -Tmetal.freertos.lds $(OBJS_NS) $(LIBPATHS) $(LIBS) -o $(OBJDIR)/bl33.elf + $(Q) $(OBJCOPY) -S -O binary $(OBJDIR)/bl33.elf $(OBJDIR)/bl33.bin + $(Q) $(OBJCOPY) -S -O ihex $(OBJDIR)/bl33.elf $(OBJDIR)/bl33.hex + +clean: + $(Q) rm -f ../../StdDriver/src/*$(OBJEXT) + +distclean: clean + diff --git a/arch/riscv32/fe310/src/sparkfun_redboard/nonsecure/main_ns.c b/arch/riscv32/fe310/src/sparkfun_redboard/nonsecure/main_ns.c new file mode 100644 index 00000000..82990991 --- /dev/null +++ b/arch/riscv32/fe310/src/sparkfun_redboard/nonsecure/main_ns.c @@ -0,0 +1,1189 @@ +/* Copyright 2019 SiFive, Inc */ +/* SPDX-License-Identifier: Apache-2.0 */ + + + +/****************************************************************************** + * + * main() creates one queue, and two tasks. It then starts the + * scheduler. + * + * The Queue Send Task: + * The queue send task is implemented by the prvQueueSendTask() function in + * this file. prvQueueSendTask() sits in a loop that causes it to repeatedly + * block for 1000 milliseconds, before sending the value 100 to the queue that + * was created within main(). Once the value is sent, the task loops + * back around to block for another 1000 milliseconds...and so on. + * + * The Queue Receive Task: + * The queue receive task is implemented by the prvQueueReceiveTask() function + * in this file. prvQueueReceiveTask() sits in a loop where it repeatedly + * blocks on attempts to read data from the queue that was created within + * blinky(). When data is received, the task checks the value of the + * data, and if the value equals the expected 100, writes 'Blink' to the UART + * (the UART is used in place of the LED to allow easy execution in QEMU). The + * 'block time' parameter passed to the queue receive function specifies that + * the task should be held in the Blocked state indefinitely to wait for data to + * be available on the queue. The queue receive task will only leave the + * Blocked state when the queue send task writes to the queue. As the queue + * send task writes to the queue every 1000 milliseconds, the queue receive + * task leaves the Blocked state every 1000 milliseconds, and therefore toggles + * the LED every 1 second. + */ + +/* Standard includes. */ +#include +#include +#include + +/* Kernel includes. */ +#include "FreeRTOS.h" +#include "task.h" +#include "queue.h" + +/* Freedom metal includes. */ +#include +#include + +#include +#include +#include +#include +#include +#include + +#include +#include "version.h"> + +extern struct metal_led *led0_red, *led0_green, *led0_blue; + +extern pmp_info_t xPmpInfo; + +#define CONFIG_APPS_SPY +#define CONFIG_APPS_HELLO_WORLD + +/* Priorities used by the tasks. */ +#define mainQUEUE_RECEIVE_TASK_PRIORITY ( tskIDLE_PRIORITY + 2 ) +#define mainQUEUE_SEND_TASK_PRIORITY ( tskIDLE_PRIORITY + 1 ) + +#ifndef _RTL_ +/* The 1s value is converted to ticks using the pdMS_TO_TICKS() macro. */ +#define mainQUEUE_TICK_COUNT_FOR_1S pdMS_TO_TICKS( 1000 ) +#else +/* For RTL Simulation we reduce the waiting timing, otherwise the simulation + * will take too more time */ +/* The 10ms value is converted to ticks using the pdMS_TO_TICKS() macro. */ +#define mainQUEUE_TICK_COUNT_FOR_1S pdMS_TO_TICKS( 10 ) +#endif +/* The maximum number items the queue can hold. The priority of the receiving +task is above the priority of the sending task, so the receiving task will +preempt the sending task and remove the queue items each time the sending task +writes to the queue. Therefore the queue will never have more than one item in +it at any time, and even with a queue length of 1, the sending task will never +find the queue full. */ +#define mainQUEUE_LENGTH ( 1 ) + +#if( portUSING_MPU_WRAPPERS == 1 ) +/** + * @brief Calls the port specific code to raise the privilege. + * + * @return pdFALSE if privilege was raised, pdTRUE otherwise. + */ +BaseType_t xPortRaisePrivilege( void ) FREERTOS_SYSTEM_CALL; +extern pmp_info_t xPmpInfo; +#endif + +/*-----------------------------------------------------------*/ +/* + * Functions: + * - prvSetupHardware: Setup Hardware according CPU and Board. + */ +static void prvSetupHardware( void ); + +/* + * The tasks as described in the comments at the top of this file. + */ +static void prvQueueReceiveTask( void *pvParameters ); +static void prvQueueSendTask( void *pvParameters ); +// void vTaskCode( void * pvParameters ) FREERTOS_SYSTEM_CALL; // PRIVILEGED_FUNCTION; +static void vTaskCode( void * pvParameters ) PRIVILEGED_FUNCTION; + +/** + * @brief Implements the task which has Read Only access to the memory region + * ucSharedMemory. + * + * @param pvParameters[in] Parameters as passed during task creation. + */ +static void prvROAccessTask( void * pvParameters ); + +/** + * @brief Implements the task which has Read Write access to the memory region + * ucSharedMemory. + * + * @param pvParameters[in] Parameters as passed during task creation. + */ +static void prvRWAccessTask( void * pvParameters ); + +/** + * @brief Size of the shared memory region. + */ +#define SHARED_MEMORY_SIZE 16 + +/** + * @brief Memory region shared between two tasks. + */ +static uint8_t ucSharedMemory[ SHARED_MEMORY_SIZE ] __attribute__( ( aligned( 32 ) ) ); +// static uint8_t ucSharedMemory1[ SHARED_MEMORY_SIZE ] __attribute__( ( aligned( 32 ) ) ); +// static uint8_t ucSharedMemory2[ SHARED_MEMORY_SIZE ] __attribute__( ( aligned( 32 ) ) ); + +/*-----------------------------------------------------------*/ + +/* The queue used by both tasks. */ +static QueueHandle_t xQueue = NULL; + +struct metal_cpu *cpu0; +struct metal_interrupt *cpu_intr, *tmr_intr; +struct metal_led *led0_red, *led0_green, *led0_blue; + +#define LED_ERROR ((led0_red == NULL) || (led0_green == NULL) || (led0_blue == NULL)) + +void _putchar(char character){ + metal_tty_putc( character ); +} + +/* Task to be created. */ +// void vTaskCode( void * pvParameters ) FREERTOS_SYSTEM_CALL // PRIVILEGED_FUNCTION +// static void vTaskCode( void * pvParameters ) PRIVILEGED_FUNCTION +// { +// unsigned long ulCounter = 0; +// char pcWriteBuffer[200]; + +// ( void ) pvParameters; + +// /* Must not just run off the end of a task function, so delete this task. +// Note that because this task was created using xTaskCreate() the stack was +// allocated dynamically and I have not included any code to free it again. */ +// // vTaskDelete( NULL ); + +// /* The parameter value is expected to be 1 as 1 is passed in the +// pvParameters value in the call to xTaskCreate() below. */ +// // configASSERT( ( ( uint32_t ) pvParameters ) == 1 ); + +// if (portIS_PRIVILEGED() == 1) { +// printf("%s task is running in priveleged mode\r\n", pcTaskGetName(NULL)); +// } else { +// printf("%s task is running in unpriveleged mode\r\n", pcTaskGetName(NULL)); +// } +// write( STDOUT_FILENO, "Test task\r\n", strlen( "Test task\r\n" ) ); + +// for( ;; ) +// { +// /* Task code goes here. */ +// vTaskDelay(mainQUEUE_TICK_COUNT_FOR_1S); +// vTaskList( pcWriteBuffer ); +// printf("\033[2J\r\n"); +// printf( pcWriteBuffer ); +// printf("\r\n"); +// // printf("[ %s ] : %d\r\n", pcTaskGetName(NULL), ++ulCounter ); +// // write( STDOUT_FILENO, "Test task\r\n", strlen( "Test task\r\n" ) ); +// } +// } + +/*-----------------------------------------------------------*/ + +static void prvROAccessTask( void * pvParameters ) +{ + /* Unused parameters. */ + ( void ) pvParameters; + + // if (portIS_PRIVILEGED() == 1) { + // // printf("ROAccess task is running in priveleged mode\r\n"); + // printf("ROAccess\r\n"); + // } else { + // printf("ROAccess task\r\n"); + // // printf("ROAccess task is running in unpriveleged mode\r\n"); + // } + + ucSharedMemory[ 0 ] = 1; + + for( ; ; ) + { + /* Wait for a 3 seconds. */ + vTaskDelay( pdMS_TO_TICKS( 3 * 1000 ) ); + // vTaskDelete(NULL); + } +} +/*-----------------------------------------------------------*/ + +static void prvRWAccessTask( void * pvParameters ) +{ + /* Unused parameters. */ + ( void ) pvParameters; + + if (portIS_PRIVILEGED() == 1) { + // write( STDOUT_FILENO, "RWAccess\r\n", strlen("RWAccess\r\n") ); + // printf("RWAccess task is running in priveleged mode\r\n"); + // printf("RWAccess\r\n"); + } else { + // write( STDOUT_FILENO, "RWAccess\r\n", strlen("RWAccess\r\n") ); + // printf("RWAccess\r\n"); + // printf("RWAccess task is running in unpriveleged mode\r\n"); + } + + // printf("[ RWAccess ] : ucSharedMemory[0] = %d\r\n", ucSharedMemory[0]); + // ucSharedMemory[0] = 1; + // printf("[ RWAccess ] : ucSharedMemory[0] = %d\r\n", ucSharedMemory[0]); + // metal_tty_putc( '#' ); + + for( ; ; ) + { + /* This task has RW access to ucSharedMemory and therefore can write to + * it. */ + // ucSharedMemory[ 0 ] = 0; + + /* Wait for a 3 seconds. */ + vTaskDelay( pdMS_TO_TICKS( 3 * 1000 ) ); + // vTaskDelete(NULL); + } +} +/*-----------------------------------------------------------*/ + +/** + * @brief clnSrvTask - cleaning service. + * + * @param None + * + * @returns None + */ +static void clnSrvTask( void *pvParameters ) +{ + const TickType_t xDelay = 1000 / portTICK_PERIOD_MS; + + (void)pvParameters; + + do { + vTaskPrioritySet( xTaskGetIdleTaskHandle(), uxTaskPriorityGet(xTaskGetCurrentTaskHandle())); +// taskYIELD(); + vTaskDelay( xDelay ); + vTaskPrioritySet( xTaskGetIdleTaskHandle(), tskIDLE_PRIORITY); + vTaskDelay( xDelay ); + } while (1); +} + +#define BUFFER_SIZE 200 + +/** + * @brief menuTask1 - . + * + * @param None + * + * @returns None + */ +static void menuTask(void *pvParameters) PRIVILEGED_FUNCTION +{ + /* Define a buffer that is large enough to hold the generated table. In most + * cases the buffer will be too large to allocate on the stack, hence in this + * example it is declared static. */ + + static char cBuffer[BUFFER_SIZE]; + extern uint32_t __unprivileged_data_section_start__[]; + extern uint32_t __unprivileged_data_section_end__[]; + TaskHandle_t xHandle_TaskROAccess = NULL, xHandle_TaskRWAccess = NULL, xHandle_ReceiveTask = NULL, xHandle_SendTask = NULL; + + static TaskParameters_t xROAccessTaskParameters = + { + .pvTaskCode = prvROAccessTask, + .pcName = "ROAccess", + .usStackDepth = 128, //configMINIMAL_STACK_SIZE, + .pvParameters = NULL, + .uxPriority = mainQUEUE_SEND_TASK_PRIORITY, + .puxStackBuffer = NULL, + // .puxStackBuffer = xROAccessTaskStack, + }; + + static TaskParameters_t xRWAccessTaskParameters = + { + .pvTaskCode = prvRWAccessTask, + .pcName = "RWAccess", + .usStackDepth = 128, //configMINIMAL_STACK_SIZE, + .pvParameters = NULL, + .uxPriority = mainQUEUE_SEND_TASK_PRIORITY, + .puxStackBuffer = NULL, + // .puxStackBuffer = xRWAccessTaskStack, + }; + static TaskParameters_t xTaskRXDefinition = + { + .pvTaskCode = prvQueueReceiveTask, + .pcName = "Rx", + .usStackDepth = configMINIMAL_STACK_SIZE, + .pvParameters = NULL, + .uxPriority = mainQUEUE_RECEIVE_TASK_PRIORITY, + .puxStackBuffer = NULL, + }; + static TaskParameters_t xTaskTXDefinition = + { + .pvTaskCode = prvQueueSendTask, + .pcName = "Tx", + .usStackDepth = configMINIMAL_STACK_SIZE, + .pvParameters = NULL, + .uxPriority = mainQUEUE_SEND_TASK_PRIORITY, + .puxStackBuffer = NULL, + }; + + + + (void)pvParameters; + + int ch; + do + { + portDISABLE_INTERRUPTS(); + printf("\n+- Menu -----------------------------------------------------+\r\n"); +#ifdef CONFIG_APPS_HELLO_WORLD + printf("| [1] - Run 2 unprvl apps & demo interaction between them |\r\n"); + printf("| [2] - Delete 2 unprvl apps |\r\n"); +#endif +#ifdef CONFIG_APPS_HOTP + printf("| [3] - Run HOTP (Ported from OP-TEE, user TA) |\r\n"); +#endif + printf("| [4] - Get FreeRTOS Task List |\r\n"); +#ifdef CONFIG_APPS_TEST + printf("| [5] - Run Test Suite for GP TEE Client & Internal API |\r\n"); +#endif +#ifdef CONFIG_APPS_HW_SECURITY_EXCEPTION_EXAMPLE + printf("| [6] - Run H/W Security exception example (from non-secure) |\r\n"); + printf("| [7] - Run H/W Security exception example (from secure) |\r\n"); +#endif +#ifdef CONFIG_APPS_SPY + printf("| [8] - Run test & spy (trying to get protected data) unprvl |\r\n"); + printf("| [9] - Delete test & spy unprvl apps |\r\n"); +#endif + printf("+------------------------------------------------------------+\r\n"); + portENABLE_INTERRUPTS(); + metal_tty_getc(&ch); + printf("\n[%c]\r\n", (char) ch); + + switch (ch) { +#ifdef CONFIG_APPS_HELLO_WORLD + case '1': + xQueue = xQueueCreate( mainQUEUE_LENGTH, sizeof( uint32_t ) ); + + if( xQueue != NULL ) + { + extern uint32_t __unprivileged_data_section_start__[]; + extern uint32_t __unprivileged_data_section_end__[]; + + /* + * Prepare xRegions for Receive Task + */ + memset(&xTaskRXDefinition.xRegions, 0, sizeof(xTaskRXDefinition.xRegions)); + + // authorize access to data and bss + // Low address + xTaskRXDefinition.xRegions[0].ulLengthInBytes = 4; + xTaskRXDefinition.xRegions[0].ulParameters = ((portPMP_REGION_READ_WRITE) | + (portPMP_REGION_ADDR_MATCH_NA4)); + addr_modifier ( xPmpInfo.granularity, + ( size_t ) __unprivileged_data_section_start__, + (size_t *) &xTaskRXDefinition.xRegions[0].pvBaseAddress); + // printf("xTaskRXDefinition.xRegions[0].pvBaseAddress = %p\r\n", xTaskRXDefinition.xRegions[0].pvBaseAddress); + // High address + xTaskRXDefinition.xRegions[1].ulLengthInBytes = 4; + xTaskRXDefinition.xRegions[1].ulParameters = ((portPMP_REGION_READ_WRITE) | + (portPMP_REGION_ADDR_MATCH_TOR)); + + addr_modifier ( xPmpInfo.granularity, + ( size_t ) __unprivileged_data_section_end__, + (size_t *) &xTaskRXDefinition.xRegions[1].pvBaseAddress); + // printf("xTaskRXDefinition.xRegions[1].pvBaseAddress = %p\r\n", xTaskRXDefinition.xRegions[1].pvBaseAddress); + +#ifdef METAL_SIFIVE_UART0 + // allow access to UART peripheral + xTaskRXDefinition.xRegions[2].ulLengthInBytes = METAL_SIFIVE_UART0_0_SIZE; + xTaskRXDefinition.xRegions[2].ulParameters = ((portPMP_REGION_READ_WRITE) | + (portPMP_REGION_ADDR_MATCH_NAPOT)); + + napot_addr_modifier ( xPmpInfo.granularity, + (size_t) METAL_SIFIVE_UART0_0_BASE_ADDRESS, + (size_t *) &xTaskRXDefinition.xRegions[2].pvBaseAddress, + xTaskRXDefinition.xRegions[2].ulLengthInBytes); +#endif /* METAL_SIFIVE_UART0 */ + + // allocate stack (It will take 2 PMP Slot - So it is not needed to put align the StackBuffer) + xTaskRXDefinition.puxStackBuffer = ( StackType_t * ) pvPortMalloc( xTaskRXDefinition.usStackDepth * sizeof( StackType_t ) ); + // xTaskRXDefinition.puxStackBuffer = xReceiveTaskStack; + // portDISABLE_INTERRUPTS(); + // printf("xTaskRXDefinition.puxStackBuffer = %p\r\n", xTaskRXDefinition.puxStackBuffer); + // portENABLE_INTERRUPTS(); + xTaskCreateRestricted( &xTaskRXDefinition, + &xHandle_ReceiveTask); + + /* + * Prepare xRegions for Send Task + */ + memset(&xTaskTXDefinition.xRegions, 0, sizeof(xTaskTXDefinition.xRegions)); + + // authorize access to data and bss + // Low address + xTaskTXDefinition.xRegions[0].ulLengthInBytes = 4; + xTaskTXDefinition.xRegions[0].ulParameters = ((portPMP_REGION_READ_WRITE) | + (portPMP_REGION_ADDR_MATCH_NA4)); + addr_modifier ( xPmpInfo.granularity, + ( size_t ) __unprivileged_data_section_start__, + (size_t *) &xTaskTXDefinition.xRegions[0].pvBaseAddress); + + // High address + xTaskTXDefinition.xRegions[1].ulLengthInBytes = 4; + xTaskTXDefinition.xRegions[1].ulParameters = ((portPMP_REGION_READ_WRITE) | + (portPMP_REGION_ADDR_MATCH_TOR)); + + addr_modifier ( xPmpInfo.granularity, + ( size_t ) __unprivileged_data_section_end__, + (size_t *) &xTaskTXDefinition.xRegions[1].pvBaseAddress); + +// #ifdef METAL_SIFIVE_GPIO0 +// // allow access to GPIO (Each peripheral are on 4Kb mapping area) +// xTaskTXDefinition.xRegions[2].ulLengthInBytes = METAL_SIFIVE_GPIO0_0_SIZE; +// xTaskTXDefinition.xRegions[2].ulParameters = ((portPMP_REGION_READ_WRITE) | +// (portPMP_REGION_ADDR_MATCH_NAPOT)); + +// napot_addr_modifier ( xPmpInfo.granularity, +// (size_t) METAL_SIFIVE_GPIO0_0_BASE_ADDRESS, +// (size_t *) &xTaskTXDefinition.xRegions[2].pvBaseAddress, +// xTaskTXDefinition.xRegions[2].ulLengthInBytes); +// #endif /* METAL_SIFIVE_GPIO0 */ + +#ifdef METAL_SIFIVE_UART0 + // allow access to UART peripheral + xTaskTXDefinition.xRegions[2].ulLengthInBytes = METAL_SIFIVE_UART0_0_SIZE; + xTaskTXDefinition.xRegions[2].ulParameters = ((portPMP_REGION_READ_WRITE) | + (portPMP_REGION_ADDR_MATCH_NAPOT)); + + napot_addr_modifier ( xPmpInfo.granularity, + (size_t) METAL_SIFIVE_UART0_0_BASE_ADDRESS, + (size_t *) &xTaskTXDefinition.xRegions[2].pvBaseAddress, + xTaskTXDefinition.xRegions[2].ulLengthInBytes); +#endif /* METAL_SIFIVE_UART0 */ + // allocate stack (It will take 2 PMP Slot - So it is not needed to put align the StackBuffer) + xTaskTXDefinition.puxStackBuffer = ( StackType_t * ) pvPortMalloc( xTaskTXDefinition.usStackDepth * sizeof( StackType_t ) ); + // xTaskTXDefinition.puxStackBuffer = xSendTaskStack; + + // printf("xTaskTXDefinition.puxStackBuffer = %p\r\n", xTaskTXDefinition.puxStackBuffer); + + xTaskCreateRestricted( &xTaskTXDefinition, + &xHandle_SendTask); + + } + + break; + case '2': + if (xHandle_ReceiveTask != NULL) + vTaskDelete(xHandle_ReceiveTask); + if (xHandle_SendTask != NULL) + vTaskDelete(xHandle_SendTask); + if (xTaskRXDefinition.puxStackBuffer != NULL) + vPortFree(xTaskRXDefinition.puxStackBuffer); + if (xTaskTXDefinition.puxStackBuffer != NULL) + vPortFree(xTaskTXDefinition.puxStackBuffer); + break; +#endif + case '4': + /* Obtain the current tick count. */ + printf("\nAuto-reload timer callback executing = %d\r\n", xTaskGetTickCount()); + + /* Pass the buffer into vTaskList() to generate the table of information. */ + vTaskList(cBuffer); + portDISABLE_INTERRUPTS(); + printf("%s\n", cBuffer); + portENABLE_INTERRUPTS(); + break; +#ifdef CONFIG_APPS_SPY + case '8': + /* + * Prepare xRegions for xROAccess Task + */ + memset(&xROAccessTaskParameters.xRegions, 0, sizeof(xROAccessTaskParameters.xRegions)); + + // authorize access to data and bss + // Low address + xROAccessTaskParameters.xRegions[0].ulLengthInBytes = 4; + xROAccessTaskParameters.xRegions[0].ulParameters = ((portPMP_REGION_READ_ONLY) | + (portPMP_REGION_ADDR_MATCH_NA4)); + addr_modifier ( xPmpInfo.granularity, + // ( size_t ) __unprivileged_data_section_start__, + ( size_t ) ucSharedMemory, + (size_t *) &xROAccessTaskParameters.xRegions[0].pvBaseAddress); + // printf("xROAccessTaskParameters.xRegions[0].pvBaseAddress = %p\r\n", xROAccessTaskParameters.xRegions[0].ulLengthInBytes); + + // High address + xROAccessTaskParameters.xRegions[1].ulLengthInBytes = 4; + xROAccessTaskParameters.xRegions[1].ulParameters = ((portPMP_REGION_READ_WRITE) | + (portPMP_REGION_ADDR_MATCH_TOR)); + + addr_modifier ( xPmpInfo.granularity, + ( size_t ) __unprivileged_data_section_end__, + (size_t *) &xROAccessTaskParameters.xRegions[1].pvBaseAddress); + +#ifdef METAL_SIFIVE_UART0 + // allow access to UART peripheral + xROAccessTaskParameters.xRegions[2].ulLengthInBytes = METAL_SIFIVE_UART0_0_SIZE; + xROAccessTaskParameters.xRegions[2].ulParameters = ((portPMP_REGION_READ_WRITE) | + (portPMP_REGION_ADDR_MATCH_NAPOT)); + + napot_addr_modifier ( xPmpInfo.granularity, + (size_t) METAL_SIFIVE_UART0_0_BASE_ADDRESS, + (size_t *) &xROAccessTaskParameters.xRegions[2].pvBaseAddress, + xROAccessTaskParameters.xRegions[2].ulLengthInBytes); +#endif /* METAL_SIFIVE_UART0 */ + + xROAccessTaskParameters.puxStackBuffer = ( StackType_t * ) pvPortMalloc( xROAccessTaskParameters.usStackDepth * sizeof( StackType_t ) ); + // xROAccessTaskParameters.puxStackBuffer = xROAccessTaskStack; + // printf("xROAccessTaskParameters.puxStackBuffer = %p\r\n", xROAccessTaskParameters.puxStackBuffer); + + + xTaskCreateRestricted( &xROAccessTaskParameters, + &xHandle_TaskROAccess); + + /* + * Prepare xRegions for xRWAccess Task + */ + memset(&xRWAccessTaskParameters.xRegions, 0, sizeof(xRWAccessTaskParameters.xRegions)); + + // authorize access to data and bss + // Low address + // printf("xROAccessTaskParameters.xRegions[0].ulLengthInBytes = %d\r\n", xROAccessTaskParameters.xRegions[0].ulLengthInBytes); + xRWAccessTaskParameters.xRegions[0].ulLengthInBytes = 4; + xRWAccessTaskParameters.xRegions[0].ulParameters = ((portPMP_REGION_READ_WRITE) | + (portPMP_REGION_ADDR_MATCH_NA4)); + addr_modifier ( xPmpInfo.granularity, + // ( size_t ) __unprivileged_data_section_start__, + ( size_t ) ucSharedMemory, + (size_t *) &xRWAccessTaskParameters.xRegions[0].pvBaseAddress); + + // printf("xRWAccessTaskParameters.xRegions[0].pvBaseAddress = %p\r\n", xRWAccessTaskParameters.xRegions[0].pvBaseAddress); + // High address + xRWAccessTaskParameters.xRegions[1].ulLengthInBytes = 4; + xRWAccessTaskParameters.xRegions[1].ulParameters = ((portPMP_REGION_READ_WRITE) | + (portPMP_REGION_ADDR_MATCH_TOR)); + + addr_modifier ( xPmpInfo.granularity, + ( size_t ) __unprivileged_data_section_end__, + (size_t *) &xRWAccessTaskParameters.xRegions[1].pvBaseAddress); + +#ifdef METAL_SIFIVE_UART0 + // allow access to UART peripheral + xRWAccessTaskParameters.xRegions[2].ulLengthInBytes = METAL_SIFIVE_UART0_0_SIZE; + xRWAccessTaskParameters.xRegions[2].ulParameters = ((portPMP_REGION_READ_WRITE) | + (portPMP_REGION_ADDR_MATCH_NAPOT)); + + napot_addr_modifier ( xPmpInfo.granularity, + (size_t) METAL_SIFIVE_UART0_0_BASE_ADDRESS, + (size_t *) &xRWAccessTaskParameters.xRegions[2].pvBaseAddress, + xRWAccessTaskParameters.xRegions[2].ulLengthInBytes); +#endif /* METAL_SIFIVE_UART0 */ + + xRWAccessTaskParameters.puxStackBuffer = ( StackType_t * ) pvPortMalloc( xRWAccessTaskParameters.usStackDepth * sizeof( StackType_t ) ); + // xRWAccessTaskParameters.puxStackBuffer = xRWAccessTaskStack; + // printf("xRWAccessTaskParameters.puxStackBuffer = %p\r\n", xRWAccessTaskParameters.puxStackBuffer); + + xTaskCreateRestricted( &xRWAccessTaskParameters, + &xHandle_TaskRWAccess); + + // spyAppTask(); + break; + case '9': + if (xHandle_TaskROAccess != NULL) + vTaskDelete(xHandle_TaskROAccess); + if (xHandle_TaskRWAccess != NULL) + vTaskDelete(xHandle_TaskRWAccess); + if (xROAccessTaskParameters.puxStackBuffer != NULL) + vPortFree(xROAccessTaskParameters.puxStackBuffer); + if (xRWAccessTaskParameters.puxStackBuffer != NULL) + vPortFree(xRWAccessTaskParameters.puxStackBuffer); + break; +#endif + default: + break; + } + taskYIELD(); + vTaskDelay(mainQUEUE_TICK_COUNT_FOR_1S); + + } while (1); +} + + +static void ledBlinkTask( void *pvParameters ) PRIVILEGED_FUNCTION +{ + unsigned long ulCounter = 0; + + /* Remove compiler warning about unused parameter. */ + ( void ) pvParameters; + + struct metal_gpio *led0; //make instance of GPIO + + //Get gpio device handle, i.e.) define IC pin here where IC's GPIO = 5, pin silkscreen = 13 + //this is the GPIO device index that is referenced from 0, make sure to check the schematic + led0 = metal_gpio_get_device(0); + + //Pins are set when initialized so we must disable it when we use it as an input/output + metal_gpio_disable_input(led0, 5); + + //Set as gpio as output + metal_gpio_enable_output(led0, 5); + + //Pins have more than one function, make sure we disconnect anything connected... + metal_gpio_disable_pinmux(led0, 5); + + //Turn ON pin + metal_gpio_set_pin(led0, 5, 1); + + for( ;; ) + { + metal_gpio_set_pin(led0, 5, ++ulCounter&0x01); + vTaskDelay(mainQUEUE_TICK_COUNT_FOR_1S); + + } +} + +// void print_stack_pointer() { +// void* p = NULL; +// printf("%p\n", (void*)&p); +// } +/*-----------------------------------------------------------*/ +int main( void ) +{ + TaskHandle_t xHandle_ReceiveTask, xHandle_SendTask, xHandle_TaskListTasks, xHandle_TaskROAccess, xHandle_TaskRWAccess; + + // static StackType_t xTaskListTasksShowStack[ configMINIMAL_STACK_SIZE * 3] __attribute__( ( aligned( 32 ) ) ); + // static StackType_t xSendTaskStack[ configMINIMAL_STACK_SIZE ] __attribute__( ( aligned( 32 ) ) ); + // static StackType_t xReceiveTaskStack[ configMINIMAL_STACK_SIZE ] __attribute__( ( aligned( 32 ) ) ); + // static StackType_t xROAccessTaskStack[ configMINIMAL_STACK_SIZE ] __attribute__( ( aligned( 32 ) ) ); + // static StackType_t xRWAccessTaskStack[ configMINIMAL_STACK_SIZE ] __attribute__( ( aligned( 32 ) ) ); + + + const char * const pcMessage = "FreeRTOS-TEE Demo start\r\n"; + const char * const pcMessageEnd = "FreeRTOS-TEE Demo end\r\n"; + const char * const pcMessageEndError = "FreeRTOS-TEE Demo end - Error no enough PMP entry\r\n"; + const char * const pcMessageGranularityError = "FreeRTOS-TEE Demo end - Error platform granularity no supported\r\n"; + + extern uint32_t __unprivileged_data_section_start__[]; + extern uint32_t __unprivileged_data_section_end__[]; + extern uint32_t __heap_start[]; + extern uint32_t __heap_end[]; + extern unsigned long __privileged_data_start__[]; + extern unsigned long __privileged_data_end__[]; + extern unsigned long __privileged_functions_start__[]; + extern unsigned long __privileged_functions_end__[]; + extern unsigned long metal_segment_stack_begin[]; + extern unsigned long metal_segment_stack_end[]; + volatile unsigned long *pul; + + prvSetupHardware(); + + printf("\n\n\t-= mTower v" VERSION " =- " __DATE__ " " __TIME__"\r\n\n"); + + // printf("__ricsv_xlen = %d\r\n", __riscv_xlen); + + // pul = __unprivileged_data_section_start__; + // printf("__unprivileged_data_section_start__ = %p\r\n", pul); + + // pul = __unprivileged_data_section_end__; + // printf("__unprivileged_data_section_end__ = %p\r\n", pul); + + // pul = __privileged_data_start__; + // printf("__privileged_data_start__ = %p\r\n", pul); + + // pul = __privileged_data_end__; + // printf("__privileged_data_end__ = %p\r\n", pul); + + // pul = metal_segment_stack_begin; + // printf("metal_segment_stack_begin = %p\r\n", pul); + + // pul = metal_segment_stack_end; + // printf("metal_segment_stack_end = %p\r\n", pul); + + // pul = __heap_start; + // printf("__heap_start = %p\r\n", pul); + + // pul = __heap_end; + // printf("__heap_end = %p\r\n", pul); + + // pul = __privileged_functions_start__; + // printf("__privileged_functions_start__ = %p\r\n", pul); + + // pul = __privileged_functions_end__; + // printf("__privileged_functions_end__ = %p\r\n", pul); + + // printf("sizeof( StackType_t ) = %d\r\n", sizeof( StackType_t )); + // printf("portTOTAL_NUM_CFG_REG = %d\r\n", portTOTAL_NUM_CFG_REG); + + // write( STDOUT_FILENO, pcMessage, strlen( pcMessage ) ); + BaseType_t xReturned; + TaskHandle_t xHandle = NULL; + + // char *buf = pvPortMalloc(8); + // if (buf != NULL) { + // printf("pvPortMalloc: *buf = %p\r\n", buf); + // } + + // xReturned = xTaskCreate( + // vTaskCode, /* Function that implements the task. */ + // "PRVLGD", /* Text name for the task. */ + // 512, /* Stack size in words, not bytes. */ + // NULL, //( void * ) 1, /* Parameter passed into the task. */ + // mainQUEUE_SEND_TASK_PRIORITY | portPRIVILEGE_BIT,/* Priority at which the task is created. */ + // &xHandle ); /* Used to pass out the created task's handle. */ + + xTaskCreate(menuTask, /* The function that implements the task. */ + "menu", /* The text name assigned to the task - for debug only as it is not used by the kernel. */ + 768, /* The size of the stack to allocate to the task. */ + (void *)NULL, /* The parameter passed to the task - just to check the functionality. */ + mainQUEUE_SEND_TASK_PRIORITY | portPRIVILEGE_BIT, /* The priority assigned to the task. */ + NULL); + + // xTaskCreate(ledBlinkTask, /* The function that implements the task. */ + // "ledBlink", /* The text name assigned to the task - for debug only as it is not used by the kernel. */ + // 128, /* The size of the stack to allocate to the task. */ + // (void *)NULL, /* The parameter passed to the task - just to check the functionality. */ + // mainQUEUE_SEND_TASK_PRIORITY | portPRIVILEGE_BIT, /* The priority assigned to the task. */ + // NULL); + +#if( portUSING_MPU_WRAPPERS == 1 ) + if (xPmpInfo.nb_pmp < 8) + { + write( STDOUT_FILENO, pcMessageEndError, strlen( pcMessageEndError ) ); + _exit(0); + } else if (xPmpInfo.granularity > 4) { + /* + * platfrom granularity > 4 bytes is not supported yet, some + * modifications are needed on FreeRTOS port to do so. + */ + write( STDOUT_FILENO, pcMessageGranularityError, strlen( pcMessageGranularityError ) ); + _exit(0); + } + + // printf("xPmpInfo.nb_pmp = %d\r\n", xPmpInfo.nb_pmp); + + /* Create the queue. */ + xQueue = xQueueCreate( mainQUEUE_LENGTH, sizeof( uint32_t ) ); + + if( xQueue != NULL ) + { + extern uint32_t __unprivileged_data_section_start__[]; + extern uint32_t __unprivileged_data_section_end__[]; + + // static TaskParameters_t xTaskListTasksShowDefinition = + // { + // .pvTaskCode = prvListTasksShowTask, + // .pcName = "UNPRVLGD", + // .usStackDepth = 224, //configMINIMAL_STACK_SIZE, + // .pvParameters = NULL, + // .uxPriority = mainQUEUE_RECEIVE_TASK_PRIORITY, + // .puxStackBuffer = NULL, + // }; + + // static TaskParameters_t xTaskRXDefinition = + // { + // .pvTaskCode = prvQueueReceiveTask, + // .pcName = "Rx", + // .usStackDepth = configMINIMAL_STACK_SIZE, + // .pvParameters = NULL, + // .uxPriority = mainQUEUE_RECEIVE_TASK_PRIORITY, + // .puxStackBuffer = NULL, + // }; + + // static TaskParameters_t xTaskTXDefinition = + // { + // .pvTaskCode = prvQueueSendTask, + // .pcName = "Tx", + // .usStackDepth = configMINIMAL_STACK_SIZE, + // .pvParameters = NULL, + // .uxPriority = mainQUEUE_SEND_TASK_PRIORITY, + // .puxStackBuffer = NULL, + // }; + + // static TaskParameters_t xROAccessTaskParameters = + // { + // .pvTaskCode = prvROAccessTask, + // .pcName = "ROAccess", + // .usStackDepth = 224, //configMINIMAL_STACK_SIZE, + // .pvParameters = NULL, + // .uxPriority = mainQUEUE_SEND_TASK_PRIORITY, + // .puxStackBuffer = NULL, + // // .puxStackBuffer = xROAccessTaskStack, + // }; + + // static TaskParameters_t xRWAccessTaskParameters = + // { + // .pvTaskCode = prvRWAccessTask, + // .pcName = "RWAccess", + // .usStackDepth = 128, //configMINIMAL_STACK_SIZE, + // .pvParameters = NULL, + // .uxPriority = mainQUEUE_SEND_TASK_PRIORITY, + // .puxStackBuffer = NULL, + // // .puxStackBuffer = xRWAccessTaskStack, + // }; + + // printf("xROAccessTaskParameters.pcName = %s\r\n", xROAccessTaskParameters.pcName); + // printf("xTaskTXDefinition.pcName = %s\r\n", xTaskTXDefinition.pcName); + + if(0 == xPmpInfo.granularity) + { + init_pmp (&xPmpInfo); + } + // printf("xPmpInfo.granularity = %d\r\n", xPmpInfo.granularity); +// /* +// * Prepare xRegions for Receive Task +// */ +// memset(&xTaskRXDefinition.xRegions, 0, sizeof(xTaskRXDefinition.xRegions)); + +// // authorize access to data and bss +// // Low address +// xTaskRXDefinition.xRegions[0].ulLengthInBytes = 4; +// xTaskRXDefinition.xRegions[0].ulParameters = ((portPMP_REGION_READ_WRITE) | +// (portPMP_REGION_ADDR_MATCH_NA4)); +// addr_modifier ( xPmpInfo.granularity, +// ( size_t ) __unprivileged_data_section_start__, +// (size_t *) &xTaskRXDefinition.xRegions[0].pvBaseAddress); +// // printf("xTaskRXDefinition.xRegions[0].pvBaseAddress = %p\r\n", xTaskRXDefinition.xRegions[0].pvBaseAddress); +// // High address +// xTaskRXDefinition.xRegions[1].ulLengthInBytes = 4; +// xTaskRXDefinition.xRegions[1].ulParameters = ((portPMP_REGION_READ_WRITE) | +// (portPMP_REGION_ADDR_MATCH_TOR)); + +// addr_modifier ( xPmpInfo.granularity, +// ( size_t ) __unprivileged_data_section_end__, +// (size_t *) &xTaskRXDefinition.xRegions[1].pvBaseAddress); +// // printf("xTaskRXDefinition.xRegions[1].pvBaseAddress = %p\r\n", xTaskRXDefinition.xRegions[1].pvBaseAddress); + +// #ifdef METAL_SIFIVE_UART0 +// // allow access to UART peripheral +// xTaskRXDefinition.xRegions[2].ulLengthInBytes = METAL_SIFIVE_UART0_0_SIZE; +// xTaskRXDefinition.xRegions[2].ulParameters = ((portPMP_REGION_READ_WRITE) | +// (portPMP_REGION_ADDR_MATCH_NAPOT)); + +// napot_addr_modifier ( xPmpInfo.granularity, +// (size_t) METAL_SIFIVE_UART0_0_BASE_ADDRESS, +// (size_t *) &xTaskRXDefinition.xRegions[2].pvBaseAddress, +// xTaskRXDefinition.xRegions[2].ulLengthInBytes); +// #endif /* METAL_SIFIVE_UART0 */ + +// // allocate stack (It will take 2 PMP Slot - So it is not needed to put align the StackBuffer) +// xTaskRXDefinition.puxStackBuffer = ( StackType_t * ) pvPortMalloc( xTaskRXDefinition.usStackDepth * sizeof( StackType_t ) ); +// // xTaskRXDefinition.puxStackBuffer = xReceiveTaskStack; +// // portDISABLE_INTERRUPTS(); +// printf("xTaskRXDefinition.puxStackBuffer = %p\r\n", xTaskRXDefinition.puxStackBuffer); +// // portENABLE_INTERRUPTS(); +// xTaskCreateRestricted( &xTaskRXDefinition, +// &xHandle_ReceiveTask); + +// /* +// * Prepare xRegions for Send Task +// */ +// memset(&xTaskTXDefinition.xRegions, 0, sizeof(xTaskTXDefinition.xRegions)); + +// // authorize access to data and bss +// // Low address +// xTaskTXDefinition.xRegions[0].ulLengthInBytes = 4; +// xTaskTXDefinition.xRegions[0].ulParameters = ((portPMP_REGION_READ_WRITE) | +// (portPMP_REGION_ADDR_MATCH_NA4)); +// addr_modifier ( xPmpInfo.granularity, +// ( size_t ) __unprivileged_data_section_start__, +// (size_t *) &xTaskTXDefinition.xRegions[0].pvBaseAddress); + +// // High address +// xTaskTXDefinition.xRegions[1].ulLengthInBytes = 4; +// xTaskTXDefinition.xRegions[1].ulParameters = ((portPMP_REGION_READ_WRITE) | +// (portPMP_REGION_ADDR_MATCH_TOR)); + +// addr_modifier ( xPmpInfo.granularity, +// ( size_t ) __unprivileged_data_section_end__, +// (size_t *) &xTaskTXDefinition.xRegions[1].pvBaseAddress); + +// #ifdef METAL_SIFIVE_GPIO0 +// // allow access to GPIO (Each peripheral are on 4Kb mapping area) +// xTaskTXDefinition.xRegions[2].ulLengthInBytes = METAL_SIFIVE_GPIO0_0_SIZE; +// xTaskTXDefinition.xRegions[2].ulParameters = ((portPMP_REGION_READ_WRITE) | +// (portPMP_REGION_ADDR_MATCH_NAPOT)); + +// napot_addr_modifier ( xPmpInfo.granularity, +// (size_t) METAL_SIFIVE_GPIO0_0_BASE_ADDRESS, +// (size_t *) &xTaskTXDefinition.xRegions[2].pvBaseAddress, +// xTaskTXDefinition.xRegions[2].ulLengthInBytes); +// #endif /* METAL_SIFIVE_GPIO0 */ + +// // #ifdef METAL_SIFIVE_UART0 +// // // allow access to UART peripheral +// // xTaskTXDefinition.xRegions[2].ulLengthInBytes = METAL_SIFIVE_UART0_0_SIZE; +// // xTaskTXDefinition.xRegions[2].ulParameters = ((portPMP_REGION_READ_WRITE) | +// // (portPMP_REGION_ADDR_MATCH_NAPOT)); + +// // napot_addr_modifier ( xPmpInfo.granularity, +// // (size_t) METAL_SIFIVE_UART0_0_BASE_ADDRESS, +// // (size_t *) &xTaskTXDefinition.xRegions[2].pvBaseAddress, +// // xTaskTXDefinition.xRegions[2].ulLengthInBytes); +// // #endif /* METAL_SIFIVE_UART0 */ +// // allocate stack (It will take 2 PMP Slot - So it is not needed to put align the StackBuffer) +// xTaskTXDefinition.puxStackBuffer = ( StackType_t * ) pvPortMalloc( xTaskTXDefinition.usStackDepth * sizeof( StackType_t ) ); +// // xTaskTXDefinition.puxStackBuffer = xSendTaskStack; + +// printf("xTaskTXDefinition.puxStackBuffer = %p\r\n", xTaskTXDefinition.puxStackBuffer); + +// xTaskCreateRestricted( &xTaskTXDefinition, +// &xHandle_SendTask); + + /* + * Prepare xRegions for ListTasksShow Task + */ +// memset(&xTaskListTasksShowDefinition.xRegions, 0, sizeof(xTaskListTasksShowDefinition.xRegions)); + +// xTaskListTasksShowDefinition.puxStackBuffer = ( StackType_t * ) pvPortMalloc( xTaskListTasksShowDefinition.usStackDepth * sizeof( StackType_t ) ); + +// // xTaskListTasksShowDefinition.puxStackBuffer = xTaskListTasksShowStack; + +// xTaskListTasksShowDefinition.xRegions[0].ulLengthInBytes = xTaskListTasksShowDefinition.usStackDepth * sizeof( StackType_t ); +// xTaskListTasksShowDefinition.xRegions[0].ulParameters = ((portPMP_REGION_READ_WRITE) | +// (portPMP_REGION_ADDR_MATCH_NAPOT)); + +// napot_addr_modifier ( xPmpInfo.granularity, +// (size_t) xTaskListTasksShowDefinition.puxStackBuffer, +// (size_t *) &xTaskListTasksShowDefinition.xRegions[0].pvBaseAddress, +// xTaskListTasksShowDefinition.xRegions[0].ulLengthInBytes); + + +// // authorize access to data and bss +// // Low address +// // xTaskListTasksShowDefinition.xRegions[0].ulLengthInBytes = 4; +// // xTaskListTasksShowDefinition.xRegions[0].ulParameters = ((portPMP_REGION_READ_WRITE) | +// // (portPMP_REGION_ADDR_MATCH_NA4)); +// // addr_modifier ( xPmpInfo.granularity, +// // ( size_t ) __unprivileged_data_section_start__, +// // (size_t *) &xTaskListTasksShowDefinition.xRegions[0].pvBaseAddress); + +// // High address +// xTaskListTasksShowDefinition.xRegions[1].ulLengthInBytes = 4; +// xTaskListTasksShowDefinition.xRegions[1].ulParameters = ((portPMP_REGION_READ_WRITE) | +// (portPMP_REGION_ADDR_MATCH_TOR)); + +// addr_modifier ( xPmpInfo.granularity, +// ( size_t ) __unprivileged_data_section_end__, +// (size_t *) &xTaskListTasksShowDefinition.xRegions[1].pvBaseAddress); + +// #ifdef METAL_SIFIVE_UART0 +// // allow access to UART peripheral +// xTaskListTasksShowDefinition.xRegions[2].ulLengthInBytes = METAL_SIFIVE_UART0_0_SIZE; +// xTaskListTasksShowDefinition.xRegions[2].ulParameters = ((portPMP_REGION_READ_WRITE) | +// (portPMP_REGION_ADDR_MATCH_NAPOT)); + +// napot_addr_modifier ( xPmpInfo.granularity, +// (size_t) METAL_SIFIVE_UART0_0_BASE_ADDRESS, +// (size_t *) &xTaskListTasksShowDefinition.xRegions[2].pvBaseAddress, +// xTaskListTasksShowDefinition.xRegions[2].ulLengthInBytes); +// #endif /* METAL_SIFIVE_UART0 */ + +// // xTaskListTasksShowDefinition.puxStackBuffer = ( StackType_t * ) pvPortMalloc( xTaskListTasksShowDefinition.usStackDepth * sizeof( StackType_t ) ); +// // // xTaskListTasksShowDefinition.puxStackBuffer = xTaskListTasksShowStack; + +// printf("xTaskListTasksShowDefinition.puxStackBuffer = %p\r\n", xTaskListTasksShowDefinition.puxStackBuffer); +// // // printf("Test = "); +// // // print_stack_pointer(); +// xTaskCreateRestricted( &xTaskListTasksShowDefinition, +// &xHandle_TaskListTasks); + + /* Start the tasks and timer running. */ + vTaskStartScheduler(); + + /* If all is well, the scheduler will now be running, and the following + line will never be reached. If the following line does execute, then + there was insufficient FreeRTOS heap memory available for the Idle and/or + timer tasks to be created. + or task have stoppped the Scheduler */ + + // vTaskDelete( xHandle_SendTask ); + // vTaskDelete( xHandle_ReceiveTask ); + } +#endif /* ( portUSING_MPU_WRAPPERS == 1 ) */ +} + +/*-----------------------------------------------------------*/ + +static void prvQueueSendTask( void *pvParameters ) +{ + TickType_t xNextWakeTime; + BaseType_t xReturned; + unsigned long ulCounter = 0; + + /* Remove compiler warning about unused parameter. */ + ( void ) pvParameters; + ( void ) xReturned; + + /* Initialise xNextWakeTime - this only needs to be done once. */ + xNextWakeTime = xTaskGetTickCount(); + + for( ; ; ) + { + printf("[ Tx ] : %d\r\n", ++ulCounter ); + + /* Place this task in the blocked state until it is time to run again. */ + vTaskDelayUntil( &xNextWakeTime, mainQUEUE_TICK_COUNT_FOR_1S); + // vTaskDelay( mainQUEUE_TICK_COUNT_FOR_1S * 500); + + + /* Send to the queue - causing the queue receive task to unblock and + toggle the LED. 0 is used as the block time so the sending operation + will not block - it shouldn't need to block as the queue should always + be empty at this point in the code. */ + xReturned = xQueueSend( xQueue, &ulCounter, 0U ); + configASSERT( xReturned == pdPASS ); + } + + /** + * SiFive CI/CD need to have a exit(0) status to pass + */ +#if( portUSING_MPU_WRAPPERS == 1 ) + /* We run into user mode, so need to be machine mode before to call vTaskEndScheduler */ + // xPortRaisePrivilege(); +#endif /* ( portUSING_MPU_WRAPPERS == 1 ) */ + // vTaskEndScheduler(); +} +/*-----------------------------------------------------------*/ + +static void prvQueueReceiveTask( void *pvParameters ) +{ + unsigned long ulReceivedValue; + + /* Remove compiler warning about unused parameter. */ + ( void ) pvParameters; + + for( ;; ) + { + /* Wait until something arrives in the queue - this task will block + indefinitely provided INCLUDE_vTaskSuspend is set to 1 in + FreeRTOSConfig.h. */ + xQueueReceive( xQueue, &ulReceivedValue, portMAX_DELAY ); + + /* To get here something must have been received from the queue, but + is it the expected value? If it is, toggle the LED. */ + + printf("[ Rx ] : %d\r\n", ulReceivedValue ); + } +} +/*-----------------------------------------------------------*/ + +static void prvSetupHardware( void ) +{ + // const char * const pcWarningMsg = "At least one of LEDs is null.\r\n"; + + // // This demo will toggle LEDs colors so we define them here + // led0_red = metal_led_get_rgb("LD0", "red"); + // led0_green = metal_led_get_rgb("LD0", "green"); + // led0_blue = metal_led_get_rgb("LD0", "blue"); + // if ((led0_red == NULL) || (led0_green == NULL) || (led0_blue == NULL)) + // { + // write( STDOUT_FILENO, pcWarningMsg, strlen( pcWarningMsg ) ); + // } + // else + // { + // // Enable each LED + // metal_led_enable(led0_red); + // metal_led_enable(led0_green); + // metal_led_enable(led0_blue); + + // // All Off + // metal_led_on(led0_red); + // metal_led_on(led0_green); + // metal_led_on(led0_blue); + // } +} +/*-----------------------------------------------------------*/ + + +void vApplicationMallocFailedHook( void ) +{ + /* vApplicationMallocFailedHook() will only be called if + configUSE_MALLOC_FAILED_HOOK is set to 1 in FreeRTOSConfig.h. It is a hook + function that will get called if a call to pvPortMalloc() fails. + pvPortMalloc() is called internally by the kernel whenever a task, queue, + timer or semaphore is created. It is also called by various parts of the + demo application. If heap_1.c or heap_2.c are used, then the size of the + heap available to pvPortMalloc() is defined by configTOTAL_HEAP_SIZE in + FreeRTOSConfig.h, and the xPortGetFreeHeapSize() API function can be used + to query the size of free heap space that remains (although it does not + provide information on how the remaining heap might be fragmented). */ + + const char * const pcErrorMsg = "ERROR malloc \r\n"; + + taskDISABLE_INTERRUPTS(); + +#if( portUSING_MPU_WRAPPERS == 1 ) + /* need to be machine mode */ + xPortRaisePrivilege(); +#endif /* ( portUSING_MPU_WRAPPERS == 1 ) */ + write( STDOUT_FILENO, pcErrorMsg, strlen(pcErrorMsg) ); + + if ( led0_red != NULL ) + { + // Red light on + metal_led_off(led0_red); + } + + _exit(1); +} +/*-----------------------------------------------------------*/ + +void vApplicationIdleHook( void ) +{ + /* vApplicationIdleHook() will only be called if configUSE_IDLE_HOOK is set + to 1 in FreeRTOSConfig.h. It will be called on each iteration of the idle + task. It is essential that code added to this hook function never attempts + to block in any way (for example, call xQueueReceive() with a block time + specified, or call vTaskDelay()). If the application makes use of the + vTaskDelete() API function (as this demo application does) then it is also + important that vApplicationIdleHook() is permitted to return to its calling + function, because it is the responsibility of the idle task to clean up + memory allocated by the kernel to any task that has since been deleted. */ +} +/*-----------------------------------------------------------*/ + +void vApplicationStackOverflowHook( TaskHandle_t pxTask, char *pcTaskName ) +{ + ( void ) pcTaskName; + ( void ) pxTask; + + /* Run time stack overflow checking is performed if + configCHECK_FOR_STACK_OVERFLOW is defined to 1 or 2. This hook + function is called if a stack overflow is detected. */ + taskDISABLE_INTERRUPTS(); + + write( STDOUT_FILENO, "ERROR Stack overflow on func: ", 30 ); + write( STDOUT_FILENO, pcTaskName, strlen( pcTaskName ) ); + write( STDOUT_FILENO, "\r\n", 3 ); + + // if ( led0_red != NULL ) + // { + // // Red light on + // metal_led_off(led0_red); + // } + + _exit(1); +} +/*-----------------------------------------------------------*/ + +void vApplicationTickHook( void ) +{ + /* The tests in the full demo expect some interaction with interrupts. */ +} +/*-----------------------------------------------------------*/ + +void vAssertCalled( void ) +{ + taskDISABLE_INTERRUPTS(); + + if ( led0_red != NULL ) + { + // Red light on + metal_led_off(led0_red); + } + + _exit(1); +} diff --git a/arch/riscv32/fe310/src/sparkfun_redboard/nonsecure/metal-platform.h b/arch/riscv32/fe310/src/sparkfun_redboard/nonsecure/metal-platform.h new file mode 100644 index 00000000..6b97e212 --- /dev/null +++ b/arch/riscv32/fe310/src/sparkfun_redboard/nonsecure/metal-platform.h @@ -0,0 +1,302 @@ +/* Copyright 2019 SiFive, Inc */ +/* SPDX-License-Identifier: Apache-2.0 */ +/* ----------------------------------- */ +/* ----------------------------------- */ + +#ifndef METAL_PLATFORM_H +#define METAL_PLATFORM_H + +/* From clock@0 */ +#define METAL_FIXED_CLOCK_0_CLOCK_0_CLOCK_FREQUENCY 16000000UL + +/* From clock@2 */ +#define METAL_FIXED_CLOCK_2_CLOCK_2_CLOCK_FREQUENCY 72000000UL + +/* From clock@5 */ +#define METAL_FIXED_CLOCK_5_CLOCK_5_CLOCK_FREQUENCY 32768UL + +/* From clock@6 */ +#define METAL_FIXED_CLOCK_6_CLOCK_6_CLOCK_FREQUENCY 32768UL + +#define METAL_FIXED_CLOCK + +/* From clint@2000000 */ +#define METAL_RISCV_CLINT0_2000000_BASE_ADDRESS 33554432UL +#define METAL_RISCV_CLINT0_0_BASE_ADDRESS 33554432UL +#define METAL_RISCV_CLINT0_2000000_SIZE 65536UL +#define METAL_RISCV_CLINT0_0_SIZE 65536UL + +#define METAL_RISCV_CLINT0 +#define METAL_RISCV_CLINT0_MSIP_BASE 0UL +#define METAL_RISCV_CLINT0_MTIMECMP_BASE 16384UL +#define METAL_RISCV_CLINT0_MTIME 49144UL + +/* From interrupt_controller@c000000 */ +#define METAL_RISCV_PLIC0_C000000_BASE_ADDRESS 201326592UL +#define METAL_RISCV_PLIC0_0_BASE_ADDRESS 201326592UL +#define METAL_RISCV_PLIC0_C000000_SIZE 67108864UL +#define METAL_RISCV_PLIC0_0_SIZE 67108864UL +#define METAL_RISCV_PLIC0_C000000_RISCV_MAX_PRIORITY 7UL +#define METAL_RISCV_PLIC0_0_RISCV_MAX_PRIORITY 7UL +#define METAL_RISCV_PLIC0_C000000_RISCV_NDEV 53UL +#define METAL_RISCV_PLIC0_0_RISCV_NDEV 53UL + +#define METAL_RISCV_PLIC0 +#define METAL_RISCV_PLIC0_PRIORITY_BASE 0UL +#define METAL_RISCV_PLIC0_PENDING_BASE 4096UL +#define METAL_RISCV_PLIC0_ENABLE_BASE 8192UL +#define METAL_RISCV_PLIC0_ENABLE_PER_HART 128UL +#define METAL_RISCV_PLIC0_CONTEXT_BASE 2097152UL +#define METAL_RISCV_PLIC0_CONTEXT_PER_HART 4096UL +#define METAL_RISCV_PLIC0_CONTEXT_THRESHOLD 0UL +#define METAL_RISCV_PLIC0_CONTEXT_CLAIM 4UL + +/* From aon@10000000 */ +#define METAL_SIFIVE_AON0_10000000_BASE_ADDRESS 268435456UL +#define METAL_SIFIVE_AON0_0_BASE_ADDRESS 268435456UL +#define METAL_SIFIVE_AON0_10000000_SIZE 32768UL +#define METAL_SIFIVE_AON0_0_SIZE 32768UL + +#define METAL_SIFIVE_AON0 +#define METAL_SIFIVE_AON0_WDOGCFG 0UL +#define METAL_SIFIVE_AON0_WDOGCOUNT 8UL +#define METAL_SIFIVE_AON0_WDOGS 16UL +#define METAL_SIFIVE_AON0_WDOGFEED 24UL +#define METAL_SIFIVE_AON0_WDOGKEY 28UL +#define METAL_SIFIVE_AON0_WDOGCMP 32UL +#define METAL_SIFIVE_AON0_RTCCFG 64UL +#define METAL_SIFIVE_AON0_RTCLO 72UL +#define METAL_SIFIVE_AON0_RTCHI 72UL +#define METAL_SIFIVE_AON0_RTCS 80UL +#define METAL_SIFIVE_AON0_RTCCMP 96UL +#define METAL_SIFIVE_AON0_LFROSCCFG 112UL +#define METAL_SIFIVE_AON0_BACKUP0 128UL +#define METAL_SIFIVE_AON0_BACKUP1 132UL +#define METAL_SIFIVE_AON0_BACKUP2 136UL +#define METAL_SIFIVE_AON0_BACKUP3 140UL +#define METAL_SIFIVE_AON0_BACKUP4 144UL +#define METAL_SIFIVE_AON0_BACKUP5 148UL +#define METAL_SIFIVE_AON0_BACKUP6 152UL +#define METAL_SIFIVE_AON0_BACKUP7 152UL +#define METAL_SIFIVE_AON0_BACKUP8 160UL +#define METAL_SIFIVE_AON0_BACKUP9 164UL +#define METAL_SIFIVE_AON0_BACKUP10 168UL +#define METAL_SIFIVE_AON0_BACKUP11 172UL +#define METAL_SIFIVE_AON0_BACKUP12 176UL +#define METAL_SIFIVE_AON0_BACKUP13 180UL +#define METAL_SIFIVE_AON0_BACKUP14 184UL +#define METAL_SIFIVE_AON0_BACKUP15 188UL +#define METAL_SIFIVE_AON0_BACKUP16 192UL +#define METAL_SIFIVE_AON0_BACKUP17 196UL +#define METAL_SIFIVE_AON0_BACKUP18 200UL +#define METAL_SIFIVE_AON0_BACKUP19 204UL +#define METAL_SIFIVE_AON0_BACKUP20 208UL +#define METAL_SIFIVE_AON0_BACKUP21 212UL +#define METAL_SIFIVE_AON0_BACKUP22 216UL +#define METAL_SIFIVE_AON0_BACKUP23 220UL +#define METAL_SIFIVE_AON0_BACKUP24 224UL +#define METAL_SIFIVE_AON0_BACKUP25 228UL +#define METAL_SIFIVE_AON0_BACKUP26 232UL +#define METAL_SIFIVE_AON0_BACKUP27 236UL +#define METAL_SIFIVE_AON0_BACKUP28 240UL +#define METAL_SIFIVE_AON0_BACKUP29 244UL +#define METAL_SIFIVE_AON0_BACKUP30 248UL +#define METAL_SIFIVE_AON0_BACKUP31 252UL +#define METAL_SIFIVE_AON0_PMU_WAKEUP_BASE 256UL +#define METAL_SIFIVE_AON0_PWM_SLEEP_BASE 288UL +#define METAL_SIFIVE_AON0_PMUIE 320UL +#define METAL_SIFIVE_AON0_PMUCAUSE 324UL +#define METAL_SIFIVE_AON0_PMUSLEEP 328UL +#define METAL_SIFIVE_AON0_PMUKEY 332UL + +/* From clock@3 */ + +#define METAL_SIFIVE_FE310_G000_HFROSC + +/* From clock@1 */ + +#define METAL_SIFIVE_FE310_G000_HFXOSC + +/* From clock@7 */ + +#define METAL_SIFIVE_FE310_G000_LFROSC + +/* From prci@10008000 */ +#define METAL_SIFIVE_FE310_G000_PRCI_10008000_BASE_ADDRESS 268468224UL +#define METAL_SIFIVE_FE310_G000_PRCI_0_BASE_ADDRESS 268468224UL +#define METAL_SIFIVE_FE310_G000_PRCI_10008000_SIZE 32768UL +#define METAL_SIFIVE_FE310_G000_PRCI_0_SIZE 32768UL + +#define METAL_SIFIVE_FE310_G000_PRCI +#define METAL_SIFIVE_FE310_G000_PRCI_HFROSCCFG 0UL +#define METAL_SIFIVE_FE310_G000_PRCI_HFXOSCCFG 4UL +#define METAL_SIFIVE_FE310_G000_PRCI_PLLCFG 8UL +#define METAL_SIFIVE_FE310_G000_PRCI_PLLOUTDIV 12UL + +/* From clock@4 */ +#define METAL_SIFIVE_FE310_G000_PLL_4_CLOCK_FREQUENCY 16000000UL + +#define METAL_SIFIVE_FE310_G000_PLL + +/* From gpio@10012000 */ +#define METAL_SIFIVE_GPIO0_10012000_BASE_ADDRESS 268509184UL +#define METAL_SIFIVE_GPIO0_0_BASE_ADDRESS 268509184UL +#define METAL_SIFIVE_GPIO0_10012000_SIZE 4096UL +#define METAL_SIFIVE_GPIO0_0_SIZE 4096UL + +#define METAL_SIFIVE_GPIO0 +#define METAL_SIFIVE_GPIO0_VALUE 0UL +#define METAL_SIFIVE_GPIO0_INPUT_EN 4UL +#define METAL_SIFIVE_GPIO0_OUTPUT_EN 8UL +#define METAL_SIFIVE_GPIO0_PORT 12UL +#define METAL_SIFIVE_GPIO0_PUE 16UL +#define METAL_SIFIVE_GPIO0_DS 20UL +#define METAL_SIFIVE_GPIO0_RISE_IE 24UL +#define METAL_SIFIVE_GPIO0_RISE_IP 28UL +#define METAL_SIFIVE_GPIO0_FALL_IE 32UL +#define METAL_SIFIVE_GPIO0_FALL_IP 36UL +#define METAL_SIFIVE_GPIO0_HIGH_IE 40UL +#define METAL_SIFIVE_GPIO0_HIGH_IP 44UL +#define METAL_SIFIVE_GPIO0_LOW_IE 48UL +#define METAL_SIFIVE_GPIO0_LOW_IP 52UL +#define METAL_SIFIVE_GPIO0_IOF_EN 56UL +#define METAL_SIFIVE_GPIO0_IOF_SEL 60UL +#define METAL_SIFIVE_GPIO0_OUT_XOR 64UL + +/* From led@0 */ + +/* From led@1 */ + +/* From led@2 */ + +#define METAL_SIFIVE_GPIO_LEDS + +/* From i2c@10016000 */ +#define METAL_SIFIVE_I2C0_10016000_BASE_ADDRESS 268525568UL +#define METAL_SIFIVE_I2C0_0_BASE_ADDRESS 268525568UL +#define METAL_SIFIVE_I2C0_10016000_SIZE 4096UL +#define METAL_SIFIVE_I2C0_0_SIZE 4096UL + +#define METAL_SIFIVE_I2C0 +#define METAL_SIFIVE_I2C0_PRESCALE_LOW 0UL +#define METAL_SIFIVE_I2C0_PRESCALE_HIGH 4UL +#define METAL_SIFIVE_I2C0_CONTROL 8UL +#define METAL_SIFIVE_I2C0_TRANSMIT 12UL +#define METAL_SIFIVE_I2C0_RECEIVE 12UL +#define METAL_SIFIVE_I2C0_COMMAND 16UL +#define METAL_SIFIVE_I2C0_STATUS 16UL + +/* From pwm@10015000 */ +#define METAL_SIFIVE_PWM0_10015000_BASE_ADDRESS 268521472UL +#define METAL_SIFIVE_PWM0_0_BASE_ADDRESS 268521472UL +#define METAL_SIFIVE_PWM0_10015000_SIZE 4096UL +#define METAL_SIFIVE_PWM0_0_SIZE 4096UL + +/* From pwm@10025000 */ +#define METAL_SIFIVE_PWM0_10025000_BASE_ADDRESS 268587008UL +#define METAL_SIFIVE_PWM0_1_BASE_ADDRESS 268587008UL +#define METAL_SIFIVE_PWM0_10025000_SIZE 4096UL +#define METAL_SIFIVE_PWM0_1_SIZE 4096UL + +/* From pwm@10035000 */ +#define METAL_SIFIVE_PWM0_10035000_BASE_ADDRESS 268652544UL +#define METAL_SIFIVE_PWM0_2_BASE_ADDRESS 268652544UL +#define METAL_SIFIVE_PWM0_10035000_SIZE 4096UL +#define METAL_SIFIVE_PWM0_2_SIZE 4096UL + +#define METAL_SIFIVE_PWM0 +#define METAL_SIFIVE_PWM0_PWMCFG 0UL +#define METAL_SIFIVE_PWM0_PWMCOUNT 8UL +#define METAL_SIFIVE_PWM0_PWMS 16UL +#define METAL_SIFIVE_PWM0_PWMCMP0 32UL +#define METAL_SIFIVE_PWM0_PWMCMP1 36UL +#define METAL_SIFIVE_PWM0_PWMCMP2 40UL +#define METAL_SIFIVE_PWM0_PWMCMP3 44UL + +/* From aon@10000000 */ +#define METAL_SIFIVE_AON0_10000000_BASE_ADDRESS 268435456UL +#define METAL_SIFIVE_AON0_0_BASE_ADDRESS 268435456UL +#define METAL_SIFIVE_AON0_10000000_SIZE 32768UL +#define METAL_SIFIVE_AON0_0_SIZE 32768UL + +#define METAL_SIFIVE_RTC0 +#define METAL_SIFIVE_RTC0_RTCCFG 64UL +#define METAL_SIFIVE_RTC0_RTCCOUNTLO 72UL +#define METAL_SIFIVE_RTC0_RTCCOUNTHI 76UL +#define METAL_SIFIVE_RTC0_RTCS 80UL +#define METAL_SIFIVE_RTC0_RTCCMP0 96UL + +/* From spi@10014000 */ +#define METAL_SIFIVE_SPI0_10014000_BASE_ADDRESS 268517376UL +#define METAL_SIFIVE_SPI0_0_BASE_ADDRESS 268517376UL +#define METAL_SIFIVE_SPI0_10014000_SIZE 4096UL +#define METAL_SIFIVE_SPI0_0_SIZE 4096UL + +/* From spi@10024000 */ +#define METAL_SIFIVE_SPI0_10024000_BASE_ADDRESS 268582912UL +#define METAL_SIFIVE_SPI0_1_BASE_ADDRESS 268582912UL +#define METAL_SIFIVE_SPI0_10024000_SIZE 4096UL +#define METAL_SIFIVE_SPI0_1_SIZE 4096UL + +/* From spi@10034000 */ +#define METAL_SIFIVE_SPI0_10034000_BASE_ADDRESS 268648448UL +#define METAL_SIFIVE_SPI0_2_BASE_ADDRESS 268648448UL +#define METAL_SIFIVE_SPI0_10034000_SIZE 4096UL +#define METAL_SIFIVE_SPI0_2_SIZE 4096UL + +#define METAL_SIFIVE_SPI0 +#define METAL_SIFIVE_SPI0_SCKDIV 0UL +#define METAL_SIFIVE_SPI0_SCKMODE 4UL +#define METAL_SIFIVE_SPI0_CSID 16UL +#define METAL_SIFIVE_SPI0_CSDEF 20UL +#define METAL_SIFIVE_SPI0_CSMODE 24UL +#define METAL_SIFIVE_SPI0_DELAY0 40UL +#define METAL_SIFIVE_SPI0_DELAY1 44UL +#define METAL_SIFIVE_SPI0_FMT 64UL +#define METAL_SIFIVE_SPI0_TXDATA 72UL +#define METAL_SIFIVE_SPI0_RXDATA 76UL +#define METAL_SIFIVE_SPI0_TXMARK 80UL +#define METAL_SIFIVE_SPI0_RXMARK 84UL +#define METAL_SIFIVE_SPI0_FCTRL 96UL +#define METAL_SIFIVE_SPI0_FFMT 100UL +#define METAL_SIFIVE_SPI0_IE 112UL +#define METAL_SIFIVE_SPI0_IP 116UL + +/* From serial@10013000 */ +#define METAL_SIFIVE_UART0_10013000_BASE_ADDRESS 268513280UL +#define METAL_SIFIVE_UART0_0_BASE_ADDRESS 268513280UL +#define METAL_SIFIVE_UART0_10013000_SIZE 4096UL +#define METAL_SIFIVE_UART0_0_SIZE 4096UL + +/* From serial@10023000 */ +#define METAL_SIFIVE_UART0_10023000_BASE_ADDRESS 268578816UL +#define METAL_SIFIVE_UART0_1_BASE_ADDRESS 268578816UL +#define METAL_SIFIVE_UART0_10023000_SIZE 4096UL +#define METAL_SIFIVE_UART0_1_SIZE 4096UL + +#define METAL_SIFIVE_UART0 +#define METAL_SIFIVE_UART0_TXDATA 0UL +#define METAL_SIFIVE_UART0_RXDATA 4UL +#define METAL_SIFIVE_UART0_TXCTRL 8UL +#define METAL_SIFIVE_UART0_RXCTRL 12UL +#define METAL_SIFIVE_UART0_IE 16UL +#define METAL_SIFIVE_UART0_IP 20UL +#define METAL_SIFIVE_UART0_DIV 24UL + +/* From aon@10000000 */ +#define METAL_SIFIVE_AON0_10000000_BASE_ADDRESS 268435456UL +#define METAL_SIFIVE_AON0_0_BASE_ADDRESS 268435456UL +#define METAL_SIFIVE_AON0_10000000_SIZE 32768UL +#define METAL_SIFIVE_AON0_0_SIZE 32768UL + +#define METAL_SIFIVE_WDOG0 +#define METAL_SIFIVE_WDOG0_MAGIC_KEY 5370206UL +#define METAL_SIFIVE_WDOG0_MAGIC_FOOD 218755085UL +#define METAL_SIFIVE_WDOG0_WDOGCFG 0UL +#define METAL_SIFIVE_WDOG0_WDOGCOUNT 8UL +#define METAL_SIFIVE_WDOG0_WDOGS 16UL +#define METAL_SIFIVE_WDOG0_WDOGFEED 24UL +#define METAL_SIFIVE_WDOG0_WDOGKEY 28UL +#define METAL_SIFIVE_WDOG0_WDOGCMP 32UL + +#endif /* METAL_PLATFORM_H*/ diff --git a/arch/riscv32/fe310/src/sparkfun_redboard/nonsecure/metal.freertos.lds b/arch/riscv32/fe310/src/sparkfun_redboard/nonsecure/metal.freertos.lds new file mode 100644 index 00000000..d70062c1 --- /dev/null +++ b/arch/riscv32/fe310/src/sparkfun_redboard/nonsecure/metal.freertos.lds @@ -0,0 +1,330 @@ +/* Copyright (c) 2020 SiFive Inc. */ +/* SPDX-License-Identifier: Apache-2.0 */ +OUTPUT_ARCH("riscv") + +/* Privileged mode Linker Script + * + * This linker script is based on metal.default.lds. It introduce specific + * section to isolate (acessible only from machine mode) and others that can be + * used in every execution mode. This linker script it tailored for FreeRTOS + * applications. + */ + +ENTRY(_enter) + +MEMORY +{ + itim (airwx) : ORIGIN = 0x8000000, LENGTH = 0x2000 + ram (arw!xi) : ORIGIN = 0x80000000, LENGTH = 0x4000 + rom (irx!wa) : ORIGIN = 0x20010000, LENGTH = 0x6a120 +} + +PHDRS +{ + rom PT_LOAD; + ram_init PT_LOAD; + tls PT_TLS; + ram PT_LOAD; + itim_init PT_LOAD; + text PT_LOAD; + lim_init PT_LOAD; +} + +SECTIONS +{ + /* Each hart is allocated its own stack of size __stack_size. This value + * can be overriden at build-time by adding the following to CFLAGS: + * + * -Xlinker --defsym=__stack_size=0xf00 + * + * where 0xf00 can be replaced with a multiple of 16 of your choice. + * + * __stack_size is PROVIDE-ed as a symbol so that initialization code + * initializes the stack pointers for each hart at the right offset from + * the _sp symbol. + */ + __stack_size = DEFINED(__stack_size) ? __stack_size : 0x400; + PROVIDE(__stack_size = __stack_size); + + /* The size of the heap can be overriden at build-time by adding the + * following to CFLAGS: + * + * -Xlinker --defsym=__heap_size=0xf00 + * + * where 0xf00 can be replaced with the value of your choice. + * + * Altertatively, the heap can be grown to fill the entire remaining region + * of RAM by adding the following to CFLAGS: + * + * -Xlinker --defsym=__heap_max=1 + * + * Note that depending on the memory layout, the bitness (32/64bit) of the + * target, and the code model in use, this might cause a relocation error. + */ + __heap_size = DEFINED(__heap_size) ? __heap_size : 0x800; + + /* The boot hart sets which hart runs the pre-main initialization routines, + * including copying .data into RAM, zeroing the BSS region, running + * constructors, etc. After initialization, the boot hart is also the only + * hart which runs application code unless the application overrides the + * secondary_main() function to start execution on secondary harts. + */ + PROVIDE(__metal_boot_hart = 0); + + /* The chicken bit is used by pre-main initialization to enable/disable + * certain core features */ + PROVIDE(__metal_chicken_bit = 1); + + /* The memory_ecc_scrub bit is used by _entry code to enable/disable + * memories scrubbing to zero */ + PROVIDE(__metal_eccscrub_bit = 0); + + /* The RAM memories map for ECC scrubbing */ + PROVIDE( metal_dtim_0_memory_start = 0x80000000 ); + PROVIDE( metal_dtim_0_memory_end = 0x80000000 + 0x4000 ); + PROVIDE( metal_itim_0_memory_start = 0x8000000 ); + PROVIDE( metal_itim_0_memory_end = 0x8000000 + 0x2000 ); + + + /* ROM SECTION + * + * The following sections contain data which lives in read-only memory, if + * such memory is present in the design, for the entire duration of program + * execution. + */ + + .init : { + /* The _enter symbol is placed in the .text.metal.init.enter section + * and must be placed at the beginning of the program */ + KEEP (*(.text.metal.init.enter)) + KEEP (*(.text.metal.init.*)) + KEEP (*(SORT_NONE(.init))) + KEEP (*(.text.libgloss.start)) + } >rom :rom + + .fini : { + KEEP (*(SORT_NONE(.fini))) + } >rom :rom + + .preinit_array : ALIGN(8) { + PROVIDE_HIDDEN (__preinit_array_start = .); + KEEP (*(.preinit_array)) + PROVIDE_HIDDEN (__preinit_array_end = .); + } >rom :rom + + .init_array : ALIGN(8) { + 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 = .); + PROVIDE_HIDDEN ( metal_constructors_start = .); + KEEP (*(SORT_BY_INIT_PRIORITY(.metal.init_array.*))); + KEEP (*(.metal.init_array)); + PROVIDE_HIDDEN ( metal_constructors_end = .); + } >rom :rom + + .fini_array : ALIGN(8) { + 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 = .); + PROVIDE_HIDDEN ( metal_destructors_start = .); + KEEP (*(SORT_BY_INIT_PRIORITY(.metal.fini_array.*))); + KEEP (*(.metal.fini_array)); + PROVIDE_HIDDEN ( metal_destructors_end = .); + } >rom :rom + + .privileged_functions : ALIGN (32) { + __privileged_functions_start__ = .; + KEEP(*(privileged_functions)) + . = ALIGN(32); + __privileged_functions_end__ = .; + } >rom + + + .ctors : { + . = ALIGN(32); + __unprivileged_section_start__ = .; + KEEP (*crtbegin.o(.ctors)) + KEEP (*crtbegin?.o(.ctors)) + KEEP (*(EXCLUDE_FILE (*crtend.o *crtend?.o ) .ctors)) + KEEP (*(SORT(.ctors.*))) + KEEP (*(.ctors)) + KEEP (*(.metal.ctors .metal.ctors.*)) + } >rom :rom + + .dtors : { + KEEP (*crtbegin.o(.dtors)) + KEEP (*crtbegin?.o(.dtors)) + KEEP (*(EXCLUDE_FILE (*crtend.o *crtend?.o ) .dtors)) + KEEP (*(SORT(.dtors.*))) + KEEP (*(.dtors)) + KEEP (*(.metal.dtors .metal.dtors.*)) + } >rom : rom + + .rodata : { + *(.rdata) + *(.rodata .rodata.*) + *(.gnu.linkonce.r.*) + . = ALIGN(8); + *(.srodata.cst16) + *(.srodata.cst8) + *(.srodata.cst4) + *(.srodata.cst2) + *(.srodata .srodata.*) + } >rom :rom + + /* ITIM SECTION + * + * The following sections contain data which is copied from read-only + * memory into an instruction tightly-integrated memory (ITIM), if one + * is present in the design, during pre-main program initialization. + * + * Generally, the data copied into the ITIM should be performance-critical + * functions which benefit from low instruction-fetch latency. + */ + + .itim : ALIGN(8) { + *(.itim .itim.*) + } >itim AT>rom :itim_init + + PROVIDE( metal_segment_itim_source_start = LOADADDR(.itim) ); + PROVIDE( metal_segment_itim_target_start = ADDR(.itim) ); + PROVIDE( metal_segment_itim_target_end = ADDR(.itim) + SIZEOF(.itim) ); + + /* LIM SECTION + * + * The following sections contain data which is copied from read-only + * memory into a loosely integrated memory (LIM), which is shared with L2 + * cache, during pre-main program initialization. + * + * Generally, the data copied into the LIM should be performance-critical + * functions which benefit from low instruction-fetch latency. + */ + + .lim : ALIGN(8) { + *(.lim .lim.*) + } >ram AT>rom :lim_init + + PROVIDE( metal_segment_lim_source_start = LOADADDR(.lim) ); + PROVIDE( metal_segment_lim_target_start = ADDR(.lim) ); + PROVIDE( metal_segment_lim_target_end = ADDR(.lim) + SIZEOF(.lim) ); + + /* TEXT SECTION + * + * The following section contains the code of the program, excluding + * everything that's been allocated into the ITIM/LIM already + */ + + .text : { + *(.text.unlikely .text.unlikely.*) + *(.text.startup .text.startup.*) + *(.text .text.*) + *(.gnu.linkonce.t.*) + *(freertos_system_calls) + . = ALIGN(32); + __unprivileged_section_end__ = .; + } >rom :text + + /* RAM SECTION + * + * The following sections contain data which is copied from read-only + * memory into a read-write-capable memory such as data tightly-integrated + * memory (DTIM) or another main memory, as well as the BSS, stack, and + * heap. + * + * You might notice that .data, .tdata, .tbss, .tbss_space, and .bss all + * have an apparently unnecessary ALIGN at their top. This is because + * the implementation of _start in Freedom Metal libgloss depends on the + * ADDR and LOADADDR being 8-byte aligned. + */ + + .data : ALIGN(8) { + . = ALIGN(32); + __unprivileged_data_section_start__ = .; + *(.data .data.*) + *(.gnu.linkonce.d.*) + . = ALIGN(8); + PROVIDE( __global_pointer$ = . + 0x800 ); + *(.sdata .sdata.* .sdata2.*) + *(.gnu.linkonce.s.*) + } >ram AT>rom :ram_init + + .tdata : ALIGN(8) { + PROVIDE( __tls_base = . ); + *(.tdata .tdata.* .gnu.linkonce.td.*) + } >ram AT>rom :tls :ram_init + + PROVIDE( __tdata_source = LOADADDR(.tdata) ); + PROVIDE( __tdata_size = SIZEOF(.tdata) ); + + PROVIDE( metal_segment_data_source_start = LOADADDR(.data) ); + PROVIDE( metal_segment_data_target_start = ADDR(.data) ); + PROVIDE( metal_segment_data_target_end = ADDR(.tdata) + SIZEOF(.tdata) ); + + .tbss : ALIGN(8) { + *(.tbss .tbss.* .gnu.linkonce.tb.*) + *(.tcommon .tcommon.*) + PROVIDE( __tls_end = . ); + } >ram AT>ram :tls :ram + PROVIDE( __tbss_size = SIZEOF(.tbss) ); + PROVIDE( __tls_size = __tls_end - __tls_base ); + + .tbss_space : ALIGN(8) { + . = . + __tbss_size; + } >ram :ram + + .bss (NOLOAD): ALIGN(8) { + *(.sbss*) + *(.gnu.linkonce.sb.*) + *(.bss .bss.*) + *(.gnu.linkonce.b.*) + *(COMMON) + . = ALIGN(32); + __unprivileged_data_section_end__ = .; + } >ram :ram + + PROVIDE( metal_segment_bss_source_start = LOADADDR(.tbss) ); + PROVIDE( metal_segment_bss_target_start = ADDR(.tbss) ); + PROVIDE( metal_segment_bss_target_end = ADDR(.bss) + SIZEOF(.bss) ); + + .privileged_data (NOLOAD) : ALIGN(32) { + __privileged_data_start__ = .; + *(privileged_data) + /* Non kernel data is kept out of the first _Privileged_Data_Region_Size + bytes of SRAM. */ + . = ALIGN(32); + __privileged_data_end__ = .; + } >ram + + + .stack (NOLOAD) : ALIGN(16) { + PROVIDE(metal_segment_stack_begin = .); + . += __stack_size; /* Hart 0 */ + PROVIDE( _sp = . ); + PROVIDE(metal_segment_stack_end = .); + } >ram :ram + + .heap (NOLOAD) : ALIGN(8) { + PROVIDE( __end = . ); + PROVIDE( __heap_start = . ); + PROVIDE( metal_segment_heap_target_start = . ); + /* If __heap_max is defined, grow the heap to use the rest of RAM, + * otherwise set the heap size to __heap_size */ + . = DEFINED(__heap_max) ? MIN( LENGTH(ram) - ( . - ORIGIN(ram)) , 0x10000000) : __heap_size; + PROVIDE( metal_segment_heap_target_end = . ); + PROVIDE( _heap_end = . ); + PROVIDE( __heap_end = . ); + } >ram :ram + + /* C++ exception handling information is + * not useful with our current runtime environment, + * and it consumes flash space. Discard it until + * we have something that can use it + */ + /* + /DISCARD/ : { + *(.eh_frame .eh_frame.*) + } + */ +} \ No newline at end of file diff --git a/arch/riscv32/fe310/src/sparkfun_redboard/nonsecure/metal.h b/arch/riscv32/fe310/src/sparkfun_redboard/nonsecure/metal.h new file mode 100644 index 00000000..e6f0e35a --- /dev/null +++ b/arch/riscv32/fe310/src/sparkfun_redboard/nonsecure/metal.h @@ -0,0 +1,1487 @@ +/* Copyright 2019 SiFive, Inc */ +/* SPDX-License-Identifier: Apache-2.0 */ +/* ----------------------------------- */ +/* ----------------------------------- */ + +#ifndef ASSEMBLY + +#include + +#ifdef __METAL_MACHINE_MACROS + +#ifndef MACROS_IF_METAL_H +#define MACROS_IF_METAL_H + +#define __METAL_CLINT_NUM_PARENTS 2 + +#ifndef __METAL_CLINT_NUM_PARENTS +#define __METAL_CLINT_NUM_PARENTS 0 +#endif +#define __METAL_PLIC_SUBINTERRUPTS 53 + +#define __METAL_PLIC_NUM_PARENTS 1 + +#ifndef __METAL_PLIC_SUBINTERRUPTS +#define __METAL_PLIC_SUBINTERRUPTS 0 +#endif +#ifndef __METAL_PLIC_NUM_PARENTS +#define __METAL_PLIC_NUM_PARENTS 0 +#endif +#ifndef __METAL_CLIC_SUBINTERRUPTS +#define __METAL_CLIC_SUBINTERRUPTS 0 +#endif + +#endif /* MACROS_IF_METAL_H*/ + +#else /* ! __METAL_MACHINE_MACROS */ + +#ifndef MACROS_ELSE_METAL_H +#define MACROS_ELSE_METAL_H + +#define __METAL_CLINT_2000000_INTERRUPTS 2 + +#define METAL_MAX_CLINT_INTERRUPTS 2 + +#define __METAL_CLINT_NUM_PARENTS 2 + +#define __METAL_INTERRUPT_CONTROLLER_C000000_INTERRUPTS 1 + +#define __METAL_PLIC_SUBINTERRUPTS 53 + +#define METAL_MAX_PLIC_INTERRUPTS 1 + +#define __METAL_PLIC_NUM_PARENTS 1 + +#define __METAL_CLIC_SUBINTERRUPTS 0 +#define METAL_MAX_CLIC_INTERRUPTS 0 + +#define METAL_MAX_LOCAL_EXT_INTERRUPTS 0 + +#define METAL_MAX_GLOBAL_EXT_INTERRUPTS 0 + +#define __METAL_GPIO_10012000_INTERRUPTS 32 + +#define METAL_MAX_GPIO_INTERRUPTS 32 + +#define __METAL_I2C_10016000_INTERRUPTS 1 + +#define METAL_MAX_I2C0_INTERRUPTS 1 + +#define __METAL_PWM_10015000_INTERRUPTS 4 + +#define __METAL_PWM_10025000_INTERRUPTS 4 + +#define __METAL_PWM_10035000_INTERRUPTS 4 + +#define METAL_MAX_PWM0_INTERRUPTS 4 + +#define METAL_MAX_PWM0_NCMP 4 + +#define __METAL_SERIAL_10013000_INTERRUPTS 1 + +#define __METAL_SERIAL_10023000_INTERRUPTS 1 + +#define METAL_MAX_UART_INTERRUPTS 1 + +#define METAL_MAX_SIMUART_INTERRUPTS 0 + + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* From clock@0 */ +extern struct __metal_driver_fixed_clock __metal_dt_clock_0; + +/* From clock@2 */ +extern struct __metal_driver_fixed_clock __metal_dt_clock_2; + +/* From clock@5 */ +extern struct __metal_driver_fixed_clock __metal_dt_clock_5; + +/* From clock@6 */ +extern struct __metal_driver_fixed_clock __metal_dt_clock_6; + +extern struct metal_memory __metal_dt_mem_dtim_80000000; + +extern struct metal_memory __metal_dt_mem_itim_8000000; + +extern struct metal_memory __metal_dt_mem_spi_10014000; + +extern struct metal_memory __metal_dt_mem_spi_10024000; + +extern struct metal_memory __metal_dt_mem_spi_10034000; + +/* From clint@2000000 */ +extern struct __metal_driver_riscv_clint0 __metal_dt_clint_2000000; + +/* From cpu@0 */ +extern struct __metal_driver_cpu __metal_dt_cpu_0; + +extern struct __metal_driver_riscv_cpu_intc __metal_dt_cpu_0_interrupt_controller; + +/* From interrupt_controller@c000000 */ +extern struct __metal_driver_riscv_plic0 __metal_dt_interrupt_controller_c000000; + +extern struct metal_pmp __metal_dt_pmp; + +/* From gpio@10012000 */ +extern struct __metal_driver_sifive_gpio0 __metal_dt_gpio_10012000; + +/* From led@0 */ +extern struct __metal_driver_sifive_gpio_led __metal_dt_led_0; + +/* From led@1 */ +extern struct __metal_driver_sifive_gpio_led __metal_dt_led_1; + +/* From led@2 */ +extern struct __metal_driver_sifive_gpio_led __metal_dt_led_2; + +/* From i2c@10016000 */ +extern struct __metal_driver_sifive_i2c0 __metal_dt_i2c_10016000; + +/* From pwm@10015000 */ +extern struct __metal_driver_sifive_pwm0 __metal_dt_pwm_10015000; + +/* From pwm@10025000 */ +extern struct __metal_driver_sifive_pwm0 __metal_dt_pwm_10025000; + +/* From pwm@10035000 */ +extern struct __metal_driver_sifive_pwm0 __metal_dt_pwm_10035000; + +/* From aon@10000000 */ +extern struct __metal_driver_sifive_rtc0 __metal_dt_rtc_10000000; + +/* From spi@10014000 */ +extern struct __metal_driver_sifive_spi0 __metal_dt_spi_10014000; + +/* From spi@10024000 */ +extern struct __metal_driver_sifive_spi0 __metal_dt_spi_10024000; + +/* From spi@10034000 */ +extern struct __metal_driver_sifive_spi0 __metal_dt_spi_10034000; + +/* From serial@10013000 */ +extern struct __metal_driver_sifive_uart0 __metal_dt_serial_10013000; + +/* From serial@10023000 */ +extern struct __metal_driver_sifive_uart0 __metal_dt_serial_10023000; + +/* From aon@10000000 */ +extern struct __metal_driver_sifive_wdog0 __metal_dt_aon_10000000; + +/* From clock@3 */ +extern struct __metal_driver_sifive_fe310_g000_hfrosc __metal_dt_clock_3; + +/* From clock@1 */ +extern struct __metal_driver_sifive_fe310_g000_hfxosc __metal_dt_clock_1; + +/* From clock@7 */ +extern struct __metal_driver_sifive_fe310_g000_lfrosc __metal_dt_clock_7; + +/* From clock@4 */ +extern struct __metal_driver_sifive_fe310_g000_pll __metal_dt_clock_4; + +/* From prci@10008000 */ +extern struct __metal_driver_sifive_fe310_g000_prci __metal_dt_prci_10008000; + + + +/* --------------------- fixed_clock ------------ */ +static __inline__ unsigned long __metal_driver_fixed_clock_rate(const struct metal_clock *clock) +{ + if ((uintptr_t)clock == (uintptr_t)&__metal_dt_clock_0) { + return METAL_FIXED_CLOCK_0_CLOCK_0_CLOCK_FREQUENCY; + } + else if ((uintptr_t)clock == (uintptr_t)&__metal_dt_clock_2) { + return METAL_FIXED_CLOCK_2_CLOCK_2_CLOCK_FREQUENCY; + } + else if ((uintptr_t)clock == (uintptr_t)&__metal_dt_clock_5) { + return METAL_FIXED_CLOCK_5_CLOCK_5_CLOCK_FREQUENCY; + } + else if ((uintptr_t)clock == (uintptr_t)&__metal_dt_clock_6) { + return METAL_FIXED_CLOCK_6_CLOCK_6_CLOCK_FREQUENCY; + } + else { + return 0; + } +} + + + +/* --------------------- fixed_factor_clock ------------ */ + + +/* --------------------- sifive_clint0 ------------ */ +static __inline__ unsigned long __metal_driver_sifive_clint0_control_base(struct metal_interrupt *controller) +{ + if ((uintptr_t)controller == (uintptr_t)&__metal_dt_clint_2000000) { + return METAL_RISCV_CLINT0_2000000_BASE_ADDRESS; + } + else { + return 0; + } +} + +static __inline__ unsigned long __metal_driver_sifive_clint0_control_size(struct metal_interrupt *controller) +{ + if ((uintptr_t)controller == (uintptr_t)&__metal_dt_clint_2000000) { + return METAL_RISCV_CLINT0_2000000_SIZE; + } + else { + return 0; + } +} + +static __inline__ int __metal_driver_sifive_clint0_num_interrupts(struct metal_interrupt *controller) +{ + if ((uintptr_t)controller == (uintptr_t)&__metal_dt_clint_2000000) { + return METAL_MAX_CLINT_INTERRUPTS; + } + else { + return 0; + } +} + +static __inline__ struct metal_interrupt * __metal_driver_sifive_clint0_interrupt_parents(struct metal_interrupt *controller, int idx) +{ + if (idx == 0) { + return (struct metal_interrupt *)&__metal_dt_cpu_0_interrupt_controller.controller; + } + else if (idx == 1) { + return (struct metal_interrupt *)&__metal_dt_cpu_0_interrupt_controller.controller; + } + else { + return NULL; + } +} + +static __inline__ int __metal_driver_sifive_clint0_interrupt_lines(struct metal_interrupt *controller, int idx) +{ + if (idx == 0) { + return 3; + } + else if (idx == 1) { + return 7; + } + else { + return 0; + } +} + + + +/* --------------------- cpu ------------ */ +static __inline__ int __metal_driver_cpu_hartid(struct metal_cpu *cpu) +{ + if ((uintptr_t)cpu == (uintptr_t)&__metal_dt_cpu_0) { + return 0; + } + else { + return -1; + } +} + +static __inline__ int __metal_driver_cpu_timebase(struct metal_cpu *cpu) +{ + if ((uintptr_t)cpu == (uintptr_t)&__metal_dt_cpu_0) { + return 16000000; + } + else { + return 0; + } +} + +static __inline__ struct metal_interrupt * __metal_driver_cpu_interrupt_controller(struct metal_cpu *cpu) +{ + if ((uintptr_t)cpu == (uintptr_t)&__metal_dt_cpu_0) { + return &__metal_dt_cpu_0_interrupt_controller.controller; + } + else { + return NULL; + } +} + +static __inline__ int __metal_driver_cpu_num_pmp_regions(struct metal_cpu *cpu) +{ + if ((uintptr_t)cpu == (uintptr_t)&__metal_dt_cpu_0) { + return 8; + } + else { + return 0; + } +} + +static __inline__ struct metal_buserror * __metal_driver_cpu_buserror(struct metal_cpu *cpu) +{ + if ((uintptr_t)cpu == (uintptr_t)&__metal_dt_cpu_0) { + return NULL; + } + else { + return NULL; + } +} + + + +/* --------------------- sifive_plic0 ------------ */ +static __inline__ unsigned long __metal_driver_sifive_plic0_control_base(struct metal_interrupt *controller) +{ + if ((uintptr_t)controller == (uintptr_t)&__metal_dt_interrupt_controller_c000000) { + return METAL_RISCV_PLIC0_C000000_BASE_ADDRESS; + } + else { + return 0; + } +} + +static __inline__ unsigned long __metal_driver_sifive_plic0_control_size(struct metal_interrupt *controller) +{ + if ((uintptr_t)controller == (uintptr_t)&__metal_dt_interrupt_controller_c000000) { + return METAL_RISCV_PLIC0_C000000_SIZE; + } + else { + return 0; + } +} + +static __inline__ int __metal_driver_sifive_plic0_num_interrupts(struct metal_interrupt *controller) +{ + if ((uintptr_t)controller == (uintptr_t)&__metal_dt_interrupt_controller_c000000) { + return METAL_RISCV_PLIC0_C000000_RISCV_NDEV; + } + else { + return 0; + } +} + +static __inline__ int __metal_driver_sifive_plic0_max_priority(struct metal_interrupt *controller) +{ + if ((uintptr_t)controller == (uintptr_t)&__metal_dt_interrupt_controller_c000000) { + return METAL_RISCV_PLIC0_C000000_RISCV_MAX_PRIORITY; + } + else { + return 0; + } +} + +static __inline__ struct metal_interrupt * __metal_driver_sifive_plic0_interrupt_parents(struct metal_interrupt *controller, int idx) +{ + if (idx == 0) { + return (struct metal_interrupt *)&__metal_dt_cpu_0_interrupt_controller.controller; + } + else { + return NULL; + } +} + +static __inline__ int __metal_driver_sifive_plic0_interrupt_lines(struct metal_interrupt *controller, int idx) +{ + if (idx == 0) { + return 11; + } + else { + return 0; + } +} + +static __inline__ int __metal_driver_sifive_plic0_context_ids(int hartid) +{ + if (hartid == 0) { + return 0; + } + else { + return -1; + } +} + + + +/* --------------------- sifive_buserror0 ------------ */ + + +/* --------------------- sifive_clic0 ------------ */ + + +/* --------------------- sifive_local_external_interrupts0 ------------ */ + + +/* --------------------- sifive_global_external_interrupts0 ------------ */ + + +/* --------------------- sifive_gpio0 ------------ */ +static __inline__ unsigned long __metal_driver_sifive_gpio0_base(struct metal_gpio *gpio) +{ + if ((uintptr_t)gpio == (uintptr_t)&__metal_dt_gpio_10012000) { + return METAL_SIFIVE_GPIO0_10012000_BASE_ADDRESS; + } + else { + return 0; + } +} + +static __inline__ unsigned long __metal_driver_sifive_gpio0_size(struct metal_gpio *gpio) +{ + if ((uintptr_t)gpio == (uintptr_t)&__metal_dt_gpio_10012000) { + return METAL_SIFIVE_GPIO0_10012000_SIZE; + } + else { + return 0; + } +} + +static __inline__ int __metal_driver_sifive_gpio0_num_interrupts(struct metal_gpio *gpio) +{ + if ((uintptr_t)gpio == (uintptr_t)&__metal_dt_gpio_10012000) { + return METAL_MAX_GPIO_INTERRUPTS; + } + else { + return 0; + } +} + +static __inline__ struct metal_interrupt * __metal_driver_sifive_gpio0_interrupt_parent(struct metal_gpio *gpio) +{ + if ((uintptr_t)gpio == (uintptr_t)&__metal_dt_gpio_10012000) { + return (struct metal_interrupt *)&__metal_dt_interrupt_controller_c000000.controller; + } + else { + return 0; + } +} + +static __inline__ int __metal_driver_sifive_gpio0_interrupt_lines(struct metal_gpio *gpio, int idx) +{ + if (((uintptr_t)gpio == (uintptr_t)&__metal_dt_gpio_10012000) && (idx == 0)) { + return 8; + } + else if ((((uintptr_t)gpio == (uintptr_t)&__metal_dt_gpio_10012000) && (idx == 1))) { + return 9; + } + else if ((((uintptr_t)gpio == (uintptr_t)&__metal_dt_gpio_10012000) && (idx == 2))) { + return 10; + } + else if ((((uintptr_t)gpio == (uintptr_t)&__metal_dt_gpio_10012000) && (idx == 3))) { + return 11; + } + else if ((((uintptr_t)gpio == (uintptr_t)&__metal_dt_gpio_10012000) && (idx == 4))) { + return 12; + } + else if ((((uintptr_t)gpio == (uintptr_t)&__metal_dt_gpio_10012000) && (idx == 5))) { + return 13; + } + else if ((((uintptr_t)gpio == (uintptr_t)&__metal_dt_gpio_10012000) && (idx == 6))) { + return 14; + } + else if ((((uintptr_t)gpio == (uintptr_t)&__metal_dt_gpio_10012000) && (idx == 7))) { + return 15; + } + else if ((((uintptr_t)gpio == (uintptr_t)&__metal_dt_gpio_10012000) && (idx == 8))) { + return 16; + } + else if ((((uintptr_t)gpio == (uintptr_t)&__metal_dt_gpio_10012000) && (idx == 9))) { + return 17; + } + else if ((((uintptr_t)gpio == (uintptr_t)&__metal_dt_gpio_10012000) && (idx == 10))) { + return 18; + } + else if ((((uintptr_t)gpio == (uintptr_t)&__metal_dt_gpio_10012000) && (idx == 11))) { + return 19; + } + else if ((((uintptr_t)gpio == (uintptr_t)&__metal_dt_gpio_10012000) && (idx == 12))) { + return 20; + } + else if ((((uintptr_t)gpio == (uintptr_t)&__metal_dt_gpio_10012000) && (idx == 13))) { + return 21; + } + else if ((((uintptr_t)gpio == (uintptr_t)&__metal_dt_gpio_10012000) && (idx == 14))) { + return 22; + } + else if ((((uintptr_t)gpio == (uintptr_t)&__metal_dt_gpio_10012000) && (idx == 15))) { + return 23; + } + else if ((((uintptr_t)gpio == (uintptr_t)&__metal_dt_gpio_10012000) && (idx == 16))) { + return 24; + } + else if ((((uintptr_t)gpio == (uintptr_t)&__metal_dt_gpio_10012000) && (idx == 17))) { + return 25; + } + else if ((((uintptr_t)gpio == (uintptr_t)&__metal_dt_gpio_10012000) && (idx == 18))) { + return 26; + } + else if ((((uintptr_t)gpio == (uintptr_t)&__metal_dt_gpio_10012000) && (idx == 19))) { + return 27; + } + else if ((((uintptr_t)gpio == (uintptr_t)&__metal_dt_gpio_10012000) && (idx == 20))) { + return 28; + } + else if ((((uintptr_t)gpio == (uintptr_t)&__metal_dt_gpio_10012000) && (idx == 21))) { + return 29; + } + else if ((((uintptr_t)gpio == (uintptr_t)&__metal_dt_gpio_10012000) && (idx == 22))) { + return 30; + } + else if ((((uintptr_t)gpio == (uintptr_t)&__metal_dt_gpio_10012000) && (idx == 23))) { + return 31; + } + else if ((((uintptr_t)gpio == (uintptr_t)&__metal_dt_gpio_10012000) && (idx == 24))) { + return 32; + } + else if ((((uintptr_t)gpio == (uintptr_t)&__metal_dt_gpio_10012000) && (idx == 25))) { + return 33; + } + else if ((((uintptr_t)gpio == (uintptr_t)&__metal_dt_gpio_10012000) && (idx == 26))) { + return 34; + } + else if ((((uintptr_t)gpio == (uintptr_t)&__metal_dt_gpio_10012000) && (idx == 27))) { + return 35; + } + else if ((((uintptr_t)gpio == (uintptr_t)&__metal_dt_gpio_10012000) && (idx == 28))) { + return 36; + } + else if ((((uintptr_t)gpio == (uintptr_t)&__metal_dt_gpio_10012000) && (idx == 29))) { + return 27; + } + else if ((((uintptr_t)gpio == (uintptr_t)&__metal_dt_gpio_10012000) && (idx == 30))) { + return 28; + } + else if ((((uintptr_t)gpio == (uintptr_t)&__metal_dt_gpio_10012000) && (idx == 31))) { + return 29; + } + else { + return 0; + } +} + + + +/* --------------------- sifive_gpio_button ------------ */ + + +/* --------------------- sifive_gpio_led ------------ */ +static __inline__ struct metal_gpio * __metal_driver_sifive_gpio_led_gpio(struct metal_led *led) +{ + if ((uintptr_t)led == (uintptr_t)&__metal_dt_led_0) { + return (struct metal_gpio *)&__metal_dt_gpio_10012000; + } + else if ((uintptr_t)led == (uintptr_t)&__metal_dt_led_1) { + return (struct metal_gpio *)&__metal_dt_gpio_10012000; + } + else if ((uintptr_t)led == (uintptr_t)&__metal_dt_led_2) { + return (struct metal_gpio *)&__metal_dt_gpio_10012000; + } + else { + return NULL; + } +} + +static __inline__ int __metal_driver_sifive_gpio_led_pin(struct metal_led *led) +{ + if ((uintptr_t)led == (uintptr_t)&__metal_dt_led_0) { + return 22; + } + else if ((uintptr_t)led == (uintptr_t)&__metal_dt_led_1) { + return 19; + } + else if ((uintptr_t)led == (uintptr_t)&__metal_dt_led_2) { + return 21; + } + else { + return 0; + } +} + +static __inline__ char * __metal_driver_sifive_gpio_led_label(struct metal_led *led) +{ + if ((uintptr_t)led == (uintptr_t)&__metal_dt_led_0) { + return "LD0red"; + } + else if ((uintptr_t)led == (uintptr_t)&__metal_dt_led_1) { + return "LD0green"; + } + else if ((uintptr_t)led == (uintptr_t)&__metal_dt_led_2) { + return "LD0blue"; + } + else { + return ""; + } +} + + + +/* --------------------- sifive_gpio_switch ------------ */ + + +/* --------------------- sifive_i2c0 ------------ */ +static __inline__ unsigned long __metal_driver_sifive_i2c0_control_base(struct metal_i2c *i2c) +{ + if ((uintptr_t)i2c == (uintptr_t)&__metal_dt_i2c_10016000) { + return METAL_SIFIVE_I2C0_10016000_BASE_ADDRESS; + } + else { + return 0; + } +} + +static __inline__ unsigned long __metal_driver_sifive_i2c0_control_size(struct metal_i2c *i2c) +{ + if ((uintptr_t)i2c == (uintptr_t)&__metal_dt_i2c_10016000) { + return METAL_SIFIVE_I2C0_10016000_SIZE; + } + else { + return 0; + } +} + +static __inline__ struct metal_clock * __metal_driver_sifive_i2c0_clock(struct metal_i2c *i2c) +{ + if ((uintptr_t)i2c == (uintptr_t)&__metal_dt_i2c_10016000) { + return (struct metal_clock *)&__metal_dt_clock_4.clock; + } + else { + return NULL; + } +} + +static __inline__ struct __metal_driver_sifive_gpio0 * __metal_driver_sifive_i2c0_pinmux(struct metal_i2c *i2c) +{ + if ((uintptr_t)i2c == (uintptr_t)&__metal_dt_i2c_10016000) { + return (struct __metal_driver_sifive_gpio0 *)&__metal_dt_gpio_10012000; + } + else { + return NULL; + } +} + +static __inline__ unsigned long __metal_driver_sifive_i2c0_pinmux_output_selector(struct metal_i2c *i2c) +{ + if ((uintptr_t)i2c == (uintptr_t)&__metal_dt_i2c_10016000) { + return 0; + } + else { + return 0; + } +} + +static __inline__ unsigned long __metal_driver_sifive_i2c0_pinmux_source_selector(struct metal_i2c *i2c) +{ + if ((uintptr_t)i2c == (uintptr_t)&__metal_dt_i2c_10016000) { + return 12288; + } + else { + return 0; + } +} + +static __inline__ int __metal_driver_sifive_i2c0_num_interrupts(struct metal_i2c *i2c) +{ + return METAL_MAX_I2C0_INTERRUPTS; +} + +static __inline__ struct metal_interrupt * __metal_driver_sifive_i2c0_interrupt_parent(struct metal_i2c *i2c) +{ + return (struct metal_interrupt *)&__metal_dt_interrupt_controller_c000000.controller; +} + +static __inline__ int __metal_driver_sifive_i2c0_interrupt_line(struct metal_i2c *i2c) +{ + if ((uintptr_t)i2c == (uintptr_t)&__metal_dt_i2c_10016000) { + return 52; + } + else { + return 0; + } +} + + + +/* --------------------- sifive_prci0 ------------ */ + + +/* --------------------- sifive_pwm0 ------------ */ +static __inline__ unsigned long __metal_driver_sifive_pwm0_control_base(struct metal_pwm *pwm) +{ + if ((uintptr_t)pwm == (uintptr_t)&__metal_dt_pwm_10015000) { + return METAL_SIFIVE_PWM0_10015000_BASE_ADDRESS; + } + else if ((uintptr_t)pwm == (uintptr_t)&__metal_dt_pwm_10025000) { + return METAL_SIFIVE_PWM0_10025000_BASE_ADDRESS; + } + else if ((uintptr_t)pwm == (uintptr_t)&__metal_dt_pwm_10035000) { + return METAL_SIFIVE_PWM0_10035000_BASE_ADDRESS; + } + else { + return 0; + } +} + +static __inline__ unsigned long __metal_driver_sifive_pwm0_control_size(struct metal_pwm *pwm) +{ + if ((uintptr_t)pwm == (uintptr_t)&__metal_dt_pwm_10015000) { + return METAL_SIFIVE_PWM0_10015000_SIZE; + } + else if ((uintptr_t)pwm == (uintptr_t)&__metal_dt_pwm_10025000) { + return METAL_SIFIVE_PWM0_10025000_SIZE; + } + else if ((uintptr_t)pwm == (uintptr_t)&__metal_dt_pwm_10035000) { + return METAL_SIFIVE_PWM0_10035000_SIZE; + } + else { + return 0; + } +} + +static __inline__ struct metal_clock * __metal_driver_sifive_pwm0_clock(struct metal_pwm *pwm) +{ + if ((uintptr_t)pwm == (uintptr_t)&__metal_dt_pwm_10015000) { + return (struct metal_clock *)&__metal_dt_clock_4.clock; + } + else if ((uintptr_t)pwm == (uintptr_t)&__metal_dt_pwm_10025000) { + return (struct metal_clock *)&__metal_dt_clock_4.clock; + } + else if ((uintptr_t)pwm == (uintptr_t)&__metal_dt_pwm_10035000) { + return (struct metal_clock *)&__metal_dt_clock_4.clock; + } + else { + return NULL; + } +} + +static __inline__ struct __metal_driver_sifive_gpio0 * __metal_driver_sifive_pwm0_pinmux(struct metal_pwm *pwm) +{ + if ((uintptr_t)pwm == (uintptr_t)&__metal_dt_pwm_10015000) { + return (struct __metal_driver_sifive_gpio0 *)&__metal_dt_gpio_10012000; + } + else if ((uintptr_t)pwm == (uintptr_t)&__metal_dt_pwm_10025000) { + return (struct __metal_driver_sifive_gpio0 *)&__metal_dt_gpio_10012000; + } + else if ((uintptr_t)pwm == (uintptr_t)&__metal_dt_pwm_10035000) { + return (struct __metal_driver_sifive_gpio0 *)&__metal_dt_gpio_10012000; + } + else { + return NULL; + } +} + +static __inline__ unsigned long __metal_driver_sifive_pwm0_pinmux_output_selector(struct metal_pwm *pwm) +{ + if ((uintptr_t)pwm == (uintptr_t)&__metal_dt_pwm_10015000) { + return 15; + } + else if ((uintptr_t)pwm == (uintptr_t)&__metal_dt_pwm_10025000) { + return 7864320; + } + else if ((uintptr_t)pwm == (uintptr_t)&__metal_dt_pwm_10035000) { + return 15360; + } + else { + return 0; + } +} + +static __inline__ unsigned long __metal_driver_sifive_pwm0_pinmux_source_selector(struct metal_pwm *pwm) +{ + if ((uintptr_t)pwm == (uintptr_t)&__metal_dt_pwm_10015000) { + return 15; + } + else if ((uintptr_t)pwm == (uintptr_t)&__metal_dt_pwm_10025000) { + return 7864320; + } + else if ((uintptr_t)pwm == (uintptr_t)&__metal_dt_pwm_10035000) { + return 15360; + } + else { + return 0; + } +} + +static __inline__ int __metal_driver_sifive_pwm0_num_interrupts(struct metal_pwm *pwm) +{ + if ((uintptr_t)pwm == (uintptr_t)&__metal_dt_pwm_10015000) { + return __METAL_PWM_10015000_INTERRUPTS; + } + else if ((uintptr_t)pwm == (uintptr_t)&__metal_dt_pwm_10025000) { + return __METAL_PWM_10025000_INTERRUPTS; + } + else if ((uintptr_t)pwm == (uintptr_t)&__metal_dt_pwm_10035000) { + return __METAL_PWM_10035000_INTERRUPTS; + } + else { + return 0; + } +} + +static __inline__ struct metal_interrupt * __metal_driver_sifive_pwm0_interrupt_parent(struct metal_pwm *pwm) +{ + return (struct metal_interrupt *)&__metal_dt_interrupt_controller_c000000.controller; +} + +static __inline__ int __metal_driver_sifive_pwm0_interrupt_lines(struct metal_pwm *pwm, int idx) +{ + if (((uintptr_t)pwm == (uintptr_t)&__metal_dt_pwm_10015000) && (idx == 0)) { + return 40; + } + else if ((((uintptr_t)pwm == (uintptr_t)&__metal_dt_pwm_10015000) && (idx == 1))) { + return 41; + } + else if ((((uintptr_t)pwm == (uintptr_t)&__metal_dt_pwm_10015000) && (idx == 2))) { + return 42; + } + else if ((((uintptr_t)pwm == (uintptr_t)&__metal_dt_pwm_10015000) && (idx == 3))) { + return 43; + } + else if ((((uintptr_t)pwm == (uintptr_t)&__metal_dt_pwm_10025000) && (idx == 0))) { + return 44; + } + else if ((((uintptr_t)pwm == (uintptr_t)&__metal_dt_pwm_10025000) && (idx == 1))) { + return 45; + } + else if ((((uintptr_t)pwm == (uintptr_t)&__metal_dt_pwm_10025000) && (idx == 2))) { + return 46; + } + else if ((((uintptr_t)pwm == (uintptr_t)&__metal_dt_pwm_10025000) && (idx == 3))) { + return 47; + } + else if ((((uintptr_t)pwm == (uintptr_t)&__metal_dt_pwm_10035000) && (idx == 0))) { + return 48; + } + else if ((((uintptr_t)pwm == (uintptr_t)&__metal_dt_pwm_10035000) && (idx == 1))) { + return 49; + } + else if ((((uintptr_t)pwm == (uintptr_t)&__metal_dt_pwm_10035000) && (idx == 2))) { + return 50; + } + else if ((((uintptr_t)pwm == (uintptr_t)&__metal_dt_pwm_10035000) && (idx == 3))) { + return 51; + } + else { + return 0; + } +} + +static __inline__ int __metal_driver_sifive_pwm0_compare_width(struct metal_pwm *pwm) +{ + if ((uintptr_t)pwm == (uintptr_t)&__metal_dt_pwm_10015000) { + return 8; + } + else if ((uintptr_t)pwm == (uintptr_t)&__metal_dt_pwm_10025000) { + return 16; + } + else if ((uintptr_t)pwm == (uintptr_t)&__metal_dt_pwm_10035000) { + return 16; + } + else { + return 0; + } +} + +static __inline__ int __metal_driver_sifive_pwm0_comparator_count(struct metal_pwm *pwm) +{ + if ((uintptr_t)pwm == (uintptr_t)&__metal_dt_pwm_10015000) { + return 4; + } + else if ((uintptr_t)pwm == (uintptr_t)&__metal_dt_pwm_10025000) { + return 4; + } + else if ((uintptr_t)pwm == (uintptr_t)&__metal_dt_pwm_10035000) { + return 4; + } + else { + return 0; + } +} + + + +/* --------------------- sifive_remapper2 ------------ */ + + +/* --------------------- sifive_rtc0 ------------ */ +static __inline__ unsigned long __metal_driver_sifive_rtc0_control_base(const struct metal_rtc *const rtc) +{ + if ((uintptr_t)rtc == (uintptr_t)&__metal_dt_rtc_10000000) { + return METAL_SIFIVE_AON0_10000000_BASE_ADDRESS; + } + else { + return 0; + } +} + +static __inline__ unsigned long __metal_driver_sifive_rtc0_control_size(const struct metal_rtc *const rtc) +{ + if ((uintptr_t)rtc == (uintptr_t)&__metal_dt_rtc_10000000) { + return METAL_SIFIVE_AON0_10000000_SIZE; + } + else { + return 0; + } +} + +static __inline__ struct metal_interrupt * __metal_driver_sifive_rtc0_interrupt_parent(const struct metal_rtc *const rtc) +{ + if ((uintptr_t)rtc == (uintptr_t)&__metal_dt_rtc_10000000) { + return (struct metal_interrupt *)&__metal_dt_interrupt_controller_c000000.controller; + } + else { + return 0; + } +} + +static __inline__ int __metal_driver_sifive_rtc0_interrupt_line(const struct metal_rtc *const rtc) +{ + if ((uintptr_t)rtc == (uintptr_t)&__metal_dt_rtc_10000000) { + return 2; + } + else { + return 0; + } +} + +static __inline__ struct metal_clock * __metal_driver_sifive_rtc0_clock(const struct metal_rtc *const rtc) +{ + if ((uintptr_t)rtc == (uintptr_t)&__metal_dt_rtc_10000000) { + return (struct metal_clock *)&__metal_dt_clock_7.clock; + } + else { + return 0; + } +} + + +static __inline__ unsigned long __metal_driver_sifive_spi0_control_base(struct metal_spi *spi) +{ + if ((uintptr_t)spi == (uintptr_t)&__metal_dt_spi_10014000) { + return METAL_SIFIVE_SPI0_10014000_BASE_ADDRESS; + } + else if ((uintptr_t)spi == (uintptr_t)&__metal_dt_spi_10024000) { + return METAL_SIFIVE_SPI0_10024000_BASE_ADDRESS; + } + else if ((uintptr_t)spi == (uintptr_t)&__metal_dt_spi_10034000) { + return METAL_SIFIVE_SPI0_10034000_BASE_ADDRESS; + } + else { + return 0; + } +} + +static __inline__ unsigned long __metal_driver_sifive_spi0_control_size(struct metal_spi *spi) +{ + if ((uintptr_t)spi == (uintptr_t)&__metal_dt_spi_10014000) { + return METAL_SIFIVE_SPI0_10014000_SIZE; + } + else if ((uintptr_t)spi == (uintptr_t)&__metal_dt_spi_10024000) { + return METAL_SIFIVE_SPI0_10024000_SIZE; + } + else if ((uintptr_t)spi == (uintptr_t)&__metal_dt_spi_10034000) { + return METAL_SIFIVE_SPI0_10034000_SIZE; + } + else { + return 0; + } +} + +static __inline__ struct metal_clock * __metal_driver_sifive_spi0_clock(struct metal_spi *spi) +{ + if ((uintptr_t)spi == (uintptr_t)&__metal_dt_spi_10014000) { + return (struct metal_clock *)&__metal_dt_clock_4.clock; + } + else if ((uintptr_t)spi == (uintptr_t)&__metal_dt_spi_10024000) { + return (struct metal_clock *)&__metal_dt_clock_4.clock; + } + else if ((uintptr_t)spi == (uintptr_t)&__metal_dt_spi_10034000) { + return (struct metal_clock *)&__metal_dt_clock_4.clock; + } + else { + return 0; + } +} + +static __inline__ struct __metal_driver_sifive_gpio0 * __metal_driver_sifive_spi0_pinmux(struct metal_spi *spi) +{ + if ((uintptr_t)spi == (uintptr_t)&__metal_dt_spi_10014000) { + return (struct __metal_driver_sifive_gpio0 *)&__metal_dt_gpio_10012000; + } + else if ((uintptr_t)spi == (uintptr_t)&__metal_dt_spi_10024000) { + return (struct __metal_driver_sifive_gpio0 *)&__metal_dt_gpio_10012000; + } + else if ((uintptr_t)spi == (uintptr_t)&__metal_dt_spi_10034000) { + return (struct __metal_driver_sifive_gpio0 *)&__metal_dt_gpio_10012000; + } + else { + return 0; + } +} + +static __inline__ unsigned long __metal_driver_sifive_spi0_pinmux_output_selector(struct metal_spi *spi) +{ + if ((uintptr_t)spi == (uintptr_t)&__metal_dt_spi_10014000) { + return 0; + } + else if ((uintptr_t)spi == (uintptr_t)&__metal_dt_spi_10024000) { + return 0; + } + else if ((uintptr_t)spi == (uintptr_t)&__metal_dt_spi_10034000) { + return 0; + } + else { + return 0; + } +} + +static __inline__ unsigned long __metal_driver_sifive_spi0_pinmux_source_selector(struct metal_spi *spi) +{ + if ((uintptr_t)spi == (uintptr_t)&__metal_dt_spi_10014000) { + return 0; + } + else if ((uintptr_t)spi == (uintptr_t)&__metal_dt_spi_10024000) { + return 60; + } + else if ((uintptr_t)spi == (uintptr_t)&__metal_dt_spi_10034000) { + return 4227858432; + } + else { + return 0; + } +} + + + +/* --------------------- sifive_test0 ------------ */ + + +/* --------------------- sifive_trace ------------ */ + +/* --------------------- sifive_uart0 ------------ */ +static __inline__ unsigned long __metal_driver_sifive_uart0_control_base(struct metal_uart *uart) +{ + if ((uintptr_t)uart == (uintptr_t)&__metal_dt_serial_10013000) { + return METAL_SIFIVE_UART0_10013000_BASE_ADDRESS; + } + else if ((uintptr_t)uart == (uintptr_t)&__metal_dt_serial_10023000) { + return METAL_SIFIVE_UART0_10023000_BASE_ADDRESS; + } + else { + return 0; + } +} + +static __inline__ unsigned long __metal_driver_sifive_uart0_control_size(struct metal_uart *uart) +{ + if ((uintptr_t)uart == (uintptr_t)&__metal_dt_serial_10013000) { + return METAL_SIFIVE_UART0_10013000_SIZE; + } + else if ((uintptr_t)uart == (uintptr_t)&__metal_dt_serial_10023000) { + return METAL_SIFIVE_UART0_10023000_SIZE; + } + else { + return 0; + } +} + +static __inline__ int __metal_driver_sifive_uart0_num_interrupts(struct metal_uart *uart) +{ + if ((uintptr_t)uart == (uintptr_t)&__metal_dt_serial_10013000) { + return METAL_MAX_UART_INTERRUPTS; + } + else if ((uintptr_t)uart == (uintptr_t)&__metal_dt_serial_10023000) { + return METAL_MAX_UART_INTERRUPTS; + } + else { + return 0; + } +} + +static __inline__ struct metal_interrupt * __metal_driver_sifive_uart0_interrupt_parent(struct metal_uart *uart) +{ + if ((uintptr_t)uart == (uintptr_t)&__metal_dt_serial_10013000) { + return (struct metal_interrupt *)&__metal_dt_interrupt_controller_c000000.controller; + } + else if ((uintptr_t)uart == (uintptr_t)&__metal_dt_serial_10023000) { + return (struct metal_interrupt *)&__metal_dt_interrupt_controller_c000000.controller; + } + else { + return 0; + } +} + +static __inline__ int __metal_driver_sifive_uart0_interrupt_line(struct metal_uart *uart) +{ + if ((uintptr_t)uart == (uintptr_t)&__metal_dt_serial_10013000) { + return 3; + } + else if ((uintptr_t)uart == (uintptr_t)&__metal_dt_serial_10023000) { + return 4; + } + else { + return 0; + } +} + +static __inline__ struct metal_clock * __metal_driver_sifive_uart0_clock(struct metal_uart *uart) +{ + if ((uintptr_t)uart == (uintptr_t)&__metal_dt_serial_10013000) { + return (struct metal_clock *)&__metal_dt_clock_4.clock; + } + else if ((uintptr_t)uart == (uintptr_t)&__metal_dt_serial_10023000) { + return (struct metal_clock *)&__metal_dt_clock_4.clock; + } + else { + return 0; + } +} + +static __inline__ struct __metal_driver_sifive_gpio0 * __metal_driver_sifive_uart0_pinmux(struct metal_uart *uart) +{ + if ((uintptr_t)uart == (uintptr_t)&__metal_dt_serial_10013000) { + return (struct __metal_driver_sifive_gpio0 *)&__metal_dt_gpio_10012000; + } + else if ((uintptr_t)uart == (uintptr_t)&__metal_dt_serial_10023000) { + return (struct __metal_driver_sifive_gpio0 *)&__metal_dt_gpio_10012000; + } + else { + return 0; + } +} + +static __inline__ unsigned long __metal_driver_sifive_uart0_pinmux_output_selector(struct metal_uart *uart) +{ + if ((uintptr_t)uart == (uintptr_t)&__metal_dt_serial_10013000) { + return 0; + } + else if ((uintptr_t)uart == (uintptr_t)&__metal_dt_serial_10023000) { + return 0; + } + else { + return 0; + } +} + +static __inline__ unsigned long __metal_driver_sifive_uart0_pinmux_source_selector(struct metal_uart *uart) +{ + if ((uintptr_t)uart == (uintptr_t)&__metal_dt_serial_10013000) { + return 196608; + } + else if ((uintptr_t)uart == (uintptr_t)&__metal_dt_serial_10023000) { + return 8650752; + } + else { + return 0; + } +} + + + +/* --------------------- sifive_simuart0 ------------ */ + + +/* --------------------- sifive_wdog0 ------------ */ +static __inline__ unsigned long __metal_driver_sifive_wdog0_control_base(const struct metal_watchdog *const watchdog) +{ + if ((uintptr_t)watchdog == (uintptr_t)&__metal_dt_aon_10000000) { + return METAL_SIFIVE_AON0_10000000_BASE_ADDRESS; + } + else { + return 0; + } +} + +static __inline__ unsigned long __metal_driver_sifive_wdog0_control_size(const struct metal_watchdog *const watchdog) +{ + if ((uintptr_t)watchdog == (uintptr_t)&__metal_dt_aon_10000000) { + return METAL_SIFIVE_AON0_10000000_SIZE; + } + else { + return 0; + } +} + +static __inline__ struct metal_interrupt * __metal_driver_sifive_wdog0_interrupt_parent(const struct metal_watchdog *const watchdog) +{ + if ((uintptr_t)watchdog == (uintptr_t)&__metal_dt_aon_10000000) { + return (struct metal_interrupt *)&__metal_dt_interrupt_controller_c000000.controller; + } + else { + return 0; + } +} + +static __inline__ int __metal_driver_sifive_wdog0_interrupt_line(const struct metal_watchdog *const watchdog) +{ + if ((uintptr_t)watchdog == (uintptr_t)&__metal_dt_aon_10000000) { + return 1; + } + else { + return 0; + } +} + +static __inline__ struct metal_clock * __metal_driver_sifive_wdog0_clock(const struct metal_watchdog *const watchdog) +{ + if ((uintptr_t)watchdog == (uintptr_t)&__metal_dt_aon_10000000) { + return (struct metal_clock *)&__metal_dt_clock_7.clock; + } + else { + return 0; + } +} + + + +/* --------------------- sifive_fe310_g000_hfrosc ------------ */ +static __inline__ struct metal_clock * __metal_driver_sifive_fe310_g000_hfrosc_ref(const struct metal_clock *clock) +{ + return (struct metal_clock *)&__metal_dt_clock_2.clock; +} + +static __inline__ struct __metal_driver_sifive_fe310_g000_prci * __metal_driver_sifive_fe310_g000_hfrosc_config_base(const struct metal_clock *clock) +{ + return (struct __metal_driver_sifive_fe310_g000_prci *)&__metal_dt_prci_10008000; +} + +static __inline__ const struct __metal_driver_vtable_sifive_fe310_g000_prci * __metal_driver_sifive_fe310_g000_hfrosc_config_vtable(struct metal_clock *clock) +{ + return &__metal_driver_vtable_sifive_fe310_g000_prci; +} + +static __inline__ long __metal_driver_sifive_fe310_g000_hfrosc_config_offset(const struct metal_clock *clock) +{ + return METAL_SIFIVE_FE310_G000_PRCI_HFROSCCFG; +} + + + +/* --------------------- sifive_fe310_g000_hfxosc ------------ */ +static __inline__ struct metal_clock * __metal_driver_sifive_fe310_g000_hfxosc_ref(const struct metal_clock *clock) +{ + return (struct metal_clock *)&__metal_dt_clock_0.clock; +} + +static __inline__ struct __metal_driver_sifive_fe310_g000_prci * __metal_driver_sifive_fe310_g000_hfxosc_config_base(const struct metal_clock *clock) +{ + return (struct __metal_driver_sifive_fe310_g000_prci *)&__metal_dt_prci_10008000; +} + +static __inline__ long __metal_driver_sifive_fe310_g000_hfxosc_config_offset(const struct metal_clock *clock) +{ + return METAL_SIFIVE_FE310_G000_PRCI_HFXOSCCFG; +} + + + +/* --------------------- sifive_fe310_g000_lfrosc ------------ */ +static __inline__ struct metal_clock * __metal_driver_sifive_fe310_g000_lfrosc_lfrosc(const struct metal_clock *clock) +{ + if ((uintptr_t)clock == (uintptr_t)&__metal_dt_clock_7) { + return (struct metal_clock *)&__metal_dt_clock_5.clock; + } + else { + return NULL; + } +} + +static __inline__ struct metal_clock * __metal_driver_sifive_fe310_g000_lfrosc_psdlfaltclk(const struct metal_clock *clock) +{ + if ((uintptr_t)clock == (uintptr_t)&__metal_dt_clock_7) { + return (struct metal_clock *)&__metal_dt_clock_6.clock; + } + else { + return NULL; + } +} + +static __inline__ unsigned long int __metal_driver_sifive_fe310_g000_lfrosc_config_reg(const struct metal_clock *clock) +{ + if ((uintptr_t)clock == (uintptr_t)&__metal_dt_clock_7) { + return 112; + } + else { + return 0; + } +} + +static __inline__ unsigned long int __metal_driver_sifive_fe310_g000_lfrosc_mux_reg(const struct metal_clock *clock) +{ + if ((uintptr_t)clock == (uintptr_t)&__metal_dt_clock_7) { + return 124; + } + else { + return 0; + } +} + + + +/* --------------------- sifive_fe310_g000_pll ------------ */ +static __inline__ struct metal_clock * __metal_driver_sifive_fe310_g000_pll_pllsel0(const struct metal_clock *clock) +{ + return (struct metal_clock *)&__metal_dt_clock_3.clock; +} + +static __inline__ struct metal_clock * __metal_driver_sifive_fe310_g000_pll_pllref(const struct metal_clock *clock) +{ + return (struct metal_clock *)&__metal_dt_clock_1.clock; +} + +static __inline__ struct __metal_driver_sifive_fe310_g000_prci * __metal_driver_sifive_fe310_g000_pll_divider_base(const struct metal_clock *clock) +{ + return (struct __metal_driver_sifive_fe310_g000_prci *)&__metal_dt_prci_10008000; +} + +static __inline__ long __metal_driver_sifive_fe310_g000_pll_divider_offset(const struct metal_clock *clock) +{ + return METAL_SIFIVE_FE310_G000_PRCI_PLLOUTDIV; +} + +static __inline__ struct __metal_driver_sifive_fe310_g000_prci * __metal_driver_sifive_fe310_g000_pll_config_base( ) +{ + return (struct __metal_driver_sifive_fe310_g000_prci *)&__metal_dt_prci_10008000; +} + +static __inline__ long __metal_driver_sifive_fe310_g000_pll_config_offset( ) +{ + return METAL_SIFIVE_FE310_G000_PRCI_PLLCFG; +} + +static __inline__ long __metal_driver_sifive_fe310_g000_pll_init_rate( ) +{ + return 16000000; +} + + + +/* --------------------- sifive_fe310_g000_prci ------------ */ +static __inline__ long __metal_driver_sifive_fe310_g000_prci_base( ) +{ + return METAL_SIFIVE_FE310_G000_PRCI_10008000_BASE_ADDRESS; +} + +static __inline__ long __metal_driver_sifive_fe310_g000_prci_size( ) +{ + return METAL_SIFIVE_FE310_G000_PRCI_10008000_SIZE; +} + +static __inline__ const struct __metal_driver_vtable_sifive_fe310_g000_prci * __metal_driver_sifive_fe310_g000_prci_vtable( ) +{ + return &__metal_driver_vtable_sifive_fe310_g000_prci; +} + + + +#define __METAL_DT_MAX_MEMORIES 3 + +struct metal_memory *__metal_memory_table[] __attribute__((weak)) = { + &__metal_dt_mem_dtim_80000000, + &__metal_dt_mem_itim_8000000, + &__metal_dt_mem_spi_10014000}; + +/* From serial@10013000 */ +#define __METAL_DT_STDOUT_UART_HANDLE (&__metal_dt_serial_10013000.uart) + +#define __METAL_DT_SERIAL_10013000_HANDLE (&__metal_dt_serial_10013000.uart) + +#define __METAL_DT_STDOUT_UART_BAUD 115200 + +/* From clint@2000000 */ +#define __METAL_DT_RISCV_CLINT0_HANDLE (&__metal_dt_clint_2000000.controller) + +#define __METAL_DT_CLINT_2000000_HANDLE (&__metal_dt_clint_2000000.controller) + +#define __METAL_DT_MAX_HARTS 1 + +#define __METAL_CPU_0_ICACHE_HANDLE 1 + +struct __metal_driver_cpu *__metal_cpu_table[] __attribute__((weak)) = { + &__metal_dt_cpu_0}; + +/* From interrupt_controller@c000000 */ +#define __METAL_DT_RISCV_PLIC0_HANDLE (&__metal_dt_interrupt_controller_c000000.controller) + +#define __METAL_DT_INTERRUPT_CONTROLLER_C000000_HANDLE (&__metal_dt_interrupt_controller_c000000.controller) + +#define __METAL_DT_PMP_HANDLE (&__metal_dt_pmp) + +#define __MEE_DT_MAX_GPIOS 1 + +struct __metal_driver_sifive_gpio0 *__metal_gpio_table[] __attribute__((weak)) = { + &__metal_dt_gpio_10012000}; + +#define __METAL_DT_MAX_BUTTONS 0 + +struct __metal_driver_sifive_gpio_button *__metal_button_table[] __attribute__((weak)) = { + NULL }; +#define __METAL_DT_MAX_LEDS 3 + +struct __metal_driver_sifive_gpio_led *__metal_led_table[] __attribute__((weak)) = { + &__metal_dt_led_0, + &__metal_dt_led_1, + &__metal_dt_led_2}; + +#define __METAL_DT_MAX_SWITCHES 0 + +struct __metal_driver_sifive_gpio_switch *__metal_switch_table[] __attribute__((weak)) = { + NULL }; +#define __METAL_DT_MAX_I2CS 1 + +struct __metal_driver_sifive_i2c0 *__metal_i2c_table[] __attribute__((weak)) = { + &__metal_dt_i2c_10016000}; + +#define __METAL_DT_MAX_PWMS 3 + +struct __metal_driver_sifive_pwm0 *__metal_pwm_table[] __attribute__((weak)) = { + &__metal_dt_pwm_10015000, + &__metal_dt_pwm_10025000, + &__metal_dt_pwm_10035000}; + +#define __METAL_DT_MAX_RTCS 1 + +struct __metal_driver_sifive_rtc0 *__metal_rtc_table[] __attribute__((weak)) = { + &__metal_dt_rtc_10000000}; + +#define __METAL_DT_MAX_SPIS 3 + +struct __metal_driver_sifive_spi0 *__metal_spi_table[] __attribute__((weak)) = { + &__metal_dt_spi_10014000, + &__metal_dt_spi_10024000, + &__metal_dt_spi_10034000}; + +#define __METAL_DT_MAX_UARTS 2 + +struct __metal_driver_sifive_uart0 *__metal_uart_table[] __attribute__((weak)) = { + &__metal_dt_serial_10013000, + &__metal_dt_serial_10023000}; + +#define __METAL_DT_MAX_SIMUARTS 0 + +struct __metal_driver_sifive_simuart0 *__metal_simuart_table[] __attribute__((weak)) = { + NULL }; +#define __METAL_DT_MAX_WDOGS 1 + +struct __metal_driver_sifive_wdog0 *__metal_wdog_table[] __attribute__((weak)) = { + &__metal_dt_aon_10000000}; + +/* From clock@4 */ +#define __METAL_DT_SIFIVE_FE310_G000_PLL_HANDLE (&__metal_dt_clock_4) + +#define __METAL_DT_CLOCK_4_HANDLE (&__metal_dt_clock_4) + +#endif /* MACROS_ELSE_METAL_H*/ + +#endif /* ! __METAL_MACHINE_MACROS */ + +#endif /* ! ASSEMBLY */ diff --git a/arch/riscv32/fe310/src/sparkfun_redboard/nonsecure/printf.c b/arch/riscv32/fe310/src/sparkfun_redboard/nonsecure/printf.c new file mode 100644 index 00000000..059bbd16 --- /dev/null +++ b/arch/riscv32/fe310/src/sparkfun_redboard/nonsecure/printf.c @@ -0,0 +1,923 @@ +/////////////////////////////////////////////////////////////////////////////// +// \author (c) Marco Paland (info@paland.com) +// 2014-2019, PALANDesign Hannover, Germany +// +// \license The MIT License (MIT) +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// +// \brief Tiny printf, sprintf and (v)snprintf implementation, optimized for speed on +// embedded systems with a very limited resources. These routines are thread +// safe and reentrant! +// Use this instead of the bloated standard/newlib printf cause these use +// malloc for printf (and may not be thread safe). +// +/////////////////////////////////////////////////////////////////////////////// + +#include +#include + +#include "printf.h" + + +// define this globally (e.g. gcc -DPRINTF_INCLUDE_CONFIG_H ...) to include the +// printf_config.h header file +// default: undefined +#ifdef PRINTF_INCLUDE_CONFIG_H +#include "printf_config.h" +#endif + + +// 'ntoa' conversion buffer size, this must be big enough to hold one converted +// numeric number including padded zeros (dynamically created on stack) +// default: 32 byte +#ifndef PRINTF_NTOA_BUFFER_SIZE +#define PRINTF_NTOA_BUFFER_SIZE 32U +#endif + +// 'ftoa' conversion buffer size, this must be big enough to hold one converted +// float number including padded zeros (dynamically created on stack) +// default: 32 byte +#ifndef PRINTF_FTOA_BUFFER_SIZE +#define PRINTF_FTOA_BUFFER_SIZE 32U +#endif + +// support for the floating point type (%f) +// default: activated +#ifndef PRINTF_DISABLE_SUPPORT_FLOAT +#define PRINTF_SUPPORT_FLOAT +#endif + +// support for exponential floating point notation (%e/%g) +// default: activated +#ifndef PRINTF_DISABLE_SUPPORT_EXPONENTIAL +#define PRINTF_SUPPORT_EXPONENTIAL +#endif + +// define the default floating point precision +// default: 6 digits +#ifndef PRINTF_DEFAULT_FLOAT_PRECISION +#define PRINTF_DEFAULT_FLOAT_PRECISION 6U +#endif + +// define the largest float suitable to print with %f +// default: 1e9 +#ifndef PRINTF_MAX_FLOAT +#define PRINTF_MAX_FLOAT 1e9 +#endif + +// support for the long long types (%llu or %p) +// default: activated +#ifndef PRINTF_DISABLE_SUPPORT_LONG_LONG +#define PRINTF_SUPPORT_LONG_LONG +#endif + +// support for the ptrdiff_t type (%t) +// ptrdiff_t is normally defined in as long or long long type +// default: activated +#ifndef PRINTF_DISABLE_SUPPORT_PTRDIFF_T +#define PRINTF_SUPPORT_PTRDIFF_T +#endif + +/////////////////////////////////////////////////////////////////////////////// + +// internal flag definitions +#define FLAGS_ZEROPAD (1U << 0U) +#define FLAGS_LEFT (1U << 1U) +#define FLAGS_PLUS (1U << 2U) +#define FLAGS_SPACE (1U << 3U) +#define FLAGS_HASH (1U << 4U) +#define FLAGS_UPPERCASE (1U << 5U) +#define FLAGS_CHAR (1U << 6U) +#define FLAGS_SHORT (1U << 7U) +#define FLAGS_LONG (1U << 8U) +#define FLAGS_LONG_LONG (1U << 9U) +#define FLAGS_PRECISION (1U << 10U) +#define FLAGS_ADAPT_EXP (1U << 11U) + + +// import float.h for DBL_MAX +#if defined(PRINTF_SUPPORT_FLOAT) +#include +#endif + + +// output function type +typedef void (*out_fct_type)(char character, void* buffer, size_t idx, size_t maxlen); + + +// wrapper (used as buffer) for output function type +typedef struct { + void (*fct)(char character, void* arg); + void* arg; +} out_fct_wrap_type; + + +// internal buffer output +static inline void _out_buffer(char character, void* buffer, size_t idx, size_t maxlen) +{ + if (idx < maxlen) { + ((char*)buffer)[idx] = character; + } +} + + +// internal null output +static inline void _out_null(char character, void* buffer, size_t idx, size_t maxlen) +{ + (void)character; (void)buffer; (void)idx; (void)maxlen; +} + + +// internal _putchar wrapper +static inline void _out_char(char character, void* buffer, size_t idx, size_t maxlen) +{ + (void)buffer; (void)idx; (void)maxlen; + if (character) { +// #ifdef C_SECURE_CODE +// volatile uint32_t* mytest = 0x50080000 + 0x84; +// *mytest &= ~(1 << 5); +// __putchar(character); +// #else + _putchar(character); +// #endif +// #ifdef C_SECURE_CODE +// *mytest |= (1 << 5); +// #endif + } +} + + +// internal output function wrapper +static inline void _out_fct(char character, void* buffer, size_t idx, size_t maxlen) +{ + (void)idx; (void)maxlen; + if (character) { + // buffer is the output fct pointer + ((out_fct_wrap_type*)buffer)->fct(character, ((out_fct_wrap_type*)buffer)->arg); + } +} + + +// internal secure strlen +// \return The length of the string (excluding the terminating 0) limited by 'maxsize' +static inline unsigned int _strnlen_s(const char* str, size_t maxsize) +{ + const char* s; + for (s = str; *s && maxsize--; ++s); + return (unsigned int)(s - str); +} + + +// internal test if char is a digit (0-9) +// \return true if char is a digit +static inline bool _is_digit(char ch) +{ + return (ch >= '0') && (ch <= '9'); +} + + +// internal ASCII string to unsigned int conversion +static unsigned int _atoi(const char** str) +{ + unsigned int i = 0U; + while (_is_digit(**str)) { + i = i * 10U + (unsigned int)(*((*str)++) - '0'); + } + return i; +} + + +// output the specified string in reverse, taking care of any zero-padding +static size_t _out_rev(out_fct_type out, char* buffer, size_t idx, size_t maxlen, const char* buf, size_t len, unsigned int width, unsigned int flags) +{ + const size_t start_idx = idx; + + // pad spaces up to given width + if (!(flags & FLAGS_LEFT) && !(flags & FLAGS_ZEROPAD)) { + for (size_t i = len; i < width; i++) { + out(' ', buffer, idx++, maxlen); + } + } + + // reverse string + while (len) { + out(buf[--len], buffer, idx++, maxlen); + } + + // append pad spaces up to given width + if (flags & FLAGS_LEFT) { + while (idx - start_idx < width) { + out(' ', buffer, idx++, maxlen); + } + } + + return idx; +} + + +// internal itoa format +static size_t _ntoa_format(out_fct_type out, char* buffer, size_t idx, size_t maxlen, char* buf, size_t len, bool negative, unsigned int base, unsigned int prec, unsigned int width, unsigned int flags) +{ + // pad leading zeros + if (!(flags & FLAGS_LEFT)) { + if (width && (flags & FLAGS_ZEROPAD) && (negative || (flags & (FLAGS_PLUS | FLAGS_SPACE)))) { + width--; + } + while ((len < prec) && (len < PRINTF_NTOA_BUFFER_SIZE)) { + buf[len++] = '0'; + } + while ((flags & FLAGS_ZEROPAD) && (len < width) && (len < PRINTF_NTOA_BUFFER_SIZE)) { + buf[len++] = '0'; + } + } + + // handle hash + if (flags & FLAGS_HASH) { + if (!(flags & FLAGS_PRECISION) && len && ((len == prec) || (len == width))) { + len--; + if (len && (base == 16U)) { + len--; + } + } + if ((base == 16U) && !(flags & FLAGS_UPPERCASE) && (len < PRINTF_NTOA_BUFFER_SIZE)) { + buf[len++] = 'x'; + } + else if ((base == 16U) && (flags & FLAGS_UPPERCASE) && (len < PRINTF_NTOA_BUFFER_SIZE)) { + buf[len++] = 'X'; + } + else if ((base == 2U) && (len < PRINTF_NTOA_BUFFER_SIZE)) { + buf[len++] = 'b'; + } + if (len < PRINTF_NTOA_BUFFER_SIZE) { + buf[len++] = '0'; + } + } + + if (len < PRINTF_NTOA_BUFFER_SIZE) { + if (negative) { + buf[len++] = '-'; + } + else if (flags & FLAGS_PLUS) { + buf[len++] = '+'; // ignore the space if the '+' exists + } + else if (flags & FLAGS_SPACE) { + buf[len++] = ' '; + } + } + + return _out_rev(out, buffer, idx, maxlen, buf, len, width, flags); +} + + +// internal itoa for 'long' type +static size_t _ntoa_long(out_fct_type out, char* buffer, size_t idx, size_t maxlen, unsigned long value, bool negative, unsigned long base, unsigned int prec, unsigned int width, unsigned int flags) +{ + char buf[PRINTF_NTOA_BUFFER_SIZE]; + size_t len = 0U; + + // no hash for 0 values + if (!value) { + flags &= ~FLAGS_HASH; + } + + // write if precision != 0 and value is != 0 + if (!(flags & FLAGS_PRECISION) || value) { + do { + const char digit = (char)(value % base); + buf[len++] = digit < 10 ? '0' + digit : (flags & FLAGS_UPPERCASE ? 'A' : 'a') + digit - 10; + value /= base; + } while (value && (len < PRINTF_NTOA_BUFFER_SIZE)); + } + + return _ntoa_format(out, buffer, idx, maxlen, buf, len, negative, (unsigned int)base, prec, width, flags); +} + + +// internal itoa for 'long long' type +#if defined(PRINTF_SUPPORT_LONG_LONG) +static size_t _ntoa_long_long(out_fct_type out, char* buffer, size_t idx, size_t maxlen, unsigned long long value, bool negative, unsigned long long base, unsigned int prec, unsigned int width, unsigned int flags) +{ + char buf[PRINTF_NTOA_BUFFER_SIZE]; + size_t len = 0U; + + // no hash for 0 values + if (!value) { + flags &= ~FLAGS_HASH; + } + + // write if precision != 0 and value is != 0 + if (!(flags & FLAGS_PRECISION) || value) { + do { + const char digit = (char)(value % base); + buf[len++] = digit < 10 ? '0' + digit : (flags & FLAGS_UPPERCASE ? 'A' : 'a') + digit - 10; + value /= base; + } while (value && (len < PRINTF_NTOA_BUFFER_SIZE)); + } + + return _ntoa_format(out, buffer, idx, maxlen, buf, len, negative, (unsigned int)base, prec, width, flags); +} +#endif // PRINTF_SUPPORT_LONG_LONG + + +#if defined(PRINTF_SUPPORT_FLOAT) + +#if defined(PRINTF_SUPPORT_EXPONENTIAL) +// forward declaration so that _ftoa can switch to exp notation for values > PRINTF_MAX_FLOAT +static size_t _etoa(out_fct_type out, char* buffer, size_t idx, size_t maxlen, double value, unsigned int prec, unsigned int width, unsigned int flags); +#endif + + +// internal ftoa for fixed decimal floating point +static size_t _ftoa(out_fct_type out, char* buffer, size_t idx, size_t maxlen, double value, unsigned int prec, unsigned int width, unsigned int flags) +{ + char buf[PRINTF_FTOA_BUFFER_SIZE]; + size_t len = 0U; + double diff = 0.0; + + // powers of 10 + static const double pow10[] = { 1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000, 1000000000 }; + + // test for special values + if (value != value) + return _out_rev(out, buffer, idx, maxlen, "nan", 3, width, flags); + if (value < -DBL_MAX) + return _out_rev(out, buffer, idx, maxlen, "fni-", 4, width, flags); + if (value > DBL_MAX) + return _out_rev(out, buffer, idx, maxlen, (flags & FLAGS_PLUS) ? "fni+" : "fni", (flags & FLAGS_PLUS) ? 4U : 3U, width, flags); + + // test for very large values + // standard printf behavior is to print EVERY whole number digit -- which could be 100s of characters overflowing your buffers == bad + if ((value > PRINTF_MAX_FLOAT) || (value < -PRINTF_MAX_FLOAT)) { +#if defined(PRINTF_SUPPORT_EXPONENTIAL) + return _etoa(out, buffer, idx, maxlen, value, prec, width, flags); +#else + return 0U; +#endif + } + + // test for negative + bool negative = false; + if (value < 0) { + negative = true; + value = 0 - value; + } + + // set default precision, if not set explicitly + if (!(flags & FLAGS_PRECISION)) { + prec = PRINTF_DEFAULT_FLOAT_PRECISION; + } + // limit precision to 9, cause a prec >= 10 can lead to overflow errors + while ((len < PRINTF_FTOA_BUFFER_SIZE) && (prec > 9U)) { + buf[len++] = '0'; + prec--; + } + + int whole = (int)value; + double tmp = (value - whole) * pow10[prec]; + unsigned long frac = (unsigned long)tmp; + diff = tmp - frac; + + if (diff > 0.5) { + ++frac; + // handle rollover, e.g. case 0.99 with prec 1 is 1.0 + if (frac >= pow10[prec]) { + frac = 0; + ++whole; + } + } + else if (diff < 0.5) { + } + else if ((frac == 0U) || (frac & 1U)) { + // if halfway, round up if odd OR if last digit is 0 + ++frac; + } + + if (prec == 0U) { + diff = value - (double)whole; + if ((!(diff < 0.5) || (diff > 0.5)) && (whole & 1)) { + // exactly 0.5 and ODD, then round up + // 1.5 -> 2, but 2.5 -> 2 + ++whole; + } + } + else { + unsigned int count = prec; + // now do fractional part, as an unsigned number + while (len < PRINTF_FTOA_BUFFER_SIZE) { + --count; + buf[len++] = (char)(48U + (frac % 10U)); + if (!(frac /= 10U)) { + break; + } + } + // add extra 0s + while ((len < PRINTF_FTOA_BUFFER_SIZE) && (count-- > 0U)) { + buf[len++] = '0'; + } + if (len < PRINTF_FTOA_BUFFER_SIZE) { + // add decimal + buf[len++] = '.'; + } + } + + // do whole part, number is reversed + while (len < PRINTF_FTOA_BUFFER_SIZE) { + buf[len++] = (char)(48 + (whole % 10)); + if (!(whole /= 10)) { + break; + } + } + + // pad leading zeros + if (!(flags & FLAGS_LEFT) && (flags & FLAGS_ZEROPAD)) { + if (width && (negative || (flags & (FLAGS_PLUS | FLAGS_SPACE)))) { + width--; + } + while ((len < width) && (len < PRINTF_FTOA_BUFFER_SIZE)) { + buf[len++] = '0'; + } + } + + if (len < PRINTF_FTOA_BUFFER_SIZE) { + if (negative) { + buf[len++] = '-'; + } + else if (flags & FLAGS_PLUS) { + buf[len++] = '+'; // ignore the space if the '+' exists + } + else if (flags & FLAGS_SPACE) { + buf[len++] = ' '; + } + } + + return _out_rev(out, buffer, idx, maxlen, buf, len, width, flags); +} + + +#if defined(PRINTF_SUPPORT_EXPONENTIAL) +// internal ftoa variant for exponential floating-point type, contributed by Martijn Jasperse +static size_t _etoa(out_fct_type out, char* buffer, size_t idx, size_t maxlen, double value, unsigned int prec, unsigned int width, unsigned int flags) +{ + // check for NaN and special values + if ((value != value) || (value > DBL_MAX) || (value < -DBL_MAX)) { + return _ftoa(out, buffer, idx, maxlen, value, prec, width, flags); + } + + // determine the sign + const bool negative = value < 0; + if (negative) { + value = -value; + } + + // default precision + if (!(flags & FLAGS_PRECISION)) { + prec = PRINTF_DEFAULT_FLOAT_PRECISION; + } + + // determine the decimal exponent + // based on the algorithm by David Gay (https://www.ampl.com/netlib/fp/dtoa.c) + union { + uint64_t U; + double F; + } conv; + + conv.F = value; + int exp2 = (int)((conv.U >> 52U) & 0x07FFU) - 1023; // effectively log2 + conv.U = (conv.U & ((1ULL << 52U) - 1U)) | (1023ULL << 52U); // drop the exponent so conv.F is now in [1,2) + // now approximate log10 from the log2 integer part and an expansion of ln around 1.5 + int expval = (int)(0.1760912590558 + exp2 * 0.301029995663981 + (conv.F - 1.5) * 0.289529654602168); + // now we want to compute 10^expval but we want to be sure it won't overflow + exp2 = (int)(expval * 3.321928094887362 + 0.5); + const double z = expval * 2.302585092994046 - exp2 * 0.6931471805599453; + const double z2 = z * z; + conv.U = (uint64_t)(exp2 + 1023) << 52U; + // compute exp(z) using continued fractions, see https://en.wikipedia.org/wiki/Exponential_function#Continued_fractions_for_ex + conv.F *= 1 + 2 * z / (2 - z + (z2 / (6 + (z2 / (10 + z2 / 14))))); + // correct for rounding errors + if (value < conv.F) { + expval--; + conv.F /= 10; + } + + // the exponent format is "%+03d" and largest value is "307", so set aside 4-5 characters + unsigned int minwidth = ((expval < 100) && (expval > -100)) ? 4U : 5U; + + // in "%g" mode, "prec" is the number of *significant figures* not decimals + if (flags & FLAGS_ADAPT_EXP) { + // do we want to fall-back to "%f" mode? + if ((value >= 1e-4) && (value < 1e6)) { + if ((int)prec > expval) { + prec = (unsigned)((int)prec - expval - 1); + } + else { + prec = 0; + } + flags |= FLAGS_PRECISION; // make sure _ftoa respects precision + // no characters in exponent + minwidth = 0U; + expval = 0; + } + else { + // we use one sigfig for the whole part + if ((prec > 0) && (flags & FLAGS_PRECISION)) { + --prec; + } + } + } + + // will everything fit? + unsigned int fwidth = width; + if (width > minwidth) { + // we didn't fall-back so subtract the characters required for the exponent + fwidth -= minwidth; + } else { + // not enough characters, so go back to default sizing + fwidth = 0U; + } + if ((flags & FLAGS_LEFT) && minwidth) { + // if we're padding on the right, DON'T pad the floating part + fwidth = 0U; + } + + // rescale the float value + if (expval) { + value /= conv.F; + } + + // output the floating part + const size_t start_idx = idx; + idx = _ftoa(out, buffer, idx, maxlen, negative ? -value : value, prec, fwidth, flags & ~FLAGS_ADAPT_EXP); + + // output the exponent part + if (minwidth) { + // output the exponential symbol + out((flags & FLAGS_UPPERCASE) ? 'E' : 'e', buffer, idx++, maxlen); + // output the exponent value + idx = _ntoa_long(out, buffer, idx, maxlen, (expval < 0) ? -expval : expval, expval < 0, 10, 0, minwidth-1, FLAGS_ZEROPAD | FLAGS_PLUS); + // might need to right-pad spaces + if (flags & FLAGS_LEFT) { + while (idx - start_idx < width) out(' ', buffer, idx++, maxlen); + } + } + return idx; +} +#endif // PRINTF_SUPPORT_EXPONENTIAL +#endif // PRINTF_SUPPORT_FLOAT + + +// internal vsnprintf +static int _vsnprintf(out_fct_type out, char* buffer, const size_t maxlen, const char* format, va_list va) +{ + unsigned int flags, width, precision, n; + size_t idx = 0U; + + if (!buffer) { + // use null output function + out = _out_null; + } + + while (*format) + { + // format specifier? %[flags][width][.precision][length] + if (*format != '%') { + // no + out(*format, buffer, idx++, maxlen); + format++; + continue; + } + else { + // yes, evaluate it + format++; + } + + // evaluate flags + flags = 0U; + do { + switch (*format) { + case '0': flags |= FLAGS_ZEROPAD; format++; n = 1U; break; + case '-': flags |= FLAGS_LEFT; format++; n = 1U; break; + case '+': flags |= FLAGS_PLUS; format++; n = 1U; break; + case ' ': flags |= FLAGS_SPACE; format++; n = 1U; break; + case '#': flags |= FLAGS_HASH; format++; n = 1U; break; + default : n = 0U; break; + } + } while (n); + + // evaluate width field + width = 0U; + if (_is_digit(*format)) { + width = _atoi(&format); + } + else if (*format == '*') { + const int w = va_arg(va, int); + if (w < 0) { + flags |= FLAGS_LEFT; // reverse padding + width = (unsigned int)-w; + } + else { + width = (unsigned int)w; + } + format++; + } + + // evaluate precision field + precision = 0U; + if (*format == '.') { + flags |= FLAGS_PRECISION; + format++; + if (_is_digit(*format)) { + precision = _atoi(&format); + } + else if (*format == '*') { + const int prec = (int)va_arg(va, int); + precision = prec > 0 ? (unsigned int)prec : 0U; + format++; + } + } + + // evaluate length field + switch (*format) { + case 'l' : + flags |= FLAGS_LONG; + format++; + if (*format == 'l') { + flags |= FLAGS_LONG_LONG; + format++; + } + break; + case 'h' : + flags |= FLAGS_SHORT; + format++; + if (*format == 'h') { + flags |= FLAGS_CHAR; + format++; + } + break; +#if defined(PRINTF_SUPPORT_PTRDIFF_T) + case 't' : + flags |= (sizeof(ptrdiff_t) == sizeof(long) ? FLAGS_LONG : FLAGS_LONG_LONG); + format++; + break; +#endif + case 'j' : + flags |= (sizeof(intmax_t) == sizeof(long) ? FLAGS_LONG : FLAGS_LONG_LONG); + format++; + break; + case 'z' : + flags |= (sizeof(size_t) == sizeof(long) ? FLAGS_LONG : FLAGS_LONG_LONG); + format++; + break; + default : + break; + } + + // evaluate specifier + switch (*format) { + case 'd' : + case 'i' : + case 'u' : + case 'x' : + case 'X' : + case 'o' : + case 'b' : { + // set the base + unsigned int base; + if (*format == 'x' || *format == 'X') { + base = 16U; + } + else if (*format == 'o') { + base = 8U; + } + else if (*format == 'b') { + base = 2U; + } + else { + base = 10U; + flags &= ~FLAGS_HASH; // no hash for dec format + } + // uppercase + if (*format == 'X') { + flags |= FLAGS_UPPERCASE; + } + + // no plus or space flag for u, x, X, o, b + if ((*format != 'i') && (*format != 'd')) { + flags &= ~(FLAGS_PLUS | FLAGS_SPACE); + } + + // ignore '0' flag when precision is given + if (flags & FLAGS_PRECISION) { + flags &= ~FLAGS_ZEROPAD; + } + + // convert the integer + if ((*format == 'i') || (*format == 'd')) { + // signed + if (flags & FLAGS_LONG_LONG) { +#if defined(PRINTF_SUPPORT_LONG_LONG) + const long long value = va_arg(va, long long); + idx = _ntoa_long_long(out, buffer, idx, maxlen, (unsigned long long)(value > 0 ? value : 0 - value), value < 0, base, precision, width, flags); +#endif + } + else if (flags & FLAGS_LONG) { + const long value = va_arg(va, long); + idx = _ntoa_long(out, buffer, idx, maxlen, (unsigned long)(value > 0 ? value : 0 - value), value < 0, base, precision, width, flags); + } + else { + const int value = (flags & FLAGS_CHAR) ? (char)va_arg(va, int) : (flags & FLAGS_SHORT) ? (short int)va_arg(va, int) : va_arg(va, int); + idx = _ntoa_long(out, buffer, idx, maxlen, (unsigned int)(value > 0 ? value : 0 - value), value < 0, base, precision, width, flags); + } + } + else { + // unsigned + if (flags & FLAGS_LONG_LONG) { +#if defined(PRINTF_SUPPORT_LONG_LONG) + idx = _ntoa_long_long(out, buffer, idx, maxlen, va_arg(va, unsigned long long), false, base, precision, width, flags); +#endif + } + else if (flags & FLAGS_LONG) { + idx = _ntoa_long(out, buffer, idx, maxlen, va_arg(va, unsigned long), false, base, precision, width, flags); + } + else { + const unsigned int value = (flags & FLAGS_CHAR) ? (unsigned char)va_arg(va, unsigned int) : (flags & FLAGS_SHORT) ? (unsigned short int)va_arg(va, unsigned int) : va_arg(va, unsigned int); + idx = _ntoa_long(out, buffer, idx, maxlen, value, false, base, precision, width, flags); + } + } + format++; + break; + } +#if defined(PRINTF_SUPPORT_FLOAT) + case 'f' : + case 'F' : + if (*format == 'F') flags |= FLAGS_UPPERCASE; + idx = _ftoa(out, buffer, idx, maxlen, va_arg(va, double), precision, width, flags); + format++; + break; +#if defined(PRINTF_SUPPORT_EXPONENTIAL) + case 'e': + case 'E': + case 'g': + case 'G': + if ((*format == 'g')||(*format == 'G')) flags |= FLAGS_ADAPT_EXP; + if ((*format == 'E')||(*format == 'G')) flags |= FLAGS_UPPERCASE; + idx = _etoa(out, buffer, idx, maxlen, va_arg(va, double), precision, width, flags); + format++; + break; +#endif // PRINTF_SUPPORT_EXPONENTIAL +#endif // PRINTF_SUPPORT_FLOAT + case 'c' : { + unsigned int l = 1U; + // pre padding + if (!(flags & FLAGS_LEFT)) { + while (l++ < width) { + out(' ', buffer, idx++, maxlen); + } + } + // char output + out((char)va_arg(va, int), buffer, idx++, maxlen); + // post padding + if (flags & FLAGS_LEFT) { + while (l++ < width) { + out(' ', buffer, idx++, maxlen); + } + } + format++; + break; + } + + case 's' : { + const char* p = va_arg(va, char*); + unsigned int l = _strnlen_s(p, precision ? precision : (size_t)-1); + // pre padding + if (flags & FLAGS_PRECISION) { + l = (l < precision ? l : precision); + } + if (!(flags & FLAGS_LEFT)) { + while (l++ < width) { + out(' ', buffer, idx++, maxlen); + } + } + // string output + while ((*p != 0) && (!(flags & FLAGS_PRECISION) || precision--)) { + out(*(p++), buffer, idx++, maxlen); + } + // post padding + if (flags & FLAGS_LEFT) { + while (l++ < width) { + out(' ', buffer, idx++, maxlen); + } + } + format++; + break; + } + + case 'p' : { + width = sizeof(void*) * 2U; + flags |= FLAGS_ZEROPAD | FLAGS_UPPERCASE; +#if defined(PRINTF_SUPPORT_LONG_LONG) + const bool is_ll = sizeof(uintptr_t) == sizeof(long long); + if (is_ll) { + idx = _ntoa_long_long(out, buffer, idx, maxlen, (uintptr_t)va_arg(va, void*), false, 16U, precision, width, flags); + } + else { +#endif + idx = _ntoa_long(out, buffer, idx, maxlen, (unsigned long)((uintptr_t)va_arg(va, void*)), false, 16U, precision, width, flags); +#if defined(PRINTF_SUPPORT_LONG_LONG) + } +#endif + format++; + break; + } + + case '%' : + out('%', buffer, idx++, maxlen); + format++; + break; + + default : + out(*format, buffer, idx++, maxlen); + format++; + break; + } + } + + // termination + out((char)0, buffer, idx < maxlen ? idx : maxlen - 1U, maxlen); + + // return written chars without terminating \0 + return (int)idx; +} + + +/////////////////////////////////////////////////////////////////////////////// + +int printf_(const char* format, ...) +{ + va_list va; + va_start(va, format); + char buffer[1]; + const int ret = _vsnprintf(_out_char, buffer, (size_t)-1, format, va); + va_end(va); + return ret; +} + + +int sprintf_(char* buffer, const char* format, ...) +{ + va_list va; + va_start(va, format); + const int ret = _vsnprintf(_out_buffer, buffer, (size_t)-1, format, va); + va_end(va); + return ret; +} + + +int snprintf_(char* buffer, size_t count, const char* format, ...) +{ + va_list va; + va_start(va, format); + const int ret = _vsnprintf(_out_buffer, buffer, count, format, va); + va_end(va); + return ret; +} + + +int vprintf_(const char* format, va_list va) +{ + char buffer[1]; + return _vsnprintf(_out_char, buffer, (size_t)-1, format, va); +} + + +int vsnprintf_(char* buffer, size_t count, const char* format, va_list va) +{ + return _vsnprintf(_out_buffer, buffer, count, format, va); +} + + +int fctprintf(void (*out)(char character, void* arg), void* arg, const char* format, ...) +{ + va_list va; + va_start(va, format); + const out_fct_wrap_type out_fct_wrap = { out, arg }; + const int ret = _vsnprintf(_out_fct, (char*)(uintptr_t)&out_fct_wrap, (size_t)-1, format, va); + va_end(va); + return ret; +} diff --git a/arch/riscv32/fe310/src/sparkfun_redboard/nonsecure/printf.h b/arch/riscv32/fe310/src/sparkfun_redboard/nonsecure/printf.h new file mode 100644 index 00000000..1e3e2794 --- /dev/null +++ b/arch/riscv32/fe310/src/sparkfun_redboard/nonsecure/printf.h @@ -0,0 +1,121 @@ +/////////////////////////////////////////////////////////////////////////////// +// \author (c) Marco Paland (info@paland.com) +// 2014-2019, PALANDesign Hannover, Germany +// +// \license The MIT License (MIT) +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// +// \brief Tiny printf, sprintf and snprintf implementation, optimized for speed on +// embedded systems with a very limited resources. +// Use this instead of bloated standard/newlib printf. +// These routines are thread safe and reentrant. +// +/////////////////////////////////////////////////////////////////////////////// + +#ifndef _PRINTF_H_ +#define _PRINTF_H_ + +#include +#include + + +#ifdef __cplusplus +extern "C" { +#endif + + +/** + * Output a character to a custom device like UART, used by the printf() function + * This function is declared here only. You have to write your custom implementation somewhere + * \param character Character to output + */ +// #ifdef C_SECURE_CODE +// void __putchar(char character); +// #else +void _putchar(char character); +// #endif + + +/** + * Tiny printf implementation + * You have to implement _putchar if you use printf() + * To avoid conflicts with the regular printf() API it is overridden by macro defines + * and internal underscore-appended functions like printf_() are used + * \param format A string that specifies the format of the output + * \return The number of characters that are written into the array, not counting the terminating null character + */ +#define printf printf_ +int printf_(const char* format, ...); + + +/** + * Tiny sprintf implementation + * Due to security reasons (buffer overflow) YOU SHOULD CONSIDER USING (V)SNPRINTF INSTEAD! + * \param buffer A pointer to the buffer where to store the formatted string. MUST be big enough to store the output! + * \param format A string that specifies the format of the output + * \return The number of characters that are WRITTEN into the buffer, not counting the terminating null character + */ +#define sprintf sprintf_ +int sprintf_(char* buffer, const char* format, ...); + + +/** + * Tiny snprintf/vsnprintf implementation + * \param buffer A pointer to the buffer where to store the formatted string + * \param count The maximum number of characters to store in the buffer, including a terminating null character + * \param format A string that specifies the format of the output + * \param va A value identifying a variable arguments list + * \return The number of characters that COULD have been written into the buffer, not counting the terminating + * null character. A value equal or larger than count indicates truncation. Only when the returned value + * is non-negative and less than count, the string has been completely written. + */ +#define snprintf snprintf_ +#define vsnprintf vsnprintf_ +int snprintf_(char* buffer, size_t count, const char* format, ...); +int vsnprintf_(char* buffer, size_t count, const char* format, va_list va); + + +/** + * Tiny vprintf implementation + * \param format A string that specifies the format of the output + * \param va A value identifying a variable arguments list + * \return The number of characters that are WRITTEN into the buffer, not counting the terminating null character + */ +#define vprintf vprintf_ +int vprintf_(const char* format, va_list va); + + +/** + * printf with output function + * You may use this as dynamic alternative to printf() with its fixed _putchar() output + * \param out An output function which takes one character and an argument pointer + * \param arg An argument pointer for user data passed to output function + * \param format A string that specifies the format of the output + * \return The number of characters that are sent to the output function, not counting the terminating null character + */ +int fctprintf(void (*out)(char character, void* arg), void* arg, const char* format, ...); + + +#ifdef __cplusplus +} +#endif + + +#endif // _PRINTF_H_ diff --git a/arch/riscv32/fe310/src/sparkfun_redboard/secure/Make.defs b/arch/riscv32/fe310/src/sparkfun_redboard/secure/Make.defs new file mode 100644 index 00000000..abc1cbb6 --- /dev/null +++ b/arch/riscv32/fe310/src/sparkfun_redboard/secure/Make.defs @@ -0,0 +1,70 @@ +############################################################################ +# arch/cortex-m23/m2351/src/numaker_pfm_m2351/secure/Make.defs +# +# Copyright (c) 2018-2019 Samsung Electronics Co., Ltd. All Rights Reserved. +# Author: Taras Drozdovskyi t.drozdovsky@samsung.com +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +############################################################################ + +-include $(TOPDIR)/Make.defs +-include $(TOPDIR)/.config + +HEAD_ASRC = + +# Common m2351 files + +CMN_ASRCS = + +CMN_CSRCS = ../../../../../../tee/lib/libutee/tee_api_operations.c \ + ../../../../../../tee/lib/libutee/tee_api.c \ + ../../../../../../tee/lib/libutee/tee_api_panic.c \ + ../../../../../../tee/lib/libutee/tee_user_mem.c \ + ../../../../../../tee/lib/libutee/tee_api_objects.c \ + ../../../../../../tee/lib/libutils/ext/trace.c \ + ../../../../../../tee/lib/libutils/ext/buf_compare_ct.c \ + ../../../../../../tee/tee/tee_svc_cryp.c \ + ../../../../../../tee/tee/tee_cryp_utl.c \ + ../../../../../../tee/tee/tee_svc.c \ + ../../../../../../tee/tee/tee_obj.c \ + ../../../../../../tee/tee/tee_pobj.c \ + ../../../../../../tee/tee/uuid.c \ + ../../../../../../tee/kernel/entry_std.c \ + ../../../../../../tee/kernel/pseudo_ta.c \ + ../../../../../../tee/kernel/user_ta.c \ + ../../../../../../tee/kernel/tee_ta_manager.c + +# Required CHIP files + +CHIP_ASRCS_S = ../../Device/Nuvoton/M2351/Source/GCC/startup_M2351.S + +CHIP_CSRCS_S = ../../Device/Nuvoton/M2351/Source/system_M2351.c \ + ../../Device/Nuvoton/M2351/Source/GCC/_syscalls.c \ + main.c + +ifeq ($(CONFIG_APPS_HELLO_WORLD),y) +CHIP_CSRCS_S += ../../../../../../apps/hello_world/ta/hello_world_ta.c +endif + +ifeq ($(CONFIG_APPS_AES),y) +CHIP_CSRCS_S += ../../../../../../apps/aes/ta/aes_ta.c +endif + +ifeq ($(CONFIG_APPS_HOTP),y) +CHIP_CSRCS_S += ../../../../../../apps/hotp/ta/hotp_ta.c +endif + +ifeq ($(CONFIG_APPS_TEST),y) +CHIP_CSRCS_S += ../../../../../../apps/test/ta/test_ta.c +endif diff --git a/arch/riscv32/fe310/src/sparkfun_redboard/secure/Makefile b/arch/riscv32/fe310/src/sparkfun_redboard/secure/Makefile new file mode 100644 index 00000000..cc825c8a --- /dev/null +++ b/arch/riscv32/fe310/src/sparkfun_redboard/secure/Makefile @@ -0,0 +1,97 @@ +############################################################################ +# arch/cortex-m23/m2351/src/numaker_pfm_m2351/secure/Makefile +# +# Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved. +# Author: Taras Drozdovskyi t.drozdovsky@samsung.com +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +############################################################################ + +-include $(TOPDIR)/Make.defs +-include $(TOPDIR)/.config +-include Make.defs + +CFLAGS += -I../ -I./ +CFLAGS += -I$(TOPDIR)/include/mtower +CFLAGS += -I../../CMSIS/Include +CFLAGS += -I../../StdDriver/inc +CFLAGS += -I../../Device/Nuvoton/M2351/Include +CFLAGS += -I$(TOPDIR)/tee_client/public +CFLAGS += -I$(TOPDIR)/tee_client/libteec/include/freertos +CFLAGS += -I$(TOPDIR)/tee/lib/libutee/include + +CFLAGS += -I$(TOPDIR)/tee/lib/libutils/ext/include +CFLAGS += -I$(TOPDIR)/tee/lib/libutils/isoc/include +CFLAGS += -I$(TOPDIR)/tee/include + +ifeq ($(CONFIG_APPS_HELLO_WORLD),y) +CFLAGS += -I$(TOPDIR)/apps/hello_world +endif + +ifeq ($(CONFIG_APPS_AES),y) +CFLAGS += -I$(TOPDIR)/apps/aes +endif + +ifeq ($(CONFIG_APPS_HOTP),y) +CFLAGS += -I$(TOPDIR)/apps/hotp +endif + +ifeq ($(CONFIG_APPS_TEST),y) +CFLAGS += -I$(TOPDIR)/apps/test +endif + +CFLAGS += -DDEBUG_PORT=$(subst ",,$(CONFIG_SECURE_DEBUG_UART)) + +OBJDIR = $(TOPDIR)/build/secure$(subst $(TOPDIR),,$(shell pwd)) + +MTOWER_S = $(TOPDIR)/mtower_s$(EXEEXT) + +ASRCS_S = $(CHIP_ASRCS_S) $(CMN_ASRCS) +AOBJS_S = $(addprefix $(OBJDIR)/, $(ASRCS_S:.S=$(OBJEXT))) + +CSRCS_S = $(CHIP_CSRCS_S) $(CMN_CSRCS) +COBJS_S = $(addprefix $(OBJDIR)/, $(CSRCS_S:.c=$(OBJEXT))) + +OBJS_S = $(AOBJS_S) $(COBJS_S) + +LIBPATHS = -L. -L"$(TOPDIR)/lib" + +LIBS = -lm -lc -lnosys -lm2351_StdDriver_s -lcrypt_s + +$(AOBJS_S): $(OBJDIR)/%$(OBJEXT): %.S + $(Q) mkdir -p $(OBJDIR)/$(dir $<) + @echo "AS: $<" + $(Q) $(CC) -c $(AFLAGS) -mcmse -DHeap_Size=0x00001400 -DStack_Size=0x00000B00 $< -o $@ + +$(COBJS_S): $(OBJDIR)/%$(OBJEXT): %.c + $(Q) mkdir -p $(OBJDIR)/$(dir $<) + @echo "CC: $<" + $(Q) $(CC) -c $(CFLAGS) -mcmse $< -o $@ + +mtower_s$(EXEEXT): $(OBJS_S) + $(Q) $(MAKE) -C ../../StdDriver/src/ TOPDIR="$(TOPDIR)" WORLD=secure libm2351_StdDriver_s.a + $(Q) $(MAKE) -C $(TOPDIR)/crypto/ TOPDIR="$(TOPDIR)" WORLD=secure libcrypt_s.a + @echo "CC: mTower_s$(EXEEXT)" + $(Q) $(CC) $(CFLAGS) -mcmse -Wl,--section-start=.text=$(CONFIG_START_ADDRESS_BL32) -Tsecure.ld $(OBJS_S) $(LIBPATHS) $(LIBS) -o $(OBJDIR)/bl32.elf +# $(Q) $(CC) $(CFLAGS) -Wl,--unresolved-symbols=ignore-all -Wl,--section-start=.gnu.sgstubs=0x3f000 -Wl,--out-implib=$(OBJDIR)/libnsc$(LIBEXT) -Wl,--cmse-implib $(OBJS_S) $(LIBPATHS) $(LIBS) + $(Q) $(CC) $(CFLAGS) -Wl,--section-start=.text=$(CONFIG_START_ADDRESS_BL32) -Wl,--unresolved-symbols=ignore-all -Wl,--out-implib=$(OBJDIR)/libnsc$(LIBEXT) -Wl,--cmse-implib -Tsecure.ld $(OBJS_S) $(LIBPATHS) $(LIBS) + $(Q) rm -f a.out + $(Q) $(OBJCOPY) -S -O binary $(OBJDIR)/bl32.elf $(OBJDIR)/bl32.bin + $(Q) cp -f $(OBJDIR)/libnsc$(LIBEXT) $(TOPDIR)/lib/ + +clean: + $(Q) $(MAKE) -C ../../NuBL2 TOPDIR="$(TOPDIR)" clean + +distclean: clean + diff --git a/arch/riscv32/fe310/src/sparkfun_redboard/secure/main.c b/arch/riscv32/fe310/src/sparkfun_redboard/secure/main.c new file mode 100644 index 00000000..0ce46d86 --- /dev/null +++ b/arch/riscv32/fe310/src/sparkfun_redboard/secure/main.c @@ -0,0 +1,639 @@ +/** + * @file arch/arm/m2351/src/numaker_pfm_m2351/secure/main.c + * @brief Provides functionality to start secure world, initialize secure + * and normal worlds, pass to execution to normal world. + * + * @copyright Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved. + * @author Taras Drozdovskyi t.drozdovsky@samsung.com + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* Included Files. */ +#include +#include +#include "M2351.h" +#include "partition_M2351.h" +#include "config.h" +#include "version.h" +#include "tee_api_types.h" + +/* Pre-processor Definitions. */ +#define NORMAL "\e[0m" +#define BLACK "\e[0;30m1" +#define RED "\e[0;31m" +#define GREEN "\e[0;32m" +#define YELLOW "\e[0;33m" +#define BLUE "\e[0;34m" +#define MAGENTA "\e[0;35m" +#define CYAN "\e[0;36m" +#define GRAY "\e[0;37m" + +/** Instruction Code of "B ." */ +#define JUMP_HERE 0xe7fee7ff + +/* Private Types. */ +/* Any types, enums, structures or unions used by the file are defined here. */ +/* typedef for NonSecure callback functions */ +typedef __NONSECURE_CALL int32_t (*NonSecure_funcptr)(uint32_t); +typedef int32_t (*Secure_funcptr)(uint32_t); + +/* Private Function Prototypes. */ +/* Prototypes of all static functions in the file are provided here. */ + +static void SYS_Init(void); +static void DEBUG_PORT_Init(void); +static void Boot_Init(uint32_t u32BootBase); + +/* Private Data. */ +/* All static data definitions appear here. */ +NonSecure_funcptr pfNonSecure_LED_On = (NonSecure_funcptr) NULL; +NonSecure_funcptr pfNonSecure_LED_Off = (NonSecure_funcptr) NULL; + +/* Public Data. */ +/* All data definitions with global scope appear here. */ + + +/* Public Function Prototypes */ +#ifdef CONFIG_APPS_HW_SECURITY_EXCEPTION_EXAMPLE +void menu_security_exception_example(void); +#endif + +extern TEE_Result tee_cryp_init(void); + +/* Private Functions. */ + +/** + * @brief SYS_Init - Init System Clock. + * + * @param None + * + * @returns None + */ +static void SYS_Init(void) +{ + /* Enable PLL */ + CLK->PLLCTL = CLK_PLLCTL_128MHz_HIRC; + + /* Waiting for PLL stable */ + while ((CLK->STATUS & CLK_STATUS_PLLSTB_Msk) == 0) + ; + + /* Set HCLK divider to 2 */ + CLK->CLKDIV0 = (CLK->CLKDIV0 & (~CLK_CLKDIV0_HCLKDIV_Msk)) | 1; + + /* Switch HCLK clock source to PLL */ + CLK->CLKSEL0 = (CLK->CLKSEL0 & (~CLK_CLKSEL0_HCLKSEL_Msk)) + | CLK_CLKSEL0_HCLKSEL_PLL; + + /* Enable UART clock */ + CLK->APBCLK0 |= CLK_APBCLK0_UART0CKEN_Msk; + + /* Select UART clock source */ + CLK->CLKSEL1 = (CLK->CLKSEL1 & (~CLK_CLKSEL1_UART0SEL_Msk)) + | CLK_CLKSEL1_UART0SEL_HIRC; + + /* Update System Core Clock */ + /* User can use SystemCoreClockUpdate() to calculate PllClock, SystemCoreClock and CycylesPerUs automatically. */ + //SystemCoreClockUpdate(); + PllClock = 128000000; // PLL + SystemCoreClock = 128000000 / 2; // HCLK + CyclesPerUs = 64000000 / 1000000; // For SYS_SysTickDelay() + + /* Init I/O Multi-function */ + /* Set multi-function pins for UART0 RXD and TXD */ + SYS->GPB_MFPH = (SYS->GPB_MFPH & (~(UART0_RXD_PB12_Msk | UART0_TXD_PB13_Msk))) + | UART0_RXD_PB12 | UART0_TXD_PB13; + + /* Init for nonsecure code */ + CLK->APBCLK0 |= CLK_APBCLK0_UART1CKEN_Msk; + CLK->CLKSEL1 = (CLK->CLKSEL1 & (~CLK_CLKSEL1_UART1SEL_Msk)) + | CLK_CLKSEL1_UART1SEL_HIRC; + SYS->GPA_MFPL = (SYS->GPA_MFPL & (~UART1_RXD_PA2_Msk)) | UART1_RXD_PA2; + SYS->GPA_MFPL = (SYS->GPA_MFPL & (~UART1_TXD_PA3_Msk)) | UART1_TXD_PA3; + +} + +/** + * @brief Boot_Init - Boot_Init function is used to jump to next boot + * code. + * + * @param None + * + * @returns None + */ +static void Boot_Init(uint32_t u32BootBase) +{ + NonSecure_funcptr fp; + + /* SCB_NS.VTOR points to the Non-secure vector table base address. */ + SCB_NS->VTOR = u32BootBase; + + /* 1st Entry in the vector table is the Non-secure Main Stack Pointer. */ + __TZ_set_MSP_NS(*((uint32_t *) SCB_NS->VTOR)); /* Set up MSP in Non-secure code */ + + /* 2nd entry contains the address of the Reset_Handler (CMSIS-CORE) function */ + fp = ((NonSecure_funcptr) (*(((uint32_t *) SCB_NS->VTOR) + 1))); + + /* Clear the LSB of the function address to indicate the function-call + will cause a state switch from Secure to Non-secure */ + fp = cmse_nsfptr_create(fp); + + /* Check if the Reset_Handler address is in Non-secure space */ + if (cmse_is_nsfptr(fp) && (((uint32_t) fp & 0xf0000000) == 0x10000000)) { + printf("Jump to execute Non-secure FreeRTOS (BL33) ...\n"); + fp(0); /* Non-secure function call */ + } else { + /* Something went wrong */ + printf("No code in non-secure region!\n"); + printf("CPU will halted at non-secure state\n"); + + /* Set nonsecure MSP in nonsecure region */ + __TZ_set_MSP_NS(NON_SECURE_SRAM_BASE + 512); + + /* Try to halted in non-secure state (SRAM) */ + M32(NON_SECURE_SRAM_BASE) = JUMP_HERE; + fp = (NonSecure_funcptr) (NON_SECURE_SRAM_BASE + 1); + fp(0); + + while (1) + ; + } +} + +/** + * @brief DEBUG_PORT_Init - performs initialization of UART. + * + * @param None + * + * @returns None + */ +static void DEBUG_PORT_Init(void) +{ + DEBUG_PORT->BAUD = UART_BAUD_MODE2 | UART_BAUD_MODE2_DIVIDER(__HIRC, 115200); + DEBUG_PORT->LINE = UART_WORD_LEN_8 | UART_PARITY_NONE | UART_STOP_BIT_1; +} + +/* Public Functions. */ + +/* Secure functions exported to NonSecure application. Must place in Non-secure + * Callable */ +/** + * @brief Secure_func - . + * + * @param None + * + * @returns None + */ +__NONSECURE_ENTRY +int32_t Secure_func(void) +{ +// printf("Secure NSC func\n"); +#ifdef CONFIG_APPS_HW_SECURITY_EXCEPTION_EXAMPLE + menu_security_exception_example(); +#endif + + return 1; +} + +/** + * @brief Secure_LED_On - . + * + * @param None + * + * @returns None + */ +__NONSECURE_ENTRY +int32_t Secure_LED_On(uint32_t num) +{ + (void) num; + +// printf("Secure LED On call by Non-secure\n"); + PA10 = 0; + PB0 = 0; + + return 0; +} + +/** + * @brief Secure_LED_Off - . + * + * @param None + * + * @returns None + */ +__NONSECURE_ENTRY +int32_t Secure_LED_Off(uint32_t num) +{ + (void) num; + +// printf("Secure LED Off call by Non-secure\n"); + PA10 = 1; + PB0 = 1; + + return 1; +} + +/* NonSecure callable function for NonSecure callback. */ +/** + * @brief Secure_LED_On_callback - . + * + * @param None + * + * @returns None + */ +__NONSECURE_ENTRY +int32_t Secure_LED_On_callback(NonSecure_funcptr *callback) +{ + pfNonSecure_LED_On = (NonSecure_funcptr) cmse_nsfptr_create(callback); + return 0; +} + +/** + * @brief Secure_LED_Off_callback - . + * + * @param None + * + * @returns None + */ +__NONSECURE_ENTRY +int32_t Secure_LED_Off_callback(NonSecure_funcptr *callback) +{ + pfNonSecure_LED_Off = (NonSecure_funcptr) cmse_nsfptr_create(callback); + return 0; +} + +/* Secure LED control function */ +/** + * @brief LED_On - . + * + * @param None + * + * @returns None + */ +int32_t LED_On(void) +{ + printf("Secure LED On\n"); + PA11 = 0; + PB1 = 0; + return 1; +} + +/** + * @brief LED_Off - . + * + * @param None + * + * @returns None + */ +int32_t LED_Off(void) +{ + printf("Secure LED Off\n"); + PA11 = 1; + PB1 = 1; + return 1; +} + +#ifdef CONFIG_APPS_HW_SECURITY_EXCEPTION_EXAMPLE +void hw_security_exception_init(void) +{ + /* Init PC for Nonsecure LED control */ + GPIO_SetMode(PC_NS, BIT1, GPIO_MODE_OUTPUT); + + /* Set Hard Fault to secure */ +// SCB->AIRCR = (0x05FA << 16) | ((SCB->AIRCR&0xfffful) & (~(1 << SCB_AIRCR_BFHFNMINS_Pos))); +// SCB->AIRCR = (0x05FA << 16) | ((SCB->AIRCR & 0xfffful) | (1 << SCB_AIRCR_BFHFNMINS_Pos)); + + if (SCB->AIRCR & SCB_AIRCR_BFHFNMINS_Msk) { + printf("Non-secure Hard Fault handled by Non-secure code.\n"); + } else { + printf("Non-secure Hard Fault handled by secure code.\n"); + } +// printf("SCB->AIRCR = 0x%08x\n", SCB->AIRCR); + + NVIC_EnableIRQ(SCU_IRQn); +} +void menu_security_exception_example(void) +{ + char ch; + int temp = 1; + +// printf("SCU->SVIOIEN = 0x%08x\n",SCU->SVIOIEN); +// printf("SCU->SRAMNSSET = 0x%08x\n",SCU->SRAMNSSET); + + printf("+---------------------------------------------------------------------+\n"); + printf("| Type | Range addresses | Size | Remarks |\n"); + printf("+---------------------------------------------------------------------+\n"); + printf("| FLASH | 0x00000000 - 0x0003F000 | 0x3F000 | 252k |\n"); + printf("| RAM | 0x20000000 - 0x20007FFF | 0x8000 | 32k |\n"); + printf("| NCS | 0x0003F000 - 0x0003FFFF | 0x1000 | 4k |\n"); + printf("| GPIO | PC | | Port C is non-secure |\n"); + printf("| GPIO | PA,PB | | Port A,B are secure |\n"); + printf("+---------------------------------------------------------------------+\n\n"); + + printf("+---------------------------------------------------------------------+\n"); + printf("| Key | Action | Expected response |\n"); + printf("+---------------------------------------------------------------------+\n"); + printf("| [1] | Read SRAM non-secure address 0x30017000 | Access successful |\n"); + printf("| | Read SRAM non-secure address 0x20017000 | RAZWI |\n"); + printf("| [2] | Read SRAM secure address 0x%08X | Access successful |\n",(unsigned int)&temp); + printf("| | Read SRAM secure address 0x%08X | RAZWI |\n",(0x10000000 + (unsigned int)&temp)); + printf("| [3] | Read FLASH secure address 0x00000000 | Access successful |\n"); + printf("| | Read FLASH secure address 0x10000000 | RAZWI |\n"); + printf("| [4] | Read FLASH non-secure address 0x%08X | Access successful |\n",CONFIG_START_ADDRESS_BL33); + printf("| | Read FLASH non-secure address 0x%08X | RAZWI |\n",(CONFIG_START_ADDRESS_BL33 - 0x10000000)); + printf("| [5] | Read GPIO non-secure port PC1_NS | Access successful |\n"); + printf("| | Write 0 GPIO non-secure port by PC1_NS | Access successful |\n"); + printf("| | Write 1 GPIO non-secure port by PC1 | RAZWI |\n"); + printf("| [6] | Read GPIO secure port PA10 | Access successful |\n"); + printf("| | Write 0 GPIO secure port by PA10 | Access successful |\n"); + printf("| | Write 1 GPIO secure port by PA10_NS | RAZWI, sec. violat.|\n"); + printf("| [7] | Write 0 FLASH to address 0x0 (directly) | Hard fault |\n"); + printf("| [8] | Read 0x20018000 address (nonexistent) | Hard fault |\n"); + printf("|[any]| Go non-secure code | Exec. non-sec code |\n"); + printf("+---------------------------------------------------------------------+\n"); + + printf("\n[%c] ", ch = getchar()); + + switch (ch) { + case '1': + printf("Read SRAM non-secure address 0x30017000 = %08x\n", M32(0x30017000)); + printf(" Read SRAM non-secure address 0x20017000 = %08x\n", M32(0x20017000)); + break; + case '2': + printf("Read SRAM secure address 0x%08X = %08x\n", (unsigned int)&temp, M32(&temp)); + printf(" Read SRAM secure address 0x%08X = %08x\n",(0x10000000 + (unsigned int)&temp), M32(0x10000000 + (unsigned int)&temp)); + break; + case '3': + printf("Read FLASH secure address 0x00000000 = %08x\n", M32(0x00000000)); + printf(" Read FLASH secure address 0x10000000 = %08x\n", M32(0x10000000)); + break; + case '4': + printf("Read FLASH non-secure address 0x%08X = %08x\n", CONFIG_START_ADDRESS_BL33, M32(CONFIG_START_ADDRESS_BL33)); + printf(" Read FLASH non-secure address 0x%08X = %08x\n", (CONFIG_START_ADDRESS_BL33 - 0x10000000), M32(CONFIG_START_ADDRESS_BL33 - 0x10000000)); + break; + case '5': + printf("Read GPIO non-secure port PC1_NS = %d\n", PC1_NS); + printf(" Write 0 GPIO non-secure port by PC1_NS."); + PC1_NS = 0; + printf(" Result: PC1_NS = %d\n", PC1_NS); + printf(" Write 1 GPIO non-secure port by PC1."); + PC1 = 1; + printf(" Result: PC1_NS = %d\n", PC1_NS); + break; + case '6': + printf(" Read GPIO secure port PA10 = %d\n", PA10); + printf(" Write 0 GPIO secure port by PA10."); + PA10 = 0; + printf(" Result: PA10 = %d\n", PA10); + printf(" Write 1 GPIO secure port by PA10_NS. Result: GPIO violation interrupt\n"); + PA10_NS = 1; + break; + case '7': + M32(0) = 0; + break; + case '8': + M32(0x20018000); + break; + default: + break; + } +} + +#ifdef CONFIG_APPS_SPY +const char mal_detected[] = "" +"\n MMMM MMMM MMMM" +"\n MMMM MMMM MMMM" +"\n MMMM MMMM MMMM" +"\n -ohMMMMMMMMMMMMMMMMMMMMMMMMMMMMho-" +"\n /dMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMd/" +"\n `hMMMM/` `/MMMMy`" +"\n hMMN/ "RED"MALWARE DETECTED!!!"GREEN" /MMMh" +"\n MMM+ oMMM" +"\n MMMMMMMM .odNMMMMMMMMMNms- MMMMMMMM" +"\n MMMMMMMM `omMMMMMMMMMMMMMMMNs` MMMMMMMM" +"\n MMM `yMMMMMMMMMMMMMMMMMMMh. MMM" +"\n MMM +MMMMNddmMMMMMmddNMMMMs MMM" +"\n MMM hMMMd-``./MMMo.``-yMMMm` MMM" +"\n MMMMMMMM hMMMs `MMM- +MMMm` MMMMMMMM" +"\n MMMMMMMM +MMMNy==+MMMMMo==sNMMMy MMMMMMMM" +"\n MMM `hMMMMMMMMh MMMMMMMMd. MMM" +"\n MMM `sNMMMMMMb dMMMMMNy. MMM" +"\n MMM -yMMMMMMMMMMMMMd:` MMM" +"\n MMMMMMMM :MMM MMM MMM+ MMMMMMMM" +"\n MMMMMMMM `:MM MMM MM:` MMMMMMMM" +"\n MMM+ MMM" +"\n hMMM/ "RED"MALWARE DETECTED!!!"GREEN" /MMMh" +"\n `hMMMh/` `/hMMMy`" +"\n /dMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMd:" +"\n -ohMMMMMMMMMMMMMMMMMMMMMMMMMMMMho-" +"\n MMMM MMMM MMMM" +"\n MMMM MMMM MMMM" +"\n MMMM MMMM MMMM\n" +"\n\t\t "RED"PLEASE RESTART BOARD!!!\n"; +#endif + +void HardFault_Handler(void) +{ + printf(RED "Secure Hard Fault Handler: invalid memory access or malware activity detected\n" NORMAL); +#ifdef CONFIG_APPS_SPY + printf("%s\n", mal_detected); +#endif + while(1); +} + +void SCU_IRQHandler(void) +{ + printf(RED "SCU_IRQHandler detected: "); +#ifdef CONFIG_APB0IEN_SECURE_VIOLATION_INTERRUPT + if((SCU->SVINTSTS & SCU_SVIOIEN_APB0IEN_Msk) == SCU_SVIOIEN_APB0IEN_Msk) { + SCU->SVINTSTS |= SCU_SVIOIEN_APB0IEN_Msk; + printf("APB0 "); + } +#endif +#ifdef CONFIG_APB1IEN_SECURE_VIOLATION_INTERRUPT + if((SCU->SVINTSTS & SCU_SVIOIEN_APB1IEN_Msk) == SCU_SVIOIEN_APB1IEN_Msk) { + SCU->SVINTSTS |= SCU_SVIOIEN_APB1IEN_Msk; + printf("APB1 "); + } +#endif +#ifdef CONFIG_GPIOIEN_SECURE_VIOLATION_INTERRUPT + if ((SCU->SVINTSTS & SCU_SVIOIEN_GPIOIEN_Msk) == SCU_SVIOIEN_GPIOIEN_Msk) { + SCU->SVINTSTS |= SCU_SVIOIEN_GPIOIEN_Msk; + printf("GPIO "); + } +#endif +#ifdef CONFIG_EBIIEN_SECURE_VIOLATION_INTERRUPT + if((SCU->SVINTSTS & SCU_SVIOIEN_EBIIEN_Msk) == SCU_SVIOIEN_EBIIEN_Msk) { + SCU->SVINTSTS |= SCU_SVIOIEN_EBIIEN_Msk; + printf("EBI "); + } +#endif +#ifdef CONFIG_USBHIEN_SECURE_VIOLATION_INTERRUPT + if((SCU->SVINTSTS & SCU_SVIOIEN_USBHIEN_Msk) == SCU_SVIOIEN_USBHIEN_Msk) { + SCU->SVINTSTS |= SCU_SVIOIEN_USBHIEN_Msk; + printf("USBH "); + } +#endif +#ifdef CONFIG_CRCIEN_SECURE_VIOLATION_INTERRUPT + if((SCU->SVINTSTS & SCU_SVIOIEN_CRCIEN_Msk) == SCU_SVIOIEN_CRCIEN_Msk) { + SCU->SVINTSTS |= SCU_SVIOIEN_CRCIEN_Msk; + printf("CRC "); + } +#endif +#ifdef CONFIG_SDH0IEN_SECURE_VIOLATION_INTERRUPT + if((SCU->SVINTSTS & SCU_SVIOIEN_SDH0IEN_Msk) == SCU_SVIOIEN_SDH0IEN_Msk) { + SCU->SVINTSTS |= SCU_SVIOIEN_SDH0IEN_Msk; + printf("SDH0 "); + } +#endif +#ifdef CONFIG_PDMA0IEN_SECURE_VIOLATION_INTERRUPT + if((SCU->SVINTSTS & SCU_SVIOIEN_PDMA0IEN_Msk) == SCU_SVIOIEN_PDMA0IEN_Msk) { + SCU->SVINTSTS |= SCU_SVIOIEN_PDMA0IEN_Msk; + printf("PDMA0 "); + } +#endif +#ifdef CONFIG_PDMA1IEN_SECURE_VIOLATION_INTERRUPT + if((SCU->SVINTSTS & SCU_SVIOIEN_PDMA1IEN_Msk) == SCU_SVIOIEN_PDMA1IEN_Msk) { + SCU->SVINTSTS |= SCU_SVIOIEN_PDMA1IEN_Msk; + printf("PDMA1 "); + } +#endif +#ifdef CONFIG_SRAM0IEN_SECURE_VIOLATION_INTERRUPT + if((SCU->SVINTSTS & SCU_SVIOIEN_SRAM0IEN_Msk) == SCU_SVIOIEN_SRAM0IEN_Msk) { + SCU->SVINTSTS |= SCU_SVIOIEN_SRAM0IEN_Msk; + printf("SRAM0 "); + } +#endif +#ifdef CONFIG_SRAM1IEN_SECURE_VIOLATION_INTERRUPT + if((SCU->SVINTSTS & SCU_SVIOIEN_SRAM1IEN_Msk) == SCU_SVIOIEN_SRAM1IEN_Msk) { + SCU->SVINTSTS |= SCU_SVIOIEN_SRAM1IEN_Msk; + printf("SRAM1 "); + } +#endif +#ifdef CONFIG_FMCIEN_SECURE_VIOLATION_INTERRUPT + if((SCU->SVINTSTS & SCU_SVIOIEN_FMCIEN_Msk) == SCU_SVIOIEN_FMCIEN_Msk) { + SCU->SVINTSTS |= SCU_SVIOIEN_FMCIEN_Msk; + printf("FMC "); + } +#endif +#ifdef CONFIG_FLASHIEN_SECURE_VIOLATION_INTERRUPT + if((SCU->SVINTSTS & SCU_SVIOIEN_FLASHIEN_Msk) == SCU_SVIOIEN_FLASHIEN_Msk) { + SCU->SVINTSTS |= SCU_SVIOIEN_FLASHIEN_Msk; + printf("FLASH "); + } +#endif +#ifdef CONFIG_SCUIEN_SECURE_VIOLATION_INTERRUPT + if((SCU->SVINTSTS & SCU_SVIOIEN_SCUIEN_Msk) == SCU_SVIOIEN_SCUIEN_Msk) { + SCU->SVINTSTS |= SCU_SVIOIEN_SCUIEN_Msk; + printf("SCU "); + } +#endif +#ifdef CONFIG_SYSIEN_SECURE_VIOLATION_INTERRUPT + if((SCU->SVINTSTS & SCU_SVIOIEN_SYSIEN_Msk) == SCU_SVIOIEN_SYSIEN_Msk) { + SCU->SVINTSTS |= SCU_SVIOIEN_SYSIEN_Msk; + printf("SYS "); + } +#endif +#ifdef CONFIG_CRPTIEN_SECURE_VIOLATION_INTERRUPT + if((SCU->SVINTSTS & SCU_SVIOIEN_CRPTIEN_Msk) == SCU_SVIOIEN_CRPTIEN_Msk) { + SCU->SVINTSTS |= SCU_SVIOIEN_CRPTIEN_Msk; + printf("CRPT "); + } +#endif + printf("violation interrupt event\n"NORMAL); +} +#endif +/** + * @brief SysTick_Handler - SysTick IRQ Handler. + * + * @param None + * + * @returns None + */ +//void SysTick_Handler(void) +//{ +// static uint32_t u32Ticks; +// +// switch (u32Ticks++) { +// case 0: +// LED_On(); +// break; +// case 200: +// LED_Off(); +// break; +// case 300: +// if (pfNonSecure_LED_On != NULL) { +// pfNonSecure_LED_On(1u); +// } +// break; +// case 500: +// printf("*"); +// if (pfNonSecure_LED_Off != NULL) { +// pfNonSecure_LED_Off(1u); +// } +// break; +// +// default: +// if (u32Ticks > 600) { +// u32Ticks = 0; +// } +// } +//} + +/** + * @brief main - entry point of mTower: secure world. + * + * @param None + * + * @returns None (function is not supposed to return) + */ +int main(void) +{ + SYS_UnlockReg(); + + SYS_Init(); + + /* UART is configured as debug port */ + DEBUG_PORT_Init(); + + printf(NORMAL "\n\n\t-=mTower v" VERSION "=- " __DATE__ " " __TIME__"\n\n"); + + printf("+---------------------------------------------+\n"); + printf("| Secure handler (BL32) is running ... |\n"); + printf("+---------------------------------------------+\n"); + + /* Init GPIO Port A for secure LED control */ + GPIO_SetMode(PA, BIT11 | BIT10, GPIO_MODE_OUTPUT); + + /* Init GPIO Port B for secure LED control */ + GPIO_SetMode(PB, BIT1 | BIT0, GPIO_MODE_OUTPUT); + + /* Generate Systick interrupt each 10 ms */ +// SysTick_Config(SystemCoreClock / 100); + +#ifdef CONFIG_APPS_HW_SECURITY_EXCEPTION_EXAMPLE + hw_security_exception_init(); +// menu_security_exception_example(); +#endif + + tee_cryp_init(); + + Boot_Init(CONFIG_START_ADDRESS_BL33); + + do { + __WFI(); + } while (1); + +} + diff --git a/arch/riscv32/fe310/src/sparkfun_redboard/secure/secure.ld b/arch/riscv32/fe310/src/sparkfun_redboard/secure/secure.ld new file mode 100644 index 00000000..63648fca --- /dev/null +++ b/arch/riscv32/fe310/src/sparkfun_redboard/secure/secure.ld @@ -0,0 +1,231 @@ +/* Linker script to configure memory regions. */ +MEMORY +{ + /* FLASH (rx) : ORIGIN = 0x00010000, LENGTH = 0x80000 */ /* 512k */ + RAM (rwx) : ORIGIN = 0x20000000, LENGTH = 0x18000 /* 160k */ + FLASH_NCS (rx) : ORIGIN = 0x0003F000, LENGTH = 0x00000400 +} + +/* Library configurations */ +GROUP(libgcc.a libc.a libm.a libnosys.a) + +/* Linker script to place sections and symbol values. Should be used together + * with other linker script that defines memory regions FLASH and RAM. + * It references following symbols, which must be defined in code: + * Reset_Handler : Entry of reset handler + * + * It defines following symbols, which code can use without definition: + * __exidx_start + * __exidx_end + * __copy_table_start__ + * __copy_table_end__ + * __zero_table_start__ + * __zero_table_end__ + * __etext + * __data_start__ + * __preinit_array_start + * __preinit_array_end + * __init_array_start + * __init_array_end + * __fini_array_start + * __fini_array_end + * __data_end__ + * __bss_start__ + * __bss_end__ + * __end__ + * end + * __HeapLimit + * __StackLimit + * __StackTop + * __stack + * __Vectors_End + * __Vectors_Size + */ +ENTRY(Reset_Handler) + +SECTIONS +{ + .text : + { + KEEP(*(.vectors)) + __Vectors_End = .; + __Vectors_Size = __Vectors_End - __Vectors; + __end__ = .; + + *(.text*) + + KEEP(*(.init)) + KEEP(*(.fini)) + + /* .ctors */ + *crtbegin.o(.ctors) + *crtbegin?.o(.ctors) + *(EXCLUDE_FILE(*crtend?.o *crtend.o) .ctors) + *(SORT(.ctors.*)) + *(.ctors) + + /* .dtors */ + *crtbegin.o(.dtors) + *crtbegin?.o(.dtors) + *(EXCLUDE_FILE(*crtend?.o *crtend.o) .dtors) + *(SORT(.dtors.*)) + *(.dtors) + + /* *(.rodata*) */ + + KEEP(*(.eh_frame*)) + } > FLASH + + .rodata : ALIGN(4) + { + __rodata_start = .; + *(.rodata .rodata.*) + /* + * 8 to avoid unwanted padding between __start_ta_head_section + * and the first structure in ta_head_section, in 64-bit + * builds + */ + . = ALIGN(4); + __start_ta_head_section = . ; + KEEP(*(ta_head_section)) + __stop_ta_head_section = . ; + . = ALIGN(4); + __start_user_ta_head_section = . ; + KEEP(*(user_ta_head_section)) + __stop_user_ta_head_section = . ; + . = ALIGN(4); + __rodata_end = .; + } > FLASH + + .ARM.extab : + { + *(.ARM.extab* .gnu.linkonce.armextab.*) + } > FLASH + + __exidx_start = .; + .ARM.exidx : + { + *(.ARM.exidx* .gnu.linkonce.armexidx.*) + } > FLASH + __exidx_end = .; + + /* To copy multiple ROM to RAM sections, + * uncomment .copy.table section and, + * define __STARTUP_COPY_MULTIPLE in startup_ARMCMx.S */ + /* + .copy.table : + { + . = ALIGN(4); + __copy_table_start__ = .; + LONG (__etext) + LONG (__data_start__) + LONG (__data_end__ - __data_start__) + LONG (__etext2) + LONG (__data2_start__) + LONG (__data2_end__ - __data2_start__) + __copy_table_end__ = .; + } > FLASH + */ + + /* To clear multiple BSS sections, + * uncomment .zero.table section and, + * define __STARTUP_CLEAR_BSS_MULTIPLE in startup_ARMCMx.S */ + /* + .zero.table : + { + . = ALIGN(4); + __zero_table_start__ = .; + LONG (__bss_start__) + LONG (__bss_end__ - __bss_start__) + LONG (__bss2_start__) + LONG (__bss2_end__ - __bss2_start__) + __zero_table_end__ = .; + } > FLASH + */ + + .gnu.sgstubs : ALIGN(2048) + { + __start_NSC = .; + . = ALIGN(4); + _ssgstubs = .; + } > FLASH + . = ALIGN(2048); + __end_NSC = .; + __etext = .; + + .data : AT (__etext) + { + __data_start__ = .; + *(vtable) + *(.data*) + + . = ALIGN(4); + /* preinit data */ + PROVIDE_HIDDEN (__preinit_array_start = .); + KEEP(*(.preinit_array)) + PROVIDE_HIDDEN (__preinit_array_end = .); + + . = ALIGN(4); + /* init data */ + PROVIDE_HIDDEN (__init_array_start = .); + KEEP(*(SORT(.init_array.*))) + KEEP(*(.init_array)) + PROVIDE_HIDDEN (__init_array_end = .); + + + . = ALIGN(4); + /* finit data */ + PROVIDE_HIDDEN (__fini_array_start = .); + KEEP(*(SORT(.fini_array.*))) + KEEP(*(.fini_array)) + PROVIDE_HIDDEN (__fini_array_end = .); + + KEEP(*(.jcr*)) + . = ALIGN(4); + /* All data end */ + __data_end__ = .; + + } > RAM + + .bss : + { + . = ALIGN(4); + __bss_start__ = .; + *(.bss*) + *(COMMON) + . = ALIGN(4); + __bss_end__ = .; + } > RAM + + .heap (COPY): + { + __HeapBase = .; + __end__ = .; + end = __end__; + KEEP(*(.heap*)) + __HeapLimit = .; + } > RAM + + /* .stack_dummy section doesn't contains any symbols. It is only + * used for linker to calculate size of stack sections, and assign + * values to stack symbols later */ + .stack_dummy (COPY): + { + KEEP(*(.stack*)) + } > RAM + + /* Set stack top to end of RAM, and stack limit move down by + * size of stack_dummy section */ + /* + __StackTop = ORIGIN(RAM) + LENGTH(RAM); + __StackLimit = __StackTop - SIZEOF(.stack_dummy); + */ + + __StackTop = __HeapLimit + SIZEOF(.stack_dummy); + __StackLimit = __HeapLimit; + + PROVIDE(__stack = __StackTop); + + /* Check if data + heap + stack exceeds RAM limit */ + ASSERT(__StackLimit >= __HeapLimit, "region RAM overflowed with stack") +} diff --git a/configs/pine64_ox64/Make.defs b/configs/pine64_ox64/Make.defs new file mode 100644 index 00000000..b1ae800f --- /dev/null +++ b/configs/pine64_ox64/Make.defs @@ -0,0 +1,131 @@ +############################################################################ +# Make.defs +# +# Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved. +# Author: Taras Drozdovskyi t.drozdovsky@samsung.com +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +############################################################################ + +-include ${TOPDIR}/.config + +GCC_OFFICIAL_VER:=$(CONFIG_GCC_VERSION) +GCC_SITE:=$(CONFIG_GCC_SITE) +GCC_SOURCE:=$(GCC_OFFICIAL_VER).zip + +TOOLCHAINPATH=$(TOPDIR)/toolchain/$(subst ",,$(CONFIG_GCC_FOLDER)) + +CROSSDEV =$(TOOLCHAINPATH)/bin/riscv64-unknown-elf- + +CC = $(CROSSDEV)gcc +LD = $(CROSSDEV)ld +AR = $(ARCROSSDEV)ar rcs +NM = $(ARCROSSDEV)nm +OBJCOPY = $(CROSSDEV)objcopy +OBJDUMP = $(CROSSDEV)objdump +SIZE = $(CROSSDEV)size + +ARCHWARNINGS = -Wall -Wextra -Wstrict-prototypes -Wshadow -Wundef -Wunused-parameter -Wchar-subscripts -Wformat -Wuninitialized -Winit-self -Wignored-qualifiers +ARCHWARNINGS += -Wno-error=unused-variable -Wno-error=format= -Wno-error=unused-function -Wno-error=implicit-function-declaration -Wno-error=deprecated-declarations -Wno-format -Wno-unused-parameter +ARCHWARNINGS += -Wno-strict-prototypes + +ARCHOPTIMIZATION = $(subst ",,$(CONFIG_OPTIMIZATION_LEVEL)) + +ifeq ($(CONFIG_DEBUG_SYMBOLS),y) + ARCHOPTIMIZATION += -g +else + ARCHOPTIMIZATION += -ffunction-sections -fdata-sections +endif + +# Produce debugging information for use by GDB. +ifeq ($(CONFIG_DEBUG_INF_GDB),y) + ARCHOPTIMIZATION += -ggdb +endif + +ARCHCPUFLAGS = -march=rv32imafcpzpsfoperand_xtheade -mabi=ilp32f -mtune=e907 + +#-mcmodel=medlow --specs=nano.specs + +#ARCHDEFINES += -DMTIME_RATE_HZ_DEF=32768 -DportUSING_MPU_WRAPPERS=1 +#ARCHDEFINES += -DCHIP=bl808 -DCPU_ID=m0 -DBOARD=bl808dk -DCONFIG_COMX=COM5 -DCMAKE_EXPORT_COMPILE_COMMANDS=OFF -DCONFIG_TLSF=y +#ARCHDEFINES += -DBL808 -DCPU_M0 -DARCH_RISCV -DCONFIG_IRQ_NUM=80 -DBOARD=bl808dk -DBOARD_DIR= -DCONFIG_DEBUG=1 -DCONFIG_ROMAPI=1 -DCONFIG_USB_HS=y +#ARCHDEFINES += -DNEWLIB=0 -DNEWLIB_STANDARD=0 + +ARCHDEFINES += -DARCH_RISCV -DBFLB_TIMESTAMP_TIMEZONE=8 -DBFLB_USE_HAL_DRIVER -DBL808 -DCPU_M0 +ARCHDEFINES += -DCONFIG_IRQ_NUM=80 -DCONFIG_LIBC_FLOAT=1 -DCONFIG_LIBC_FLOAT_EX=1 -DCONFIG_LIBC_LONG_LONG=1 -DCONFIG_LOG_LEVEL=3 -DCONFIG_TLSF +ARCHDEFINES += -DportasmHANDLE_INTERRUPT=interrupt_entry -Ddefault_interrupt_handler=freertos_risc_v_trap_handler -DCONFIG_FREERTOS +ARCHDEFINES += -DportUSING_MPU_WRAPPERS=1 + +AFLAGS = $(CFLAGS) -D__ASSEMBLY__ + +LDFLAGS = $(ARCHOPTIMIZATION) + +ARCHINCLUDES = -I$(TOPDIR)/include +#ARCHINCLUDES += -I$(TOPDIR)/arch/riscv32/bl808/src/bsp/board/bl808dk/. +ARCHINCLUDES += -I$(TOPDIR)/arch/riscv32/bl808/src/components/libc/newlib/. +ARCHINCLUDES += -I$(TOPDIR)/arch/riscv32/bl808/src/components/libc/. +ARCHINCLUDES += -I$(TOPDIR)/arch/riscv32/bl808/src/components/mm/. +ARCHINCLUDES += -I$(TOPDIR)/arch/riscv32/bl808/src/components/mm/tlsf/. +ARCHINCLUDES += -I$(TOPDIR)/arch/riscv32/bl808/src/components/utils/log +ARCHINCLUDES += -I$(TOPDIR)/arch/riscv32/bl808/src/components/utils/ring_buffer +ARCHINCLUDES += -I$(TOPDIR)/arch/riscv32/bl808/src/components/utils/bflb_block_pool +ARCHINCLUDES += -I$(TOPDIR)/arch/riscv32/bl808/src/components/utils/bflb_timestamp +ARCHINCLUDES += -I$(TOPDIR)/arch/riscv32/bl808/src/components/utils/getopt +ARCHINCLUDES += -I$(TOPDIR)/arch/riscv32/bl808/src/drivers/lhal/include +ARCHINCLUDES += -I$(TOPDIR)/arch/riscv32/bl808/src/drivers/lhal/include/arch +ARCHINCLUDES += -I$(TOPDIR)/arch/riscv32/bl808/src/drivers/lhal/include/arch/risc-v/t-head +ARCHINCLUDES += -I$(TOPDIR)/arch/riscv32/bl808/src/drivers/lhal/include/arch/risc-v/t-head/Core/Include +ARCHINCLUDES += -I$(TOPDIR)/arch/riscv32/bl808/src/drivers/lhal/config/bl808 +ARCHINCLUDES += -I$(TOPDIR)/arch/riscv32/bl808/src/drivers/lhal/src/flash +ARCHINCLUDES += -I$(TOPDIR)/arch/riscv32/bl808/src/drivers/soc/bl808/std/include +ARCHINCLUDES += -I$(TOPDIR)/arch/riscv32/bl808/src/drivers/soc/bl808/std/include/hardware + +ARCHINCLUDES += -I$(TOPDIR)/arch/riscv32/bl808/src/pine64_ox64/nonsecure$ + +#ARCHINCLUDES += -I$(TOPDIR)/FreeRTOS-metal/FreeRTOS-Kernel/include +#ARCHINCLUDES += -I$(TOPDIR)/FreeRTOS-metal/FreeRTOS-Kernel/portable/GCC/RISC-V +#ARCHINCLUDES += -I$(TOPDIR)/FreeRTOS-metal/FreeRTOS-Kernel/portable/GCC/RISC-V/chip_specific_extensions/RV32I_CLINT_no_extensions + +ARCHINCLUDES += -I$(TOPDIR)/arch/riscv32/bl808/src/components/os/freertos/include +ARCHINCLUDES += -I$(TOPDIR)/arch/riscv32/bl808/src/components/os/freertos/portable/GCC/RISC-V/common +ARCHINCLUDES += -I$(TOPDIR)/arch/riscv32/bl808/src/components/os/freertos/portable/GCC/RISC-V/common/chip_specific_extensions/RV32I_CLINT_no_extensions + +CFLAGS = -fno-jump-tables -fno-common -fms-extensions -ffunction-sections -fdata-sections -fmessage-length=0 -fstrict-volatile-bitfields -fshort-enums -fno-builtin + +CFLAGS += -MMD -std=gnu99 -g3 -O2 + +CFLAGS += $(ARCHWARNINGS) $(ARCHOPTIMIZATION) $(ARCHCPUFLAGS) $(ARCHINCLUDES) $(ARCHDEFINES) + +ASMEXT = .S +OBJEXT = .o +LIBEXT = .a +EXEEXT = .elf +HEXEXT = .hex + +ECHO = `which echo` + +HOSTCC = gcc +HOSTINCLUDES = -I. +HOSTCFLAGS = -Wall -Wstrict-prototypes -Wshadow -Wundef -g -pipe +HOSTLDFLAGS = + +GREEN=\033[0;32m +NORMAL=\033[0m +BLACK=\033[0;30m1 +RED=\033[0;31m +YELLOW=\033[0;33m +BLUE=\033[0;34m +MAGENTA=\033[0;35m +CYAN=\033[0;36m +GRAY=\033[0;37m diff --git a/configs/pine64_ox64/defconfig b/configs/pine64_ox64/defconfig new file mode 100644 index 00000000..e578f307 --- /dev/null +++ b/configs/pine64_ox64/defconfig @@ -0,0 +1,167 @@ +# +# Automatically generated file; DO NOT EDIT. +# mTower/ Configuration +# + +# +# Build Setup +# +CONFIG_OPTIMIZATION_LEVEL_OS=y +# CONFIG_OPTIMIZATION_LEVEL_O1 is not set +# CONFIG_OPTIMIZATION_LEVEL_O2 is not set +# CONFIG_OPTIMIZATION_LEVEL_O3 is not set +CONFIG_OPTIMIZATION_LEVEL="-Os" + +# +# Debug Options +# +# CONFIG_DEBUG_ALERT is not set +# CONFIG_DEBUG_FEATURES is not set +# CONFIG_DEBUG_SYMBOLS is not set + +# +# GP TEE API Debug Options +# +# CONFIG_TRACE_LEVEL_MSG is not set +# CONFIG_TRACE_LEVEL_ERROR is not set +CONFIG_TRACE_LEVEL_INFO=y +# CONFIG_TRACE_LEVEL_DEBUG is not set +# CONFIG_TRACE_LEVEL_FLOW is not set +CONFIG_TRACE_LEVEL=2 +# CONFIG_DEBUGLEVEL_ERROR is not set +CONFIG_DEBUGLEVEL_INFO=y +# CONFIG_DEBUGLEVEL_DEBUG is not set +# CONFIG_DEBUGLEVEL_FLOW is not set +CONFIG_DEBUGLEVEL=2 + +# +# Trusted boot +# +# CONFIG_BOOTLOADER1 is not set +# CONFIG_BOOTLOADER2 is not set +CONFIG_BOOTLOADER32=y +CONFIG_BOOTLOADER33=y +CONFIG_START_ADDRESS_BL32=0x00000000 +CONFIG_START_ADDRESS_BL33=0x10040000 + +# +# System Type +# +# CONFIG_ARCH_CORTEX_M23 is not set +# CONFIG_ARCH_CORTEX_M33 is not set +CONFIG_ARCH_RISCV_32=y +CONFIG_ARCH="riscv32" +CONFIG_ARCH_FAMILY="bl808" +CONFIG_PLATFORM="pine64_ox64" +CONFIG_FMC_SECURE_ROM_SIZE=0x40000 +# CONFIG_APB0IEN_SECURE_VIOLATION_INTERRUPT is not set +# CONFIG_APB1IEN_SECURE_VIOLATION_INTERRUPT is not set +# CONFIG_GPIOIEN_SECURE_VIOLATION_INTERRUPT is not set +# CONFIG_EBIIEN_SECURE_VIOLATION_INTERRUPT is not set +# CONFIG_USBHIEN_SECURE_VIOLATION_INTERRUPT is not set +# CONFIG_CRCIEN_SECURE_VIOLATION_INTERRUPT is not set +# CONFIG_SDH0IEN_SECURE_VIOLATION_INTERRUPT is not set +# CONFIG_PDMA0IEN_SECURE_VIOLATION_INTERRUPT is not set +# CONFIG_PDMA1IEN_SECURE_VIOLATION_INTERRUPT is not set +# CONFIG_SRAM0IEN_SECURE_VIOLATION_INTERRUPT is not set +# CONFIG_SRAM1IEN_SECURE_VIOLATION_INTERRUPT is not set +# CONFIG_FMCIEN_SECURE_VIOLATION_INTERRUPT is not set +# CONFIG_FLASHIEN_SECURE_VIOLATION_INTERRUPT is not set +# CONFIG_SCUIEN_SECURE_VIOLATION_INTERRUPT is not set +# CONFIG_SYSIEN_SECURE_VIOLATION_INTERRUPT is not set +# CONFIG_CRPTIEN_SECURE_VIOLATION_INTERRUPT is not set +CONFIG_SAU_CONTROL=y +CONFIG_ENABLE_SAU=y +CONFIG_SAU_INIT_REGION0=y +CONFIG_SAU_INIT_START0=0x20000000 +CONFIG_SAU_INIT_END0=0x20008000 +# CONFIG_SAU_INIT_REGION1 is not set +# CONFIG_SAU_INIT_REGION2 is not set +CONFIG_SAU_INIT_REGION3=y +CONFIG_SAU_INIT_START3=0x0003F000 +CONFIG_SAU_INIT_END3=0x0003F7FF +CONFIG_SAU_INIT_REGION4=y +CONFIG_SAU_INIT_START4=0x10040000 +CONFIG_SAU_INIT_END4=0x1007FFFF +CONFIG_SAU_INIT_REGION5=y +CONFIG_SAU_INIT_START5=0x00807E00 +CONFIG_SAU_INIT_END5=0x00807FFF +CONFIG_SAU_INIT_REGION6=y +CONFIG_SAU_INIT_START6=0x30008000 +CONFIG_SAU_INIT_END6=0x30017FFF +CONFIG_SAU_INIT_REGION7=y +CONFIG_SAU_INIT_START7=0x50000000 +CONFIG_SAU_INIT_END7=0x5FFFFFFF +# CONFIG_ARCH_FAMILY_FE310 is not set +CONFIG_ARCH_FAMILY_BL808=y + +# +# BL808 Configuration Options +# +CONFIG_PLATFORM_PINE64_OX64=y + +# +# Secure Attribution Configuration +# + +# +# GPIO Secure Attribution Configuration +# + +# +# Peripheral Secure Attribution Configuration +# + +# +# Assign Interrupt to Secure or Non-secure Vector +# + +# +# Enable secure violation interrupts +# + +# +# Enable and Set Secure/Non-Secure region +# + +# +# Toolchain Configuration Options +# +# CONFIG_GCC_VERSION_6_1_2017Q1 is not set +# CONFIG_GCC_VERSION_6_1_2017Q2 is not set +# CONFIG_GCC_VERSION_8_2018Q4 is not set +# CONFIG_GCC_VERSION_10_2_0_2020_12 is not set +CONFIG_GCC_VERSION_T_HEAD_10_2_0=y +CONFIG_GCC_SITE="https://github.com/bouffalolab/toolchain_gcc_t-head_linux" +CONFIG_GCC_FOLDER="toolchain_gcc_t-head_linux-master" + +# +# GP TEE Configuration +# +CONFIG_MAX_CRYPTO_CHUNK_SIZE=0x400 + +# +# Application Configuration +# + +# +# Pseudo TAs: +# +# CONFIG_APPS_HELLO_WORLD is not set + +# +# User TAs: +# +# CONFIG_APPS_AES is not set +# CONFIG_APPS_HOTP is not set + +# +# Test suite: +# +# CONFIG_APPS_TEST is not set + +# +# H/W Security exception: +# +# CONFIG_APPS_HW_SECURITY_EXCEPTION_EXAMPLE is not set +# CONFIG_APPS_SPY is not set diff --git a/configs/sparkfun_redboard/Make.defs b/configs/sparkfun_redboard/Make.defs new file mode 100644 index 00000000..72fd1ef3 --- /dev/null +++ b/configs/sparkfun_redboard/Make.defs @@ -0,0 +1,105 @@ +############################################################################ +# Make.defs +# +# Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved. +# Author: Taras Drozdovskyi t.drozdovsky@samsung.com +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +############################################################################ + +-include ${TOPDIR}/.config + +GCC_OFFICIAL_VER:=$(CONFIG_GCC_VERSION) +GCC_SITE:=$(CONFIG_GCC_SITE) +GCC_SOURCE:=$(GCC_OFFICIAL_VER).tar.gz + +TOOLCHAINPATH=$(TOPDIR)/toolchain/$(subst ",,$(CONFIG_GCC_FOLDER)) + +CROSSDEV =$(TOOLCHAINPATH)/bin/riscv64-unknown-elf- + +CC = $(CROSSDEV)gcc +LD = $(CROSSDEV)ld +AR = $(ARCROSSDEV)ar rcs +NM = $(ARCROSSDEV)nm +OBJCOPY = $(CROSSDEV)objcopy +OBJDUMP = $(CROSSDEV)objdump +SIZE = $(CROSSDEV)size + +JC = javac + +ARCHWARNINGS = -Wall -Wextra -Wstrict-prototypes -Wshadow -Wundef -Wunused-parameter + +ARCHOPTIMIZATION = $(subst ",,$(CONFIG_OPTIMIZATION_LEVEL)) + +ifeq ($(CONFIG_DEBUG_SYMBOLS),y) + ARCHOPTIMIZATION += -g +else + ARCHOPTIMIZATION += -ffunction-sections -fdata-sections +endif + +# Produce debugging information for use by GDB. +ifeq ($(CONFIG_DEBUG_INF_GDB),y) + ARCHOPTIMIZATION += -ggdb +endif + +ARCHCPUFLAGS = -march=rv32imac -mabi=ilp32 -mcmodel=medlow --specs=nano.specs + +ARCHINCLUDES = -I$(TOPDIR)/include + +ARCHDEFINES = -DPACKAGE_NAME=\"freedom-metal\" -DPACKAGE_TARNAME=\"freedom-metal\" +ARCHDEFINES += -DPACKAGE_VERSION=\"v0.1.2\" -DPACKAGE_STRING=\"freedom-metal\ v0.1.2\" +ARCHDEFINES += -DPACKAGE_BUGREPORT=\"https://github.com/sifive/freedom-metal/issues\" +ARCHDEFINES += -DPACKAGE_URL=\"https://github.com/sifive/freedom-metal\" -DPACKAGE=\"freedom-metal\" -DVERSION=\"v0.1.2\" +ARCHDEFINES += -DMTIME_RATE_HZ_DEF=32768 -DportUSING_MPU_WRAPPERS=1 + +# -I. +# -I/home/virtual-box/projects/SiFive/freedom-e-sdk/freedom-metal +# -I/home/virtual-box/projects/SiFive/freedom-e-sdk/bsp/sifive-hifive1-revb/install/include +# --specs=nano.specs -DMTIME_RATE_HZ_DEF=32768 +# -O0 -g -MT gloss/sys_read.o -MD -MP -MF + +# $depbase.Tpo -c -o gloss/sys_read.o +# /home/virtual-box/projects/SiFive/freedom-e-sdk/freedom-metal/gloss/sys_read.c &&\ +#depbase=`echo gloss/sys_sbrk.o | sed 's|[^/]*$|.deps/&|;s|\.o$||'`;\ + +CFLAGS = -std=gnu99 -pipe -Wl,--gc-sections + +CFLAGS += $(ARCHWARNINGS) $(ARCHOPTIMIZATION) $(ARCHCPUFLAGS) $(ARCHINCLUDES) $(ARCHDEFINES) + +AFLAGS = $(CFLAGS) -D__ASSEMBLY__ + +LDFLAGS = $(ARCHOPTIMIZATION) + +ASMEXT = .S +OBJEXT = .o +LIBEXT = .a +EXEEXT = .elf +HEXEXT = .hex + +ECHO = `which echo` + +HOSTCC = gcc +HOSTINCLUDES = -I. +HOSTCFLAGS = -Wall -Wstrict-prototypes -Wshadow -Wundef -g -pipe +HOSTLDFLAGS = + +GREEN=\033[0;32m +NORMAL=\033[0m +BLACK=\033[0;30m1 +RED=\033[0;31m +YELLOW=\033[0;33m +BLUE=\033[0;34m +MAGENTA=\033[0;35m +CYAN=\033[0;36m +GRAY=\033[0;37m diff --git a/configs/sparkfun_redboard/defconfig b/configs/sparkfun_redboard/defconfig new file mode 100644 index 00000000..1b65f2b7 --- /dev/null +++ b/configs/sparkfun_redboard/defconfig @@ -0,0 +1,166 @@ +# +# Automatically generated file; DO NOT EDIT. +# rTower/ Configuration +# + +# +# Build Setup +# +CONFIG_OPTIMIZATION_LEVEL_OS=y +# CONFIG_OPTIMIZATION_LEVEL_O1 is not set +# CONFIG_OPTIMIZATION_LEVEL_O2 is not set +# CONFIG_OPTIMIZATION_LEVEL_O3 is not set +CONFIG_OPTIMIZATION_LEVEL="-Os" + +# +# Debug Options +# +# CONFIG_DEBUG_ALERT is not set +# CONFIG_DEBUG_FEATURES is not set +# CONFIG_DEBUG_SYMBOLS is not set + +# +# GP TEE API Debug Options +# +# CONFIG_TRACE_LEVEL_MSG is not set +# CONFIG_TRACE_LEVEL_ERROR is not set +CONFIG_TRACE_LEVEL_INFO=y +# CONFIG_TRACE_LEVEL_DEBUG is not set +# CONFIG_TRACE_LEVEL_FLOW is not set +CONFIG_TRACE_LEVEL=2 +# CONFIG_DEBUGLEVEL_ERROR is not set +CONFIG_DEBUGLEVEL_INFO=y +# CONFIG_DEBUGLEVEL_DEBUG is not set +# CONFIG_DEBUGLEVEL_FLOW is not set +CONFIG_DEBUGLEVEL=2 + +# +# Trusted boot +# +# CONFIG_BOOTLOADER1 is not set +# CONFIG_BOOTLOADER2 is not set +CONFIG_BOOTLOADER32=y +CONFIG_BOOTLOADER33=y +CONFIG_START_ADDRESS_BL32=0x00000000 +CONFIG_START_ADDRESS_BL33=0x10040000 + +# +# System Type +# +# CONFIG_ARCH_CORTEX_M23 is not set +# CONFIG_ARCH_CORTEX_M33 is not set +CONFIG_ARCH_RISCV_32=y +CONFIG_ARCH="riscv32" +CONFIG_ARCH_FAMILY="fe310" +CONFIG_PLATFORM="sparkfun_redboard" +CONFIG_FMC_SECURE_ROM_SIZE=0x40000 +# CONFIG_APB0IEN_SECURE_VIOLATION_INTERRUPT is not set +# CONFIG_APB1IEN_SECURE_VIOLATION_INTERRUPT is not set +# CONFIG_GPIOIEN_SECURE_VIOLATION_INTERRUPT is not set +# CONFIG_EBIIEN_SECURE_VIOLATION_INTERRUPT is not set +# CONFIG_USBHIEN_SECURE_VIOLATION_INTERRUPT is not set +# CONFIG_CRCIEN_SECURE_VIOLATION_INTERRUPT is not set +# CONFIG_SDH0IEN_SECURE_VIOLATION_INTERRUPT is not set +# CONFIG_PDMA0IEN_SECURE_VIOLATION_INTERRUPT is not set +# CONFIG_PDMA1IEN_SECURE_VIOLATION_INTERRUPT is not set +# CONFIG_SRAM0IEN_SECURE_VIOLATION_INTERRUPT is not set +# CONFIG_SRAM1IEN_SECURE_VIOLATION_INTERRUPT is not set +# CONFIG_FMCIEN_SECURE_VIOLATION_INTERRUPT is not set +# CONFIG_FLASHIEN_SECURE_VIOLATION_INTERRUPT is not set +# CONFIG_SCUIEN_SECURE_VIOLATION_INTERRUPT is not set +# CONFIG_SYSIEN_SECURE_VIOLATION_INTERRUPT is not set +# CONFIG_CRPTIEN_SECURE_VIOLATION_INTERRUPT is not set +CONFIG_SAU_CONTROL=y +CONFIG_ENABLE_SAU=y +CONFIG_SAU_INIT_REGION0=y +CONFIG_SAU_INIT_START0=0x20000000 +CONFIG_SAU_INIT_END0=0x20008000 +# CONFIG_SAU_INIT_REGION1 is not set +# CONFIG_SAU_INIT_REGION2 is not set +CONFIG_SAU_INIT_REGION3=y +CONFIG_SAU_INIT_START3=0x0003F000 +CONFIG_SAU_INIT_END3=0x0003F7FF +CONFIG_SAU_INIT_REGION4=y +CONFIG_SAU_INIT_START4=0x10040000 +CONFIG_SAU_INIT_END4=0x1007FFFF +CONFIG_SAU_INIT_REGION5=y +CONFIG_SAU_INIT_START5=0x00807E00 +CONFIG_SAU_INIT_END5=0x00807FFF +CONFIG_SAU_INIT_REGION6=y +CONFIG_SAU_INIT_START6=0x30008000 +CONFIG_SAU_INIT_END6=0x30017FFF +CONFIG_SAU_INIT_REGION7=y +CONFIG_SAU_INIT_START7=0x50000000 +CONFIG_SAU_INIT_END7=0x5FFFFFFF +CONFIG_ARCH_FAMILY_FE310=y + +# +# FE310 Configuration Options +# +CONFIG_PLATFORM_SPARKFUN_RED_BOARD=y + +# +# Secure Attribution Configuration +# + +# +# GPIO Secure Attribution Configuration +# + +# +# Peripheral Secure Attribution Configuration +# + +# +# Assign Interrupt to Secure or Non-secure Vector +# + +# +# Enable secure violation interrupts +# + +# +# Enable and Set Secure/Non-Secure region +# + +# +# Toolchain Configuration Options +# +# CONFIG_GCC_VERSION_6_1_2017Q1 is not set +# CONFIG_GCC_VERSION_6_1_2017Q2 is not set +# CONFIG_GCC_VERSION_8_2018Q4 is not set +CONFIG_GCC_VERSION_10_2_0_2020_12=y +CONFIG_GCC_VERSION="riscv64-unknown-elf-toolchain-10.2.0-2020.12.8-x86_64-linux-ubuntu14" +CONFIG_GCC_SITE="https://static.dev.sifive.com/dev-tools/freedom-tools/v2020.12" +CONFIG_GCC_FOLDER="riscv64-unknown-elf-toolchain-10.2.0-2020.12.8-x86_64-linux-ubuntu14" + +# +# GP TEE Configuration +# +CONFIG_MAX_CRYPTO_CHUNK_SIZE=0x400 + +# +# Application Configuration +# + +# +# Pseudo TAs: +# +# CONFIG_APPS_HELLO_WORLD is not set + +# +# User TAs: +# +# CONFIG_APPS_AES is not set +# CONFIG_APPS_HOTP is not set + +# +# Test suite: +# +# CONFIG_APPS_TEST is not set + +# +# H/W Security exception: +# +# CONFIG_APPS_HW_SECURITY_EXCEPTION_EXAMPLE is not set +# CONFIG_APPS_SPY is not set diff --git a/docs/images/platforms/sparkfun_redboard/sparkfun_redboard.png b/docs/images/platforms/sparkfun_redboard/sparkfun_redboard.png new file mode 100644 index 00000000..f023b02a Binary files /dev/null and b/docs/images/platforms/sparkfun_redboard/sparkfun_redboard.png differ diff --git a/docs/sparkfun_redboard.md b/docs/sparkfun_redboard.md new file mode 100644 index 00000000..89569228 --- /dev/null +++ b/docs/sparkfun_redboard.md @@ -0,0 +1,30 @@ +# mTower on SparkFun RED-V RedBoard - SiFive RISC-V FE310 + +[![SparkFun RED-V RedBoard](images/platforms/sparkfun_redboard/sparkfun_redboard.png)](https://www.sparkfun.com/products/15594) + +# Contents +1. [Introduction](#1-introduction) +2. [Regular build](#2-regular-build) +3. [JLINK Programming Tool](#3-jlink-programming-tool) +4. [Flash mTower on the device and run](#4-flash-mtower-on-the-device-and-run) +5. [References](#5-references) + +## 1. Introduction +The instructions here will tell how to run mTower on the [SparkFun RED-V RedBoard] board. + +## 2. Regular build +Start out by following the "Get and build the solution" in the [build.md] file. +> Warning: Need to download the toolchain (exec. `make toolchain`). + +## 3. Jlink Programming Tool +> TBD + +## 4. Flash mTower on the device and run + > TBD + +## 5. References + +[https://www.sparkfun.com/products/15594](https://www.sparkfun.com/products/15594) + +[build.md]: build.md +[SparkFun RED-V RedBoard]: https://www.sparkfun.com/products/15594 diff --git a/freertos/Makefile b/freertos/Makefile index 2ac5accd..cda175b6 100644 --- a/freertos/Makefile +++ b/freertos/Makefile @@ -24,17 +24,21 @@ CFLAGS += -I./include CFLAGS += -I$(TOPDIR)/include/mtower CFLAGS += -I$(TOPDIR)/arch/$(CONFIG_ARCH)/$(CONFIG_ARCH_FAMILY)/src/$(CONFIG_PLATFORM) -CFLAGS += -I./portable/GCC/ARM_V8M +CFLAGS += -I./portable/GCC/$(FREE_RTOS_ARCH) CFLAGS += -I../arch/$(subst ",,$(CONFIG_ARCH))/$(subst ",,$(CONFIG_ARCH_FAMILY))/src/$(subst ",,$(CONFIG_PLATFORM))/nonsecure -CFLAGS += -I../arch/cortex-m23/m2351/src/Device/Nuvoton/M2351/Include -CFLAGS += -I../arch/cortex-m23/m2351/src/CMSIS/Include -CFLAGS += -I../arch/cortex-m23/m2351/src/StdDriver/inc +CFLAGS += -I$(TOPDIR)/arch/$(CONFIG_ARCH)/$(CONFIG_ARCH_FAMILY)/src/CMSIS/Include ifeq ($(WORLD), secure) CFLAGS += -mcmse endif -SRCS = croutine.c list.c queue.c stream_buffer.c tasks.c timers.c -SRCS += portable/GCC/ARM_V8M/port.c portable/MemMang/heap_2.c +SRCS = croutine.c list.c queue.c stream_buffer.c tasks.c timers.c event_groups.c +SRCS += portable/GCC/$(FREE_RTOS_ARCH)/port.c portable/MemMang/heap_4.c + +ifeq ($(FREE_RTOS_ARCH), RISC-V) +SRCS += portable/GCC/$(FREE_RTOS_ARCH)/pmp.c +SRCS += portable/Common/mpu_wrappers.c +CFLAGS += -DportUSING_MPU_WRAPPERS=1 +endif ifneq ($(WORLD), secure) CFLAGS += -DDEBUG_PORT=$(subst ",,$(CONFIG_NONSECURE_DEBUG_UART)) diff --git a/freertos/portable/GCC/RISC-V/chip_specific_extensions/Pulpino_Vega_RV32M1RM/freertos_risc_v_chip_specific_extensions.h b/freertos/portable/GCC/RISC-V/chip_specific_extensions/Pulpino_Vega_RV32M1RM/freertos_risc_v_chip_specific_extensions.h new file mode 100644 index 00000000..ef954846 --- /dev/null +++ b/freertos/portable/GCC/RISC-V/chip_specific_extensions/Pulpino_Vega_RV32M1RM/freertos_risc_v_chip_specific_extensions.h @@ -0,0 +1,109 @@ +/* + * FreeRTOS Kernel V10.2.1 + * Copyright (C) 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and t + + o permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * http://www.FreeRTOS.org + * http://aws.amazon.com/freertos + * + * 1 tab == 4 spaces! + */ + +/* + * The FreeRTOS kernel's RISC-V port is split between the the code that is + * common across all currently supported RISC-V chips (implementations of the + * RISC-V ISA), and code that tailors the port to a specific RISC-V chip: + * + * + FreeRTOS\Source\portable\GCC\RISC-V-RV32\portASM.S contains the code that + * is common to all currently supported RISC-V chips. There is only one + * portASM.S file because the same file is built for all RISC-V target chips. + * + * + Header files called freertos_risc_v_chip_specific_extensions.h contain the + * code that tailors the FreeRTOS kernel's RISC-V port to a specific RISC-V + * chip. There are multiple freertos_risc_v_chip_specific_extensions.h files + * as there are multiple RISC-V chip implementations. + * + * !!!NOTE!!! + * TAKE CARE TO INCLUDE THE CORRECT freertos_risc_v_chip_specific_extensions.h + * HEADER FILE FOR THE CHIP IN USE. This is done using the assembler's (not the + * compiler's!) include path. For example, if the chip in use includes a core + * local interrupter (CLINT) and does not include any chip specific register + * extensions then add the path below to the assembler's include path: + * FreeRTOS\Source\portable\GCC\RISC-V-RV32\chip_specific_extensions\RV32I_CLINT_no_extensions + * + */ + +/* + * This freertos_risc_v_chip_specific_extensions.h is for use with Pulpino Ri5cy + * devices, developed and tested using the Vega board RV32M1RM. + */ + +#ifndef __FREERTOS_RISC_V_EXTENSIONS_H__ +#define __FREERTOS_RISC_V_EXTENSIONS_H__ + +#define portasmHAS_CLINT 0 + +/* Constants to define the additional registers found on the Pulpino RI5KY. */ +#define lpstart0 0x7b0 +#define lpend0 0x7b1 +#define lpcount0 0x7b2 +#define lpstart1 0x7b4 +#define lpend1 0x7b5 +#define lpcount1 0x7b6 + +/* Six additional registers to save and restore, as per the #defines above. */ +#define portasmADDITIONAL_CONTEXT_SIZE 6 /* Must be even number on 32-bit cores. */ + +/* Save additional registers found on the Pulpino. */ +.macro portasmSAVE_ADDITIONAL_REGISTERS + addi sp, sp, -(portasmADDITIONAL_CONTEXT_SIZE * portWORD_SIZE) /* Make room for the additional registers. */ + csrr t0, lpstart0 /* Load additional registers into accessible temporary registers. */ + csrr t1, lpend0 + csrr t2, lpcount0 + csrr t3, lpstart1 + csrr t4, lpend1 + csrr t5, lpcount1 + sw t0, 1 * portWORD_SIZE( sp ) + sw t1, 2 * portWORD_SIZE( sp ) + sw t2, 3 * portWORD_SIZE( sp ) + sw t3, 4 * portWORD_SIZE( sp ) + sw t4, 5 * portWORD_SIZE( sp ) + sw t5, 6 * portWORD_SIZE( sp ) + .endm + +/* Restore the additional registers found on the Pulpino. */ +.macro portasmRESTORE_ADDITIONAL_REGISTERS + lw t0, 1 * portWORD_SIZE( sp ) /* Load additional registers into accessible temporary registers. */ + lw t1, 2 * portWORD_SIZE( sp ) + lw t2, 3 * portWORD_SIZE( sp ) + lw t3, 4 * portWORD_SIZE( sp ) + lw t4, 5 * portWORD_SIZE( sp ) + lw t5, 6 * portWORD_SIZE( sp ) + csrw lpstart0, t0 + csrw lpend0, t1 + csrw lpcount0, t2 + csrw lpstart1, t3 + csrw lpend1, t4 + csrw lpcount1, t5 + addi sp, sp, (portasmADDITIONAL_CONTEXT_SIZE * portWORD_SIZE )/* Remove space added for additional registers. */ + .endm + +#endif /* __FREERTOS_RISC_V_EXTENSIONS_H__ */ diff --git a/freertos/portable/GCC/RISC-V/chip_specific_extensions/RV32I_CLINT_no_extensions/freertos_risc_v_chip_specific_extensions.h b/freertos/portable/GCC/RISC-V/chip_specific_extensions/RV32I_CLINT_no_extensions/freertos_risc_v_chip_specific_extensions.h new file mode 100644 index 00000000..b3562abe --- /dev/null +++ b/freertos/portable/GCC/RISC-V/chip_specific_extensions/RV32I_CLINT_no_extensions/freertos_risc_v_chip_specific_extensions.h @@ -0,0 +1,68 @@ +/* + * FreeRTOS Kernel V10.2.1 + * Copyright (C) 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * http://www.FreeRTOS.org + * http://aws.amazon.com/freertos + * + * 1 tab == 4 spaces! + */ + +/* + * The FreeRTOS kernel's RISC-V port is split between the the code that is + * common across all currently supported RISC-V chips (implementations of the + * RISC-V ISA), and code that tailors the port to a specific RISC-V chip: + * + * + FreeRTOS\Source\portable\GCC\RISC-V-RV32\portASM.S contains the code that + * is common to all currently supported RISC-V chips. There is only one + * portASM.S file because the same file is built for all RISC-V target chips. + * + * + Header files called freertos_risc_v_chip_specific_extensions.h contain the + * code that tailors the FreeRTOS kernel's RISC-V port to a specific RISC-V + * chip. There are multiple freertos_risc_v_chip_specific_extensions.h files + * as there are multiple RISC-V chip implementations. + * + * !!!NOTE!!! + * TAKE CARE TO INCLUDE THE CORRECT freertos_risc_v_chip_specific_extensions.h + * HEADER FILE FOR THE CHIP IN USE. This is done using the assembler's (not the + * compiler's!) include path. For example, if the chip in use includes a core + * local interrupter (CLINT) and does not include any chip specific register + * extensions then add the path below to the assembler's include path: + * FreeRTOS\Source\portable\GCC\RISC-V-RV32\chip_specific_extensions\RV32I_CLINT_no_extensions + * + */ + + +#ifndef __FREERTOS_RISC_V_EXTENSIONS_H__ +#define __FREERTOS_RISC_V_EXTENSIONS_H__ + +#define portasmHAS_CLINT 1 +#define portasmADDITIONAL_CONTEXT_SIZE 0 /* Must be even number on 32-bit cores. */ + +.macro portasmSAVE_ADDITIONAL_REGISTERS + /* No additional registers to save, so this macro does nothing. */ + .endm + +/* Restore the additional registers found on the Pulpino. */ +.macro portasmRESTORE_ADDITIONAL_REGISTERS + /* No additional registers to restore, so this macro does nothing. */ + .endm + +#endif /* __FREERTOS_RISC_V_EXTENSIONS_H__ */ diff --git a/freertos/portable/GCC/RISC-V/chip_specific_extensions/readme.txt b/freertos/portable/GCC/RISC-V/chip_specific_extensions/readme.txt new file mode 100644 index 00000000..69d98d92 --- /dev/null +++ b/freertos/portable/GCC/RISC-V/chip_specific_extensions/readme.txt @@ -0,0 +1,23 @@ +/* + * The FreeRTOS kernel's RISC-V port is split between the the code that is + * common across all currently supported RISC-V chips (implementations of the + * RISC-V ISA), and code that tailors the port to a specific RISC-V chip: + * + * + FreeRTOS\Source\portable\GCC\RISC-V-RV32\portASM.S contains the code that + * is common to all currently supported RISC-V chips. There is only one + * portASM.S file because the same file is built for all RISC-V target chips. + * + * + Header files called freertos_risc_v_chip_specific_extensions.h contain the + * code that tailors the FreeRTOS kernel's RISC-V port to a specific RISC-V + * chip. There are multiple freertos_risc_v_chip_specific_extensions.h files + * as there are multiple RISC-V chip implementations. + * + * !!!NOTE!!! + * TAKE CARE TO INCLUDE THE CORRECT freertos_risc_v_chip_specific_extensions.h + * HEADER FILE FOR THE CHIP IN USE. This is done using the assembler's (not the + * compiler's!) include path. For example, if the chip in use includes a core + * local interrupter (CLINT) and does not include any chip specific register + * extensions then add the path below to the assembler's include path: + * FreeRTOS\Source\portable\GCC\RISC-V-RV32\chip_specific_extensions\RV32I_CLINT_no_extensions + * + */ diff --git a/freertos/portable/GCC/RISC-V/pmp.c b/freertos/portable/GCC/RISC-V/pmp.c new file mode 100644 index 00000000..9c82e461 --- /dev/null +++ b/freertos/portable/GCC/RISC-V/pmp.c @@ -0,0 +1,830 @@ +/* Copyright 2020 SiFive, Inc */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "pmp.h" + +/** + * @brief get the mxlen from misa register + * + */ +static __attribute__((naked, unused)) int32_t get_mxlen (void) +{ + __asm__ __volatile__ ( + " csrr a0, misa \n" /* get misa reg. */ + " bgt a0, zero, 1f \n" /* check if > 0 */ + " slli a1 , a0, 1 \n" /* shift left of 1 bit */ + " blt a1, zero, 2f \n" /* check if < 0 */ + " li a0, 64 \n" /* else mxlen = 32 */ + " ret \n" + "1: \n" + " li a0, 32 \n" + " ret \n" + "2: \n" + " li a0, 128 \n" + " ret \n" + ::: "a0", "a1" + ); + + /* just to fix warning */ + return(0); +} + +static int32_t _get_detected_granularity(size_t address) { + if (0 == address) { + return PMP_INVALID_PARAM; + } + + /* Get the index of the least significant set bit */ + int index = 0; + while (((address >> index) & 0x1) == 0) { + index += 1; + } + + /* The granularity is equal to 2^(index + 2) bytes */ + return (1 << (index + 2)); +} + + +/** + * @brief initialize pmp, basically compute pmp granularity and number of region + * + * @param[out] pmp_info info on pmp + * @return int32_t + */ +int32_t init_pmp (pmp_info_t * pmp_info) +{ + int32_t result = PMP_DEFAULT_ERROR; + size_t i = 0; + uint8_t pmp_config = 0; + size_t address = 0; + + if ( NULL == pmp_info ) { + return(PMP_INVALID_POINTER); + } + + pmp_info->nb_pmp = MAX_PMP_REGION; + + for (i = 0; i < MAX_PMP_REGION; i++) { + + pmp_config = 0; + address = 0; + result = write_pmp_config (pmp_info, i, pmp_config, address); + if(PMP_SUCCESS > result) { + return(result); + } + + address = -1; + result = write_pmp_config (pmp_info, i, pmp_config, address); + if(PMP_SUCCESS > result) { + return(result); + } + + result = read_pmp_config (pmp_info, i, &pmp_config, &address); + if(PMP_SUCCESS > result) { + return(result); + } + + /* Compute granularity */ + if(0 == i) { + result = _get_detected_granularity(address); + if(PMP_SUCCESS > result) { + return(result); + } + pmp_info->granularity = result; + } + + /* Check number of active region */ + if(0 == address) { + pmp_info->nb_pmp = i; + break; + } + } + + return(PMP_SUCCESS); +} + +int32_t set_pmp_config (pmp_cfg_t * config, uint8_t * register_val) +{ + uint8_t reg_temp = 0; + + if ( (NULL == config) || (NULL == register_val) ) { + return(PMP_INVALID_POINTER); + } + + if (true == config->R) { + reg_temp |= 1 << PMP_READ_RIGHT_OFFSET; + } + + if (true == config->W) { + reg_temp |= 1 << PMP_WRITE_RIGHT_OFFSET; + } + + if (true == config->X) { + reg_temp |= 1 << PMP_EXECUTE_RIGHT_OFFSET; + } + + if (true == config->L) { + reg_temp |= 1 << PMP_LOCK_RIGHT_OFFSET; + } + + switch (config->A) + { + case PMP_OFF: + case PMP_TOR: + case PMP_NA4: + case PMP_NAPOT: + reg_temp |= config->A << PMP_ADDRESS_RIGHT_OFFSET; + break; + default: + return(PMP_INVALID_PARAM); + } + + *register_val = reg_temp; + + return(PMP_SUCCESS); +} + +int32_t get_pmp_config (uint8_t * register_val, pmp_cfg_t * config) +{ + if ( (NULL == config) || (NULL == register_val) ) { + return(PMP_INVALID_POINTER); + } + + config->R = *register_val & PMP_CFG_1_BIT_MASK; + config->W = (*register_val >> PMP_WRITE_RIGHT_OFFSET) & PMP_CFG_1_BIT_MASK; + config->X = (*register_val >> PMP_EXECUTE_RIGHT_OFFSET) & PMP_CFG_1_BIT_MASK; + config->A = (*register_val >> PMP_ADDRESS_RIGHT_OFFSET) & PMP_CFG_2_BIT_MASK; + config->L = (*register_val >> PMP_LOCK_RIGHT_OFFSET) & PMP_CFG_1_BIT_MASK; + + return(PMP_SUCCESS); +} + + +int32_t write_pmp_config (pmp_info_t * pmp_info, uint32_t region, + uint8_t pmp_config, size_t address) +{ + uint32_t bit_shift = 0; + + /* check pointer not null */ + if (NULL == pmp_info) { + return(PMP_INVALID_POINTER); + } + + /* check region */ + if (region >= pmp_info->nb_pmp) { + return(PMP_INVALID_PARAM); + } + + switch (region) { + case 0: + __asm__ __volatile__("csrw pmpaddr0, %[addr]" ::[addr] "r"(address) :); + break; + case 1: + __asm__ __volatile__("csrw pmpaddr1, %[addr]" ::[addr] "r"(address) :); + break; + case 2: + __asm__ __volatile__("csrw pmpaddr2, %[addr]" ::[addr] "r"(address) :); + break; + case 3: + __asm__ __volatile__("csrw pmpaddr3, %[addr]" ::[addr] "r"(address) :); + break; + case 4: + __asm__ __volatile__("csrw pmpaddr4, %[addr]" ::[addr] "r"(address) :); + break; + case 5: + __asm__ __volatile__("csrw pmpaddr5, %[addr]" ::[addr] "r"(address) :); + break; + case 6: + __asm__ __volatile__("csrw pmpaddr6, %[addr]" ::[addr] "r"(address) :); + break; + case 7: + __asm__ __volatile__("csrw pmpaddr7, %[addr]" ::[addr] "r"(address) :); + break; + case 8: + __asm__ __volatile__("csrw pmpaddr8, %[addr]" ::[addr] "r"(address) :); + break; + case 9: + __asm__ __volatile__("csrw pmpaddr9, %[addr]" ::[addr] "r"(address) :); + break; + case 10: + __asm__ __volatile__("csrw pmpaddr10, %[addr]" ::[addr] "r"(address) :); + break; + case 11: + __asm__ __volatile__("csrw pmpaddr11, %[addr]" ::[addr] "r"(address) :); + break; + case 12: + __asm__ __volatile__("csrw pmpaddr12, %[addr]" ::[addr] "r"(address) :); + break; + case 13: + __asm__ __volatile__("csrw pmpaddr13, %[addr]" ::[addr] "r"(address) :); + break; + case 14: + __asm__ __volatile__("csrw pmpaddr14, %[addr]" ::[addr] "r"(address) :); + break; + case 15: + __asm__ __volatile__("csrw pmpaddr15, %[addr]" ::[addr] "r"(address) :); + break; + } + +#if __riscv_xlen == 32 + bit_shift = (region % 4) << 3; + switch (region / 4) { + case 0: + __asm__ __volatile__( + "li t1, 0xFF\n" + "sll t1, t1, %[bit_shift] \n" + "sll t2, %[cfg], %[bit_shift] \n" + "csrc pmpcfg0, t1 \n" + "csrs pmpcfg0, t2 \n" + : + : [bit_shift] "r"(bit_shift), [cfg] "r"(pmp_config) + : "t1", "t2" + ); + break; + case 1: + __asm__ __volatile__( + "li t1, 0xFF\n" + "sll t1, t1, %[bit_shift] \n" + "sll t2, %[cfg], %[bit_shift] \n" + "csrc pmpcfg1, t1 \n" + "csrs pmpcfg1, t2 \n" + : + : [bit_shift] "r"(bit_shift), [cfg] "r"(pmp_config) + : "t1", "t2" + ); + break; + case 2: + __asm__ __volatile__( + "li t1, 0xFF\n" + "sll t1, t1, %[bit_shift] \n" + "sll t2, %[cfg], %[bit_shift] \n" + "csrc pmpcfg2, t1 \n" + "csrs pmpcfg2, t2 \n" + : + : [bit_shift] "r"(bit_shift), [cfg] "r"(pmp_config) + : "t1", "t2" + ); + break; + case 3: + __asm__ __volatile__( + "li t1, 0xFF\n" + "sll t1, t1, %[bit_shift] \n" + "sll t2, %[cfg], %[bit_shift] \n" + "csrc pmpcfg3, t1 \n" + "csrs pmpcfg3, t2 \n" + : + : [bit_shift] "r"(bit_shift), [cfg] "r"(pmp_config) + : "t1", "t2" + ); + break; + } +#elif __riscv_xlen == 64 + bit_shift = (region % 8) << 3; + switch (region / 8) { + case 0: + __asm__ __volatile__( + "li t1, 0xFF\n" + "sll t1, t1, %[bit_shift] \n" + "sll t2, %[cfg], %[bit_shift] \n" + "csrc pmpcfg0, t1 \n" + "csrs pmpcfg0, t2 \n" + : + : [bit_shift] "r"(bit_shift), [cfg] "r"(pmp_config) + : "t1", "t2" + ); + break; + case 1: + __asm__ __volatile__( + "li t1, 0xFF\n" + "sll t1, t1, %[bit_shift] \n" + "sll t2, %[cfg], %[bit_shift] \n" + "csrc pmpcfg2, t1 \n" + "csrs pmpcfg2, t2 \n" + : + : [bit_shift] "r"(bit_shift), [cfg] "r"(pmp_config) + : "t1", "t2" + ); + break; + } +#else + return (PMP_ERR_UNSUPPORTED); +#endif + + return(PMP_SUCCESS); + +} + +int32_t read_pmp_config (pmp_info_t * pmp_info, uint32_t region, + uint8_t * pmp_config, size_t * address) +{ + uint8_t pmp_config_temp = 0; + + if ( (NULL == pmp_info) || (NULL == pmp_config) || (NULL == address) ) { + return(PMP_INVALID_POINTER); + } + + /* check region */ + if (region >= pmp_info->nb_pmp) { + return(PMP_INVALID_PARAM); + } + +#if __riscv_xlen == 32 + switch (region / 4) { + case 0: + __asm__ __volatile__( + "csrr %[cfg], pmpcfg0" + : [cfg] "=r"(pmp_config_temp) + :: + ); + break; + case 1: + __asm__ __volatile__( + "csrr %[cfg], pmpcfg1" + : [cfg] "=r"(pmp_config_temp) + :: + ); + break; + case 2: + __asm__ __volatile__( + "csrr %[cfg], pmpcfg2" + : [cfg] "=r"(pmp_config_temp) + :: + ); + break; + case 3: + __asm__ __volatile__( + "csrr %[cfg], pmpcfg3" + : [cfg] "=r"(pmp_config_temp) + :: + ); + break; + } + + *pmp_config = (0xFF & (pmp_config_temp >> (8 * (region % 4)))); +#elif __riscv_xlen == 64 + switch (region / 8) { + case 0: + __asm__ __volatile__( + "csrr %[cfg], pmpcfg0" + : [cfg] "=r"(pmp_config_temp) + :: + ); + break; + case 1: + __asm__ __volatile__( + "csrr %[cfg], pmpcfg2" + : [cfg] "=r"(pmp_config_temp) + :: + ); + break; + } + + *pmp_config = (0xFF & (pmp_config_temp >> (8 * (region % 8)))); +#else + return (PMP_ERR_UNSUPPORTED); +#endif + + switch (region) { + case 0: + __asm__ __volatile__("csrr %[addr], pmpaddr0" : [addr] "=r"(*address)::); + break; + case 1: + __asm__ __volatile__("csrr %[addr], pmpaddr1" : [addr] "=r"(*address)::); + break; + case 2: + __asm__ __volatile__("csrr %[addr], pmpaddr2" : [addr] "=r"(*address)::); + break; + case 3: + __asm__ __volatile__("csrr %[addr], pmpaddr3" : [addr] "=r"(*address)::); + break; + case 4: + __asm__ __volatile__("csrr %[addr], pmpaddr4" : [addr] "=r"(*address)::); + break; + case 5: + __asm__ __volatile__("csrr %[addr], pmpaddr5" : [addr] "=r"(*address)::); + break; + case 6: + __asm__ __volatile__("csrr %[addr], pmpaddr6" : [addr] "=r"(*address)::); + break; + case 7: + __asm__ __volatile__("csrr %[addr], pmpaddr7" : [addr] "=r"(*address)::); + break; + case 8: + __asm__ __volatile__("csrr %[addr], pmpaddr8" : [addr] "=r"(*address)::); + break; + case 9: + __asm__ __volatile__("csrr %[addr], pmpaddr9" : [addr] "=r"(*address)::); + break; + case 10: + __asm__ __volatile__("csrr %[addr], pmpaddr10" : [addr] "=r"(*address)::); + break; + case 11: + __asm__ __volatile__("csrr %[addr], pmpaddr11" : [addr] "=r"(*address)::); + break; + case 12: + __asm__ __volatile__("csrr %[addr], pmpaddr12" : [addr] "=r"(*address)::); + break; + case 13: + __asm__ __volatile__("csrr %[addr], pmpaddr13" : [addr] "=r"(*address)::); + break; + case 14: + __asm__ __volatile__("csrr %[addr], pmpaddr14" : [addr] "=r"(*address)::); + break; + case 15: + __asm__ __volatile__("csrr %[addr], pmpaddr15" : [addr] "=r"(*address)::); + break; + } + + return(PMP_SUCCESS); +} + +int32_t read_pmp_configs (size_t * configs, uint32_t pmp_region) +{ + size_t jump_idx = 0; + + if ( NULL == configs ) { + return(PMP_INVALID_POINTER); + } + + jump_idx = (NB_PMP_CFG_REG - (pmp_region / SIZE_PMP_CFG_REG)) << 3; + +#if __riscv_xlen == 32 + __asm__ __volatile__ ( + "la t0, 1f \n" + "add t0, t0, %[jump_idx] \n" + "jr t0 \n" + "1: \n" + "csrr t2, pmpcfg3 \n" + "sw t2, 12(%[configs]) \n" + "csrr t1, pmpcfg2 \n" + "sw t1, 8(%[configs]) \n" + "csrr t2, pmpcfg1 \n" + "sw t2, 4(%[configs]) \n" + "csrr t1, pmpcfg0 \n" + "sw t1, 0(%[configs]) \n" + : + : [configs] "r" (configs), + [jump_idx] "r" (jump_idx) + : "t0", "t1", "t2" + ); +#elif __riscv_xlen == 64 + __asm__ __volatile__ ( + "la t0, 1f \n" + "add t0, t0, %[jump_idx] \n" + "jr t0 \n" + "1: \n" + "csrr t2, pmpcfg2 \n" + "sd t2, 8(%[configs]) \n" + "csrr t1, pmpcfg0 \n" + "sd t1, 0(%[configs]) \n" + : + : [configs] "r" (configs), + [jump_idx] "r" (jump_idx) + : "t0", "t1", "t2" + ); +#else + return (PMP_ERR_UNSUPPORTED); +#endif + + return(PMP_SUCCESS); +} + +int32_t write_pmp_configs (size_t * configs, uint32_t pmp_region) +{ + size_t jump_idx = 0; + + if ( NULL == configs ) { + return(PMP_INVALID_POINTER); + } + + jump_idx = (NB_PMP_CFG_REG - (pmp_region / SIZE_PMP_CFG_REG)) << 3; + +#if __riscv_xlen == 32 + __asm__ __volatile__ ( + "la t0, 1f \n" + "add t0, t0, %[jump_idx] \n" + "jr t0 \n" + "1: \n" + "lw t2, 12(%[configs]) \n" + "csrw pmpcfg3, t2 \n" + "lw t1, 8(%[configs]) \n" + "csrw pmpcfg2, t1 \n" + "lw t2, 4(%[configs]) \n" + "csrw pmpcfg1, t2 \n" + "lw t1, 0(%[configs]) \n" + "csrw pmpcfg0, t1 \n" + : + : [configs] "r" (configs), + [jump_idx] "r" (jump_idx) + : "t0", "t1", "t2" + ); +#elif __riscv_xlen == 64 + __asm__ __volatile__ ( + "la t0, 1f \n" + "add t0, t0, %[jump_idx] \n" + "jr t0 \n" + "1: \n" + "ld t2, 8(%[configs]) \n" + "csrw pmpcfg2, t2 \n" + "ld t1, 0(%[configs]) \n" + "csrw pmpcfg0, t1 \n" + : + : [configs] "r" (configs), + [jump_idx] "r" (jump_idx) + : "t0", "t1", "t2" + ); +#else + return (PMP_ERR_UNSUPPORTED); +#endif + + return(PMP_SUCCESS); +} + +int32_t write_pmp_addrs (size_t * address, uint32_t pmp_region) +{ + size_t inst_offset = 0; + + if ( NULL == address ) { + return(PMP_INVALID_POINTER); + } + + if ( MAX_PMP_REGION < pmp_region ) { + return(PMP_INVALID_PARAM); + } + +#if __riscv_xlen == 32 + inst_offset = (MAX_PMP_REGION - pmp_region) << 3; + + __asm__ __volatile__ ( + "la t1, 1f \n" + "add t2, t1, %[inst_offset] \n" + "jr t2 \n" + "1: \n" + "lw t2, 60(%[address]) \n" + "csrw pmpaddr15, t2 \n" + "lw t1, 56(%[address]) \n" + "csrw pmpaddr14, t1 \n" + "lw t2, 52(%[address]) \n" + "csrw pmpaddr13, t2 \n" + "lw t1, 48(%[address]) \n" + "csrw pmpaddr12, t1 \n" + "lw t1, 44(%[address]) \n" + "csrw pmpaddr11, t2 \n" + "lw t1, 40(%[address]) \n" + "csrw pmpaddr10, t1 \n" + "lw t2, 36(%[address]) \n" + "csrw pmpaddr9, t2 \n" + "lw t1, 32(%[address]) \n" + "csrw pmpaddr8, t1 \n" + "lw t2, 28(%[address]) \n" + "csrw pmpaddr7, t2 \n" + "lw t1, 24(%[address]) \n" + "csrw pmpaddr6, t1 \n" + "lw t2, 20(%[address]) \n" + "csrw pmpaddr5, t2 \n" + "lw t1, 16(%[address]) \n" + "csrw pmpaddr4, t1 \n" + "lw t2, 12(%[address]) \n" + "csrw pmpaddr3, t2 \n" + "lw t1, 8(%[address]) \n" + "csrw pmpaddr2, t1 \n" + "lw t2, 4(%[address]) \n" + "csrw pmpaddr1, t2 \n" + "lw t1, 0(%[address]) \n" + "csrw pmpaddr0, t1 \n" + : + : [address] "r" (address), + [inst_offset] "r" (inst_offset) + : "t1", "t2" + ); +#elif __riscv_xlen == 64 + inst_offset = (MAX_PMP_REGION - pmp_region) << 3; + + __asm__ __volatile__ ( + "la t1, 1f \n" + "add t2, t1, %[inst_offset] \n" + "jr t2 \n" + "1: \n" + "ld t4, 120(%[address]) \n" + "csrw pmpaddr15, t4 \n" + "ld t3, 112(%[address]) \n" + "csrw pmpaddr14, t3 \n" + "ld t2, 104(%[address]) \n" + "csrw pmpaddr13, t2 \n" + "ld t1, 96(%[address]) \n" + "csrw pmpaddr12, t1 \n" + "ld t4, 88(%[address]) \n" + "csrw pmpaddr11, t4 \n" + "ld t3, 80(%[address]) \n" + "csrw pmpaddr10, t3 \n" + "ld t2, 72(%[address]) \n" + "csrw pmpaddr9, t2 \n" + "ld t1, 64(%[address]) \n" + "csrw pmpaddr8, t1 \n" + "ld t4, 56(%[address]) \n" + "csrw pmpaddr7, t4 \n" + "ld t3, 48(%[address]) \n" + "csrw pmpaddr6, t3 \n" + "ld t2, 40(%[address]) \n" + "csrw pmpaddr5, t2 \n" + "ld t1, 32(%[address]) \n" + "csrw pmpaddr4, t1 \n" + "ld t4, 24(%[address]) \n" + "csrw pmpaddr3, t4 \n" + "ld t3, 16(%[address]) \n" + "csrw pmpaddr2, t3 \n" + "ld t2, 8(%[address]) \n" + "csrw pmpaddr1, t2 \n" + "ld t1, 0(%[address]) \n" + "csrw pmpaddr0, t1 \n" + : + : [address] "r" (address), + [inst_offset] "r" (inst_offset) + : "t1", "t2", "t3", "t4" + ); +#else + return (PMP_ERR_UNSUPPORTED); +#endif + + return(PMP_SUCCESS); +} + +int32_t read_pmp_addrs (size_t * address, uint32_t pmp_region) +{ + uint32_t inst_offset = 0; + + if ( NULL == address ) { + return(PMP_INVALID_POINTER); + } + + if ( MAX_PMP_REGION < pmp_region ) { + return(PMP_INVALID_PARAM); + } + +#if __riscv_xlen == 32 + inst_offset = (MAX_PMP_REGION - pmp_region) << 3; + + __asm__ __volatile__ ( + "la t1, 1f \n" + "add t2, t1, %[inst_offset] \n" + "jr t2 \n" + "1: \n" + "csrr t2, pmpaddr15 \n" + "sw t2, 60(%[address]) \n" + "csrr t1, pmpaddr14 \n" + "sw t1, 56(%[address]) \n" + "csrr t2, pmpaddr13 \n" + "sw t2, 52(%[address]) \n" + "csrr t1, pmpaddr12 \n" + "sw t1, 48(%[address]) \n" + "csrr t2, pmpaddr11 \n" + "sw t2, 44(%[address]) \n" + "csrr t1, pmpaddr10 \n" + "sw t1, 40(%[address]) \n" + "csrr t2, pmpaddr9 \n" + "sw t2, 36(%[address]) \n" + "csrr t1, pmpaddr8 \n" + "sw t1, 32(%[address]) \n" + "csrr t2, pmpaddr7 \n" + "sw t2, 28(%[address]) \n" + "csrr t1, pmpaddr6 \n" + "sw t1, 24(%[address]) \n" + "csrr t2, pmpaddr5 \n" + "sw t2, 20(%[address]) \n" + "csrr t1, pmpaddr4 \n" + "sw t1, 16(%[address]) \n" + "csrr t2, pmpaddr3 \n" + "sw t2, 12(%[address]) \n" + "csrr t1, pmpaddr2 \n" + "sw t1, 8(%[address]) \n" + "csrr t2, pmpaddr1 \n" + "sw t2, 4(%[address]) \n" + "csrr t1, pmpaddr0 \n" + "sw t1, 0(%[address]) \n" + : + : [address] "r" (address), + [inst_offset] "r" (inst_offset) + : "t1", "t2" + ); +#elif __riscv_xlen == 64 + inst_offset = (MAX_PMP_REGION - pmp_region) << 3; + + __asm__ __volatile__ ( + "la t1, 1f \n" + "add t2, t1, %[inst_offset] \n" + "jr t2 \n" + "1: \n" + "csrr t4, pmpaddr15 \n" + "sd t4, 120(%[address]) \n" + "csrr t3, pmpaddr14 \n" + "sd t3, 112(%[address]) \n" + "csrr t2, pmpaddr13 \n" + "sd t2, 104(%[address]) \n" + "csrr t1, pmpaddr12 \n" + "sd t1, 96(%[address]) \n" + "csrr t4, pmpaddr11 \n" + "sd t4, 88(%[address]) \n" + "csrr t3, pmpaddr10 \n" + "sd t3, 80(%[address]) \n" + "csrr t2, pmpaddr9 \n" + "sd t2, 72(%[address]) \n" + "csrr t1, pmpaddr8 \n" + "sd t1, 64(%[address]) \n" + "csrr t4, pmpaddr7 \n" + "sd t4, 56(%[address]) \n" + "csrr t3, pmpaddr6 \n" + "sd t3, 48(%[address]) \n" + "csrr t2, pmpaddr5 \n" + "sd t2, 40(%[address]) \n" + "csrr t1, pmpaddr4 \n" + "sd t1, 32(%[address]) \n" + "csrr t4, pmpaddr3 \n" + "sd t4, 24(%[address]) \n" + "csrr t3, pmpaddr2 \n" + "sd t3, 16(%[address]) \n" + "csrr t2, pmpaddr1 \n" + "sd t2, 8(%[address]) \n" + "csrr t1, pmpaddr0 \n" + "sd t1, 0(%[address]) \n" + : + : [address] "r" (address), + [inst_offset] "r" (inst_offset) + : "t1", "t2", "t3", "t4" + ); +#else + return (PMP_ERR_UNSUPPORTED); +#endif + + return(PMP_SUCCESS); +} + +int32_t addr_modifier ( size_t granularity, + size_t address_in, + size_t * address_out) { + + if(NULL == address_out) { + return(PMP_INVALID_POINTER); + } + + if ( 0 != address_in % granularity ) { + return(PMP_UNALIGNED_ADDRESS); + } + + *address_out = address_in >> 2; + + return(PMP_SUCCESS); +} + +int32_t napot_addr_modifier (size_t granularity, + size_t address_in, + size_t * address_out, + size_t size) +{ + size_t index = 0; + + if(NULL == address_out) { + return(PMP_INVALID_POINTER); + } + + // check size is power of 2 and superior or equal to 4 + if ( granularity > size ) { + return(PMP_ERR_GRANULARITY); + } + + if ( 0 != address_in % granularity ) { + return(PMP_UNALIGNED_ADDRESS); + } + + // in case the whole adressable space is requested + if ((size_t)-1 == size) { + size = size >> 1; + size += 1; + size = size >> 1; + } else { + size = size >> 2; + } + + address_in = address_in >> 2; + + /* check if size is a power of 2 */ + for (index = 0; index < sizeof(size_t); index++ ) { + if(0 != ((size >> index) & 1)) { + if(index + 1 == sizeof(size_t)) { + break; + } else if( 0 == (size >> (index + 1)) ) { + break; + } else { + return(PMP_INVALID_NAPOT_SIZE); + } + } + } + + /* check alignment between size and address */ + if(0 != address_in % size) { + return(PMP_UNALIGNED_ADDRESS); + } + + *address_out = address_in | ((size -1) >> 1); + + return(PMP_SUCCESS); +} diff --git a/freertos/portable/GCC/RISC-V/pmp.h b/freertos/portable/GCC/RISC-V/pmp.h new file mode 100644 index 00000000..09936551 --- /dev/null +++ b/freertos/portable/GCC/RISC-V/pmp.h @@ -0,0 +1,246 @@ +/* Copyright 2018 SiFive, Inc */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include +#include +#include + +#define MAX_PMP_REGION (16UL) + +#if __riscv_xlen == 32 +#define NB_PMP_CFG_REG (4) +#define SIZE_PMP_CFG_REG (4) +#elif __riscv_xlen == 64 +#define NB_PMP_CFG_REG (2) +#define SIZE_PMP_CFG_REG (8) +#endif + +#define PMP_CFG_1_BIT_MASK (1) +#define PMP_CFG_2_BIT_MASK (3) + +#define PMP_READ_RIGHT_OFFSET (0) +#define PMP_WRITE_RIGHT_OFFSET (1) +#define PMP_EXECUTE_RIGHT_OFFSET (2) +#define PMP_ADDRESS_RIGHT_OFFSET (3) +#define PMP_LOCK_RIGHT_OFFSET (7) + +#define PMP_READ_RIGHT_MASK (PMP_READ_RIGHT_OFFSET << \ + PMP_CFG_1_BIT_MASK) +#define PMP_WRITE_RIGHT_MASK (PMP_WRITE_RIGHT_OFFSET << \ + PMP_CFG_1_BIT_MASK) +#define PMP_EXECUTE_RIGHT_MASK (PMP_EXECUTE_RIGHT_OFFSET << \ + PMP_CFG_1_BIT_MASK) +#define PMP_ADDRESS_RIGHT_MASK (PMP_CFG_2_BIT_MASK << \ + PMP_ADDRESS_RIGHT_OFFSET) +#define PMP_LOCK_RIGHT_MASK (PMP_LOCK_RIGHT_OFFSET << \ + PMP_CFG_1_BIT_MASK) + +/** + * @brief pmp information + */ +typedef struct { + /** @brief Number of PMP javailable on the hart */ + uint32_t nb_pmp; + /** @brief smallest granularity available on the hart */ + uint32_t granularity; +} pmp_info_t ; + +/** + * @brief pmp configuration structure + */ +typedef struct { + /** @brief read right configuration */ + uint8_t R; + /** @brief write right configuration */ + uint8_t W; + /** @brief execute right configuration */ + uint8_t X; + /** @brief address matching configuration */ + uint8_t A; + /** @brief lock mode */ + uint8_t L; +} pmp_cfg_t ; + +/** + * @brief PMP address strcuture + */ +typedef struct { + /* address base value */ + size_t address; + /* granularity value of the region */ + size_t granularity; +} pmp_addr_t ; + +/** + * @brief error code associated to PMP + * @details the return value should be a signed 32 bits value, this way error + * @details are coded as negative values, status code as positive values and + * @details success is 0 + */ +enum pmp_error_e { + /* in case of success */ + PMP_SUCCESS = 0, + /* default error, the cause is blurry (probably undesired execution path) */ + PMP_DEFAULT_ERROR = (int)0x80000000, + /* invalid pointer */ + PMP_INVALID_POINTER, + /* error system garnularity incompatible */ + PMP_ERR_GRANULARITY, + /* invalid NAPOT size (not power of 2) */ + PMP_INVALID_NAPOT_SIZE, + /* unaligned address */ + PMP_UNALIGNED_ADDRESS, + /* invalid parameter */ + PMP_INVALID_PARAM, + /* unsupported */ + PMP_ERR_UNSUPPORTED, +}; + +/** + * @brief address mode + */ +enum pmp_address_mode_e { + /* Null Region */ + PMP_OFF = 0, + /* Top of Range mode */ + PMP_TOR, + /* Naturally aligned four-bytes region */ + PMP_NA4, + /* Naturally aligned power-of-two region, >= 8 bytes */ + PMP_NAPOT, +}; + +/** + * @brief get the number of pmp for the hart and granularity + * + * @param pmp_info structure that contains numbre of pmp and granularity + * @return int32_t 0 on success + * @return int32_t < 0 on failure + */ +int32_t init_pmp (pmp_info_t * pmp_info); + +/** + * @brief Set the pmp config object + * + * @param[in] config PMP configuration structure + * @param[out] register_val config value of the region in pmpcfgx + * @return int32_t 0 in case of success + * @return int32_t <0 otherwise + */ +int32_t set_pmp_config (pmp_cfg_t * config, uint8_t * register_val); + +/** + * @brief Get the pmp config object + * + * @param[in] register_val config value of the region in pmpcfgx + * @param[out] config PMP configuration structure + * @return int32_t 0 in case of success + * @return int32_t <0 otherwise + */ +int32_t get_pmp_config (uint8_t * register_val, pmp_cfg_t * config); + +/** + * @brief + * + * @param[in] pmp_info PMP information (number of PMP and hart granularity) + * @param[in] region PMP region to configure + * @param[in] pmp_config PMP configuration value (byte in pmpcfx) + * @param[in] address Value that will be set in pmpaddrx of the PMP region + * @return int32_t 0 in case of success + * @return int32_t <0 otherwise + */ +int32_t write_pmp_config (pmp_info_t * pmp_info, uint32_t region, + uint8_t pmp_config, size_t address); + +/** + * @brief + * + * @param[in] pmp_info PMP information (number of PMP and hart granularity) + * @param[in] region PMP region from which to read configuration + * @param[out] pmp_config PMP configuration value (byte in pmpcfx) + * @param[out] address value that will be read from pmpaddrx of the PMP region + * @return int32_t 0 in case of success + * @return int32_t <0 otherwise + */ +int32_t read_pmp_config (pmp_info_t * pmp_info, uint32_t region, + uint8_t * pmp_config, size_t * address); + +/** + * @brief write all PMP pmpcfgx registers in one time + * + * @param[in] configs PMP config table (pmpcfx table) + * @param[in] pmp_region number of pmp regions available + * @return int32_t 0 in case of success + * @return int32_t <0 otherwise + * @warning configs address should be aligned (32 bits for RV32 64 bits for RV64) + * @warning RV64) + * @warning address table should be of + * @warning sizeof(size_t)*(pmp_region / SIZE_PMP_CFG_REG) length + */ +int32_t write_pmp_configs (size_t * configs, uint32_t pmp_region); +/** + * @brief read all PMP pmpcfgx registers in one time + * + * @param[out] configs PMP config table (pmpcfx table) + * @param[in] pmp_region number of pmp regions available + * @return int32_t 0 in case of success + * @return int32_t <0 otherwise + * @warning configs address should be aligned (32 bits for RV32 64 bits for RV64) + * @warning RV64) + * @warning address table should be of + * @warning sizeof(size_t)*(pmp_region / SIZE_PMP_CFG_REG) length + */ +int32_t read_pmp_configs (size_t * configs, uint32_t pmp_region); + +/** + * @brief write all PMP pmpaddrx in one time + * + * @param[in] address PMP address table (pmpaddrx table) + * @param[in] pmp_region number of pmp regions available + * @return int32_t 0 in case of success + * @return int32_t <0 otherwise + * @warning address table should be of sizeof(size_t)*pmp_region length + */ +int32_t write_pmp_addrs (size_t * address, uint32_t pmp_region); + +/** + * @brief read all PMP pmpaddrx in one time + * + * @param[out] address PMP address table (pmpaddrx table) + * @param[in] pmp_region number of pmp regions available + * @return int32_t 0 in case of success + * @return int32_t <0 otherwise + * @warning address table should be of sizeof(size_t)*pmp_region length + */ +int32_t read_pmp_addrs (size_t * address, uint32_t pmp_region); + +/** + * @brief compute the pmpaddrx for NA4 and TOR mode (do granularity checks) + * + * @param[in] granularity Memory granularity in byte + * @param[in] address_in Base address of the region + * @param[out] address_out address compatible with NA4 or TOR style (can be + * use into pmpaddrx regs) + * @return int32_t 0 in case of success + * @return int32_t <0 otherwise + */ +int32_t addr_modifier ( size_t granularity, + size_t address_in, + size_t * address_out); + +/** + * @brief compute the pmpaddrx for NAPOT mode (do granularity checks) + * + * @param[in] granularity Memory granularity in byte + * @param[in] address_in Base address of the region + * @param[out] address_out address NAPOT style (can be use into pmpaddrx + * regs) + * @param[in] size Size of the region + * @return int32_t 0 in case of success + * @return int32_t <0 otherwise + */ +int32_t napot_addr_modifier (size_t granularity, + size_t address_in, + size_t * address_out, + size_t size); + \ No newline at end of file diff --git a/freertos/portable/GCC/RISC-V/port.c b/freertos/portable/GCC/RISC-V/port.c new file mode 100644 index 00000000..fe23bc64 --- /dev/null +++ b/freertos/portable/GCC/RISC-V/port.c @@ -0,0 +1,981 @@ +/* + * FreeRTOS Kernel V10.2.1 + * Copyright (C) 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * http://www.FreeRTOS.org + * http://aws.amazon.com/freertos + * + * 1 tab == 4 spaces! + */ + +/* ------------------------------------------------------------------ + * This file is part of the FreeRTOS distribution and was contributed + * to the project by SiFive + * + * Implementation of functions defined in portable.h for the RISC-V + * RV32 port. + * ------------------------------------------------------------------ + */ + + +/* Scheduler includes. */ +#include "FreeRTOS.h" +#include +#include "task.h" +#include "portmacro.h" +#include "string.h" + +PRIVILEGED_DATA StackType_t xISRStackTop; + +/* + * Setup the timer to generate the tick interrupts. The implementation in this + * file is weak to allow application writers to change the timer used to + * generate the tick interrupt. + */ +void vPortSetupTimerInterrupt( void ) __attribute__( ( weak ) ); + +#if( configENABLE_FPU == 1 ) + /* + * Setup the Floating Point Unit (FPU). + */ + void prvSetupFPU( void ) PRIVILEGED_FUNCTION; +#endif /* configENABLE_FPU */ + +/*-----------------------------------------------------------*/ + +/* Used to program the machine timer compare register. */ +PRIVILEGED_DATA uint64_t ullNextTime = 0ULL; +const uint64_t *pullNextTime = &ullNextTime; +const size_t uxTimerIncrementsForOneTick = ( size_t ) ( configCPU_CLOCK_HZ / configTICK_RATE_HZ ); /* Assumes increment won't go over 32-bits. */ +PRIVILEGED_DATA volatile uint64_t * pullMachineTimerCompareRegister; +volatile uint64_t * const pullMachineTimerRegister = ( volatile uint64_t * const ) ( configCLINT_BASE_ADDRESS + 0xBFF8 ); + +#if( portUSING_MPU_WRAPPERS == 1 ) +/** Variable that contains the current privilege state */ +volatile uint32_t privilege_status = ePortMACHINE_MODE; + +/* We require the address of the pxCurrentTCB variable, but don't want to know +any details of its type. */ +typedef void TCB_t; +extern volatile TCB_t * volatile pxCurrentTCB; +#endif + +/* Set configCHECK_FOR_STACK_OVERFLOW to 3 to add ISR stack checking to task +stack checking. A problem in the ISR stack will trigger an assert, not call the +stack overflow hook function (because the stack overflow hook is specific to a +task stack, not the ISR stack). */ +#if( configCHECK_FOR_STACK_OVERFLOW > 2 ) + #warning This path not tested, or even compiled yet. + /* Don't use 0xa5 as the stack fill bytes as that is used by the kernerl for + the task stacks, and so will legitimately appear in many positions within + the ISR stack. */ + #define portISR_STACK_FILL_BYTE 0xee + + static const uint8_t ucExpectedStackBytes[] = { + portISR_STACK_FILL_BYTE, portISR_STACK_FILL_BYTE, portISR_STACK_FILL_BYTE, portISR_STACK_FILL_BYTE, \ + portISR_STACK_FILL_BYTE, portISR_STACK_FILL_BYTE, portISR_STACK_FILL_BYTE, portISR_STACK_FILL_BYTE, \ + portISR_STACK_FILL_BYTE, portISR_STACK_FILL_BYTE, portISR_STACK_FILL_BYTE, portISR_STACK_FILL_BYTE, \ + portISR_STACK_FILL_BYTE, portISR_STACK_FILL_BYTE, portISR_STACK_FILL_BYTE, portISR_STACK_FILL_BYTE, \ + portISR_STACK_FILL_BYTE, portISR_STACK_FILL_BYTE, portISR_STACK_FILL_BYTE, portISR_STACK_FILL_BYTE }; \ + + #define portCHECK_ISR_STACK() configASSERT( ( memcmp( ( void * ) xISRStack, ( void * ) ucExpectedStackBytes, sizeof( ucExpectedStackBytes ) ) == 0 ) ) +#else + /* Define the function away. */ + #define portCHECK_ISR_STACK() +#endif /* configCHECK_FOR_STACK_OVERFLOW > 2 */ + +#if( portUSING_MPU_WRAPPERS == 1 ) +void vResetPrivilege( void ) __attribute__ (( naked )); + +pmp_info_t xPmpInfo = {0,0}; + +BaseType_t xIsPrivileged( void ) +{ + return(privilege_status == ePortMACHINE_MODE); +} + +/** + * @brief Setup of base region (first 3 regions) + * @details those regions won't be reconfigured during context switch + * + */ +static void prvSetupPMP( void ) PRIVILEGED_FUNCTION +{ + extern uint32_t __unprivileged_section_start__[]; + extern uint32_t __unprivileged_section_end__[]; + + uint8_t ucDefaultAttribute; + size_t uxDefaultBaseAddr; + /** + * considered as unused in certain cases because of macro + * configASSERT_DEFINED + */ + int32_t lResult __attribute__((unused)) = PMP_DEFAULT_ERROR; + + if(0 == xPmpInfo.granularity) { + lResult = init_pmp (&xPmpInfo); + #if( configASSERT_DEFINED == 1 ) + { + configASSERT(0 <= lResult); + } + #endif + } + + /* Check the expected PMP is present. */ + if( portMINIMAL_NB_PMP <= xPmpInfo.nb_pmp) + { + /* First setup the start address of the unprivilleged flash */ + ucDefaultAttribute = 0; + uxDefaultBaseAddr = 0; + + lResult = addr_modifier (xPmpInfo.granularity, + ( size_t ) __unprivileged_section_start__, + &uxDefaultBaseAddr); + #if( configASSERT_DEFINED == 1 ) + { + configASSERT(0 <= lResult); + } + #endif + + ucDefaultAttribute = + ((portPMP_REGION_READ_ONLY) | + (portPMP_REGION_EXECUTE) | + (portPMP_REGION_ADDR_MATCH_NA4)); + + lResult = write_pmp_config (&xPmpInfo, portUNPRIVILEGED_EXECUTE_REGION_START, + ucDefaultAttribute, uxDefaultBaseAddr); + #if( configASSERT_DEFINED == 1 ) + { + configASSERT(0 <= lResult); + } + #endif + + /* Setup the end address of the unprivilleged flash */ + ucDefaultAttribute = 0; + uxDefaultBaseAddr = 0; + lResult = addr_modifier (xPmpInfo.granularity, + ( size_t ) __unprivileged_section_end__, + &uxDefaultBaseAddr); + #if( configASSERT_DEFINED == 1 ) + { + configASSERT(0 <= lResult); + } + #endif + + ucDefaultAttribute = ((portPMP_REGION_READ_ONLY) | + (portPMP_REGION_EXECUTE) | + (portPMP_REGION_ADDR_MATCH_TOR)); + + lResult = write_pmp_config (&xPmpInfo, portUNPRIVILEGED_EXECUTE_REGION_END, + ucDefaultAttribute, uxDefaultBaseAddr); + #if( configASSERT_DEFINED == 1 ) + { + configASSERT(0 <= lResult); + } + #endif + + /* Allow read only on the privilege status varibale */ + ucDefaultAttribute = 0; + uxDefaultBaseAddr = 0; + + lResult = addr_modifier (xPmpInfo.granularity, + ( size_t ) &privilege_status, + &uxDefaultBaseAddr); + #if( configASSERT_DEFINED == 1 ) + { + configASSERT(0 <= lResult); + } + #endif + + ucDefaultAttribute = + ((portPMP_REGION_READ_WRITE) | + (portPMP_REGION_ADDR_MATCH_NA4)); + + lResult = write_pmp_config (&xPmpInfo, portPRIVILEGE_STATUS_REGION, + ucDefaultAttribute, uxDefaultBaseAddr); + #if( configASSERT_DEFINED == 1 ) + { + configASSERT(0 <= lResult); + } + #endif + } +} +#endif +/*-----------------------------------------------------------*/ + +/** + * @brief + * + * @param xIsrTop + */ +BaseType_t xPortFreeRTOSInit( StackType_t xIsrTop ) PRIVILEGED_FUNCTION +{ + UBaseType_t uxHartid; + + extern BaseType_t xPortMoveISRStackTop( StackType_t *xISRStackTop); + + /** + * We do the initialization of FreeRTOS priviledged data section + */ + #if( portUSING_MPU_WRAPPERS == 1 ) + { + /** + * Considered by the compiler as unused because of the inline asm block + */ + extern uint32_t __privileged_data_start__[] __attribute__((unused)); + extern uint32_t __privileged_data_end__[] __attribute__((unused)); + __asm__ __volatile__ ( + /* Zero the privileged_data segment. */ + "la t1, __privileged_data_start__ \n" + "la t2, __privileged_data_end__ \n" + + "bge t1, t2, 2f \n" + + "1: \n" + #if __riscv_xlen == 32 + "sw x0, 0(t1) \n" + "addi t1, t1, 4 \n" + "blt t1, t2, 1b \n" + #else + "sd x0, 0(t1) \n" + "addi t1, t1, 8 \n" + "blt t1, t2, 1b \n" + #endif + "2: \n" + :: + : "t1", "t2" + + ); + + } + #endif + + /* + * xIsrStack Is a Buffer Allocated into Application + * xIsrStack_top is the Top adress of this Buffer + * it will contain the isrstack and space or the registeries backup + * + * Top +----------------------+ xIsrTop + * | .... | + * | | + * Bottom +----------------------+ xISRStack + * + * stack mapping after : + * Top +----------------------+ xIsrTop + * | Space to store | + * | context before | + * | FreeRtos scheduling | + * | .... | + * +----------------------+ xISRStackTop + * | stack space for | + * | ISR execution | + * | .... | + * | | + * Bottom +----------------------+ xISRStack + */ + + xISRStackTop = xIsrTop; + + if ( 0 == xPortMoveISRStackTop(&xISRStackTop)){ + /* Error no enough place to store cntext or bad parameter */ + return -1; + } + + #if( configASSERT_DEFINED == 1 ) + { + /* Check alignment of the interrupt stack - which is the same as the + stack that was being used by main() prior to the scheduler being + started. */ + configASSERT( ( xISRStackTop & portBYTE_ALIGNMENT_MASK ) == 0 ); + } + #endif /* configASSERT_DEFINED */ + + __asm__ __volatile__ ("csrr %0, mhartid" : "=r"(uxHartid)); + + pullMachineTimerCompareRegister = ( volatile uint64_t *) ( configCLINT_BASE_ADDRESS + 0x4000 + uxHartid * sizeof(uint64_t) ); + + #if( configCLINT_BASE_ADDRESS != 0 ) + /* There is a clint then interrupts can branch directly to the FreeRTOS + * trap handler. + */ + __asm__ __volatile__ ( + "la t0, freertos_risc_v_trap_handler\n" + "csrw mtvec, t0\n" + ); + #else + #warning "*** The interrupt controller must to be configured before (ouside of this file). ***" + #endif + + #if( portUSING_MPU_WRAPPERS == 1 ) + /* Configure the regions in the PMP that are common to all tasks. */ + prvSetupPMP(); + #endif + + return 0; +} +/*-----------------------------------------------------------*/ + +/** + * @brief Do the PMP config switch when switching task + * @param ulNbPmp number of configurable PMP + * @param xPMPSettings PMP configs stored in task TCB + * @warning the number of configurable PMP is not the total number of PMP + * + */ +#if( portUSING_MPU_WRAPPERS == 1 ) +__attribute__ (( naked )) void vPortPmpSwitch ( uint32_t ulNbPmp, + xMPU_SETTINGS * xPMPSettings) PRIVILEGED_FUNCTION +#else +__attribute__ (( naked )) void vPortPmpSwitch ( uint32_t ulNbPmp) PRIVILEGED_FUNCTION +#endif +{ +#if( portUSING_MPU_WRAPPERS == 1 ) + /** + * a0: xPmpInfo.nb_pmp - 3 (3 because We use 3 pmp config by default) + * a1: pxCurrentTCB->xPMPSettings (supposed to be the 2nd element of structure TCB_t) + */ + #if( __riscv_xlen == 32 ) + __asm__ __volatile__ ( + "addi a0, %0, -3 \n\t" + "addi a1, %1, 4 \n\t" + :: "r"(xPmpInfo.nb_pmp), "r"(pxCurrentTCB) : "a0", "a1" + ); + #endif /* ( __riscv_xlen == 32 ) */ + #if( __riscv_xlen == 64 ) + __asm__ __volatile__ ( + "addi a0, %0, -3 \n\t" + "addi a1, %1, 8 \n\t" + :: "r"(xPmpInfo.nb_pmp), "r"(pxCurrentTCB) : "a0", "a1" + ); + #endif /* ( __riscv_xlen == 64 ) */ + + /* Compute jump offset to avoid configure unuse PMP */ + __asm__ __volatile__ ( + "li t0, 13 \n" /* maximum number of reconfigurable PMP for a core */ + "sub a2, t0, a0 \n" + "slli a2, a2, 3 \n" /* compute the jump offset */ + ::: "a0", "a2", "t0" + ); + + /* clear pmp config before setting addr */ +#if __riscv_xlen == 32 + __asm__ __volatile__ ( + "li t1, 0x18181818 \n" + /** + * we avoid disabling permanent PMP config, therefore those region mask + * are set to 0 + */ + "li t2, 0x18000000 \n" + "csrc pmpcfg0, t2 \n" + "csrc pmpcfg1, t1 \n" + "csrc pmpcfg2, t1 \n" + "csrc pmpcfg3, t1 \n" + :::"t1", "t2" + ); +#elif __riscv_xlen == 64 + __asm__ __volatile__ ( + "li t1, 0x1818181818181818 \n" + /** + * we avoid disabling permanent PMP config, therefore those region mask + * are set to 0 + */ + "li t2, 0x1818181818000000 \n" + "csrc pmpcfg0, t2 \n" + "csrc pmpcfg2, t1 \n" + :::"t1", "t2" + ); +#endif + + /** + * Save pmp address in pmpaddrx registers + * Please note that pmpaddr0, pmpaddr1, pmpaddr2 are not reconfigured, + * they are configured once at the initialization of the scheduler + */ +#if __riscv_xlen == 32 + __asm__ __volatile__ ( + "add t0, a1, 32 \n" /* get pmp address configs */ + "la t1, 1f \n" /* compute the jump address */ + "add t2, t1, a2 \n" + "jr t2 \n" + "1: \n" + "lw t2, 48(t0) \n" + "csrw pmpaddr15, t2 \n" + "lw t1, 44(t0) \n" + "csrw pmpaddr14, t1 \n" + "lw t2, 40(t0) \n" + "csrw pmpaddr13, t2 \n" + "lw t1, 36(t0) \n" + "csrw pmpaddr12, t1 \n" + "lw t2, 32(t0) \n" + "csrw pmpaddr11, t2 \n" + "lw t1, 28(t0) \n" + "csrw pmpaddr10, t1 \n" + "lw t2, 24(t0) \n" + "csrw pmpaddr9, t2 \n" + "lw t1, 20(t0) \n" + "csrw pmpaddr8, t1 \n" + "lw t2, 16(t0) \n" + "csrw pmpaddr7, t2 \n" + "lw t1, 12(t0) \n" + "csrw pmpaddr6, t1 \n" + "lw t2, 8(t0) \n" + "csrw pmpaddr5, t2 \n" + "lw t1, 4(t0) \n" + "csrw pmpaddr4, t1 \n" + "lw t2, 0(t0) \n" + "csrw pmpaddr3, t2 \n" + ::: "t0", "t1", "t2" + ); +#elif __riscv_xlen == 64 + __asm__ __volatile__ ( + "add t0, a1, 32 \n" /* get pmp address configs */ + "la t1, 1f \n" /* compute the jump address */ + "add t2, t1, a2 \n" + "jr t2 \n" + "1: \n" + "ld t2, 96(t0) \n" + "csrw pmpaddr15, t2 \n" + "ld t1, 88(t0) \n" + "csrw pmpaddr14, t1 \n" + "ld t2, 80(t0) \n" + "csrw pmpaddr13, t2 \n" + "ld t1, 72(t0) \n" + "csrw pmpaddr12, t1 \n" + "ld t2, 64(t0) \n" + "csrw pmpaddr11, t2 \n" + "ld t1, 56(t0) \n" + "csrw pmpaddr10, t1 \n" + "ld t2, 48(t0) \n" + "csrw pmpaddr9, t2 \n" + "ld t1, 40(t0) \n" + "csrw pmpaddr8, t1 \n" + "ld t2, 32(t0) \n" + "csrw pmpaddr7, t2 \n" + "ld t1, 24(t0) \n" + "csrw pmpaddr6, t1 \n" + "ld t2, 16(t0) \n" + "csrw pmpaddr5, t2 \n" + "ld t1, 8(t0) \n" + "csrw pmpaddr4, t1 \n" + "ld t2, 0(t0) \n" + "csrw pmpaddr3, t2 \n" + ::: "t0", "t1", "t2" + ); +#endif + + +#if __riscv_xlen == 32 + /* Compute jump offset to avoid configure unuse PMP 32bits version */ + __asm__ __volatile__ ( + "addi a0, a0, 2 \n" + "srli t1, a0, 2 \n" /* divide by 4 */ + "li t2, 3 \n" /* number of config regs (4) */ + "sub t2, t2, t1 \n" + "slli t2, t2, 4 \n" + ::: "a0", "t1", "t2" + ); +#elif __riscv_xlen == 64 + /* Compute jump offset to avoid configure unuse PMP 64bits version */ + __asm__ __volatile__ ( + "addi a0, a0, 2 \n" + "srli t1, a0, 3 \n" /* divide by 8 */ + "li t2, 1 \n" /* number of config regs (2) */ + "sub t2, t2, t1 \n" + "slli t2, t2, 4 \n" + ::: "a0", "t1", "t2" + ); +#endif + + /* Configure PMP mode (rights and mode) */ +#if __riscv_xlen == 32 + __asm__ __volatile__ ( + "add a0, a1, 16 \n" /* get pmp config mask */ + + "la t0, 1f \n" + "add t0, t0, t2 \n" + "jr t0 \n" + "1: \n" + + "lw t1, 12(a0) \n" + "lw t2, 12(a1)\n" + "csrc pmpcfg3, t1 \n" + "csrs pmpcfg3, t2 \n" + + "lw t1, 8(a0) \n" + "lw t2, 8(a1)\n" + "csrc pmpcfg2, t1 \n" + "csrs pmpcfg2, t2 \n" + + "lw t1, 4(a0) \n" + "lw t2, 4(a1)\n" + "csrc pmpcfg1, t1 \n" + "csrs pmpcfg1, t2 \n" + + "lw t1, 0(a0) \n" + "lw t2, 0(a1)\n" + "csrc pmpcfg0, t1 \n" + "csrs pmpcfg0, t2 \n" + ::: "t0", "t1", "t2", "a0", "a1" + ); +#elif __riscv_xlen == 64 + __asm__ __volatile__ ( + "add a0, a1, 16 \n" /* get pmp config mask */ + + "la t0, 1f \n" + "add t0, t0, t2 \n" + "jr t0 \n" + "1: \n" + + "ld t1, 8(a0) \n" + "ld t2, 8(a1)\n" + "csrc pmpcfg2, t1 \n" + "csrs pmpcfg2, t2 \n" + + "ld t1, 0(a0) \n" + "ld t2, 0(a1)\n" + "csrc pmpcfg0, t1 \n" + "csrs pmpcfg0, t2 \n" + ::: "t0", "t1", "t2", "a0", "a1" + ); +#endif + __asm__ __volatile__ ( + "fence.i \n" + ::: + ); +#endif /* ( portUSING_MPU_WRAPPERS == 1 ) */ + + __asm__ __volatile__ ( + "ret \n" + ::: + ); +} + +/*-----------------------------------------------------------*/ + +#if( configCLINT_BASE_ADDRESS != 0 ) + + void vPortSetupTimerInterrupt( void ) + { + #if( __riscv_xlen == 32 ) + uint32_t ulCurrentTimeHigh, ulCurrentTimeLow; + volatile uint32_t * const pulTimeHigh = ( volatile uint32_t * const ) ( configCLINT_BASE_ADDRESS + 0xBFFC ); + volatile uint32_t * const pulTimeLow = ( volatile uint32_t * const ) ( configCLINT_BASE_ADDRESS + 0xBFF8 ); + #endif /* __riscv_xlen == 32 */ + #if( __riscv_xlen == 64 ) + volatile uint64_t * const pulTime = ( volatile uint64_t * const ) ( configCLINT_BASE_ADDRESS + 0xBFF8 ); + #endif /* __riscv_xlen == 64 */ + + #if( __riscv_xlen == 32 ) + do + { + ulCurrentTimeHigh = *pulTimeHigh; + ulCurrentTimeLow = *pulTimeLow; + } while( ulCurrentTimeHigh != *pulTimeHigh ); + + ullNextTime = ( uint64_t ) ulCurrentTimeHigh; + ullNextTime <<= 32ULL; + ullNextTime |= ( uint64_t ) ulCurrentTimeLow; + #endif /* __riscv_xlen == 32 */ + #if( __riscv_xlen == 64 ) + ullNextTime = *pulTime; + #endif /* __riscv_xlen == 64 */ + + ullNextTime += ( uint64_t ) uxTimerIncrementsForOneTick; + *pullMachineTimerCompareRegister = ullNextTime; + + /* Prepare the time to use after the next tick interrupt. */ + ullNextTime += ( uint64_t ) uxTimerIncrementsForOneTick; + } + +#endif /* ( configCLINT_BASE_ADDRESS != 0 ) */ +/*-----------------------------------------------------------*/ + +/** + * @brief Start scheduler + * + * @return BaseType_t error code (pdFAIL or pdPASS) + */ +BaseType_t xPortStartScheduler( void ) PRIVILEGED_FUNCTION +{ + extern void xPortStartFirstTask( void ); + + #if( configASSERT_DEFINED == 1 ) + { + volatile uint32_t mtvec = 0; + + /* Check the least significant two bits of mtvec are 00 - indicating + single vector mode. */ + __asm__ __volatile__ ( + " csrr %0, mtvec \n" + : "=r"( mtvec ) + ); + + configASSERT( ( mtvec & 0x03UL ) == 0 ); + } + #endif /* configASSERT_DEFINED */ + + /* If there is a CLINT then it is ok to use the default implementation + in this file, otherwise vPortSetupTimerInterrupt() must be implemented to + configure whichever clock is to be used to generate the tick interrupt. */ + vPortSetupTimerInterrupt(); + + xPortStartFirstTask(); + + /* Should not get here as after calling xPortStartFirstTask() only tasks + should be executing. */ + return pdFAIL; +} +/*-----------------------------------------------------------*/ + +void vPortEndScheduler( void ) +{ + extern void xPortRestoreBeforeFirstTask(void); + + xPortRestoreBeforeFirstTask(); + + /* + * Should not get here as after calling xPortRestoreBeforeFirstTask() we should + * return after de execution of xPortStartFirstTask in xPortStartScheduler function. + */ + for( ;; ); +} +/*-----------------------------------------------------------*/ + +#if( portUSING_MPU_WRAPPERS == 1 ) +__attribute__((naked)) void vPortSyscall( unsigned int Value ) PRIVILEGED_FUNCTION +{ + /* Remove compiler warning about unused parameter. */ + ( void ) Value; + + __asm__ __volatile__ ( + " ecall \n" + " ret \n" + ::: + ); +} +/*-----------------------------------------------------------*/ + +__attribute__((naked)) void vRaisePrivilege( void ) PRIVILEGED_FUNCTION +{ + __asm__ __volatile__ ( + " .extern privilege_status \n" + " li a0,%0 \n" + " ecall \n" + " la a0, privilege_status \n" + " li t0, %1 \n" + " sw t0, 0(a0) \n" /* we use sw because privilege_status is uint32_t */ + " ret \n" + ::"i"(portSVC_SWITCH_TO_MACHINE), "i"(ePortMACHINE_MODE): + ); +} +/*-----------------------------------------------------------*/ + +__attribute__((naked)) void vResetPrivilege( void ) PRIVILEGED_FUNCTION +{ + __asm__ __volatile__ ( + " .extern privilege_status \n" + " li a0,%0 \n" + " ecall \n" + " la a0, privilege_status \n" + " li t0, %1 \n" + " sw t0, 0(a0) \n" /* we use sw because privilege_status is uint32_t */ + " ret \n" + ::"i"(portSVC_SWITCH_TO_USER), "i"(ePortUSER_MODE): + ); +} +/*-----------------------------------------------------------*/ + +/** + * @brief Store PMP settings in Task TCB - the name this function + * is vPortStoreTaskMPUSettings in order to be MPU compliant + * + * @param[out] xPMPSettings PMP settings stored in Task TCB + * @param[in] xRegions PMP configuration of the task + * @param[in] pxBottomOfStack address of bottom of stack + * @param[in] ulStackDepth size of stack + */ +void vPortStoreTaskMPUSettings( xMPU_SETTINGS *xPMPSettings, + const struct xMEMORY_REGION * const xRegions, + StackType_t *pxBottomOfStack, + uint32_t ulStackDepth ) PRIVILEGED_FUNCTION +{ + int32_t lIndex; + uint32_t ul; + + /** + * considered as unused in certain cases because of macro + * configASSERT_DEFINED + */ + int32_t lResult __attribute__((unused)) = PMP_DEFAULT_ERROR; + size_t uxBaseAddressChecked = 0; + + if(0 == xPmpInfo.granularity) { + lResult = init_pmp (&xPmpInfo); + #if( configASSERT_DEFINED == 1 ) + { + configASSERT(0 <= lResult); + } + #endif + } + + memset(xPMPSettings, 0, sizeof(xMPU_SETTINGS)); + + if( xRegions == NULL ) { + /* No PMP regions are specified so allow access to all data section */ + + /* Config stack start address */ + uxBaseAddressChecked = 0; + lResult = addr_modifier (xPmpInfo.granularity, + (size_t) pxBottomOfStack, + &uxBaseAddressChecked); + #if( configASSERT_DEFINED == 1 ) + { + configASSERT(0 <= lResult); + } + #endif + + xPMPSettings->uxRegionBaseAddress[0] = uxBaseAddressChecked; + + xPMPSettings->uxPmpConfigRegAttribute[portGET_PMPCFG_IDX(portSTACK_REGION_START)] += + ((UBaseType_t)((portPMP_REGION_READ_WRITE) | + (portPMP_REGION_ADDR_MATCH_NA4)) << + portPMPCFG_BIT_SHIFT(portSTACK_REGION_START)); + + xPMPSettings->uxPmpConfigRegMask[portGET_PMPCFG_IDX(portSTACK_REGION_START)] += + ((UBaseType_t)0xFF << portPMPCFG_BIT_SHIFT(portSTACK_REGION_START)); + + /* Config stack end address and TOR */ + uxBaseAddressChecked = 0; + lResult = addr_modifier (xPmpInfo.granularity, + (size_t) pxBottomOfStack + ( size_t ) ulStackDepth * sizeof( StackType_t ), + &uxBaseAddressChecked); + #if( configASSERT_DEFINED == 1 ) + { + configASSERT(0 <= lResult); + } + #endif + + xPMPSettings->uxRegionBaseAddress[1] = uxBaseAddressChecked; + + xPMPSettings->uxPmpConfigRegAttribute[portGET_PMPCFG_IDX(portSTACK_REGION_END)] += + ((UBaseType_t)((portPMP_REGION_READ_WRITE) | + (portPMP_REGION_ADDR_MATCH_TOR)) << + portPMPCFG_BIT_SHIFT(portSTACK_REGION_END)); + + xPMPSettings->uxPmpConfigRegMask[portGET_PMPCFG_IDX(portSTACK_REGION_END)] += + ((UBaseType_t)0xFF << portPMPCFG_BIT_SHIFT(portSTACK_REGION_END)); + + /* Invalidate all other configurable regions. */ + for( ul = 2; ul < portNUM_CONFIGURABLE_REGIONS_REAL (xPmpInfo.nb_pmp) + 2; ul++ ) + { + xPMPSettings->uxRegionBaseAddress[ul] = 0UL; + xPMPSettings->uxPmpConfigRegMask[portGET_PMPCFG_IDX(portSTACK_REGION_START + ul)] += + ((UBaseType_t)0xFF << portPMPCFG_BIT_SHIFT(portSTACK_REGION_START + ul)); + } + } + else + { + /* This function is called automatically when the task is created - in + which case the stack region parameters will be valid. At all other + times the stack parameters will not be valid and it is assumed that the + stack region has already been configured. */ + if( ulStackDepth > 0 ) + { + /* Config stack start address */ + uxBaseAddressChecked = 0; + lResult = addr_modifier (xPmpInfo.granularity, + (size_t) pxBottomOfStack, + &uxBaseAddressChecked); + #if( configASSERT_DEFINED == 1 ) + { + configASSERT(0 <= lResult); + } + #endif + + xPMPSettings->uxRegionBaseAddress[0] = uxBaseAddressChecked; + + xPMPSettings->uxPmpConfigRegAttribute[portGET_PMPCFG_IDX(portSTACK_REGION_START)] += + ((UBaseType_t)((portPMP_REGION_READ_WRITE) | + (portPMP_REGION_ADDR_MATCH_NA4)) << + portPMPCFG_BIT_SHIFT(portSTACK_REGION_START)); + + xPMPSettings->uxPmpConfigRegMask[portGET_PMPCFG_IDX(portSTACK_REGION_START)] += + ((UBaseType_t)0xFF << portPMPCFG_BIT_SHIFT(portSTACK_REGION_START)); + + /* Config stack end address and TOR */ + uxBaseAddressChecked = 0; + lResult = addr_modifier (xPmpInfo.granularity, + (size_t) pxBottomOfStack + ( size_t ) ulStackDepth * sizeof( StackType_t ), + &uxBaseAddressChecked); + #if( configASSERT_DEFINED == 1 ) + { + configASSERT(0 <= lResult); + } + #endif + + xPMPSettings->uxRegionBaseAddress[1] = uxBaseAddressChecked; + + xPMPSettings->uxPmpConfigRegAttribute[portGET_PMPCFG_IDX(portSTACK_REGION_END)] += + ((UBaseType_t)((portPMP_REGION_READ_WRITE) | + (portPMP_REGION_ADDR_MATCH_TOR)) << + portPMPCFG_BIT_SHIFT(portSTACK_REGION_END)); + + xPMPSettings->uxPmpConfigRegMask[portGET_PMPCFG_IDX(portSTACK_REGION_END)] += + ((UBaseType_t)0xFF << portPMPCFG_BIT_SHIFT(portSTACK_REGION_END)); + } + + lIndex = 0; + + for( ul = 2; ul < (portNUM_CONFIGURABLE_REGIONS_REAL (xPmpInfo.nb_pmp) + 2); ul++ ) + { + if( ( xRegions[ lIndex ] ).ulLengthInBytes > 0UL ) + { + xPMPSettings->uxRegionBaseAddress[ul] = (size_t) xRegions[ lIndex ].pvBaseAddress; + + xPMPSettings->uxPmpConfigRegAttribute[portGET_PMPCFG_IDX(portSTACK_REGION_START + ul)] += + ((UBaseType_t)( xRegions[ lIndex ].ulParameters ) << + portPMPCFG_BIT_SHIFT(portSTACK_REGION_START + ul)); + + xPMPSettings->uxPmpConfigRegMask[portGET_PMPCFG_IDX(portSTACK_REGION_START + ul)] += + ((UBaseType_t)0xFF << portPMPCFG_BIT_SHIFT(portSTACK_REGION_START + ul)); + } + else + { + /* Invalidate the region. */ + xPMPSettings->uxRegionBaseAddress[ul] = 0UL; + xPMPSettings->uxPmpConfigRegMask[portGET_PMPCFG_IDX(portSTACK_REGION_START + ul)] += + ((UBaseType_t)0xFF << portPMPCFG_BIT_SHIFT(portSTACK_REGION_START + ul)); + } + + lIndex++; + } + + #if( configASSERT_DEFINED == 1 ) + { + // check we do not want to configure unavailable regions + if(xPmpInfo.nb_pmp < MAX_PMP_REGION) { + configASSERT(xRegions[ lIndex ].ulLengthInBytes == 0UL); + } + } + #endif + } +} +/*-----------------------------------------------------------*/ +#endif + +__attribute__((naked)) void vPortUpdatePrivilegeStatus( UBaseType_t status ) PRIVILEGED_FUNCTION +{ + /* Remove compiler warning about unused parameter. */ + ( void ) status; + + #if ( portUSING_MPU_WRAPPERS == 1 ) + __asm__ __volatile__( + " .extern privilege_status \n" + " li t0, 0x1800 \n" + " and t0, a0, t0 \n" + " srli t0, t0, 11 \n" + " la a0, privilege_status \n" + " sw t0, 0(a0)" + ::: "t0", "a0" + ); + #endif /* if ( portUSING_MPU_WRAPPERS == 1 ) */ + + __asm__ __volatile__ ( + " ret \n" + ::: + ); +} +/*-----------------------------------------------------------*/ + +#if ( portUSING_MPU_WRAPPERS == 1 ) +StackType_t * pxPortInitialiseStack( StackType_t * pxTopOfStack, + TaskFunction_t pxCode, + void * pvParameters, + BaseType_t xRunPrivileged ) PRIVILEGED_FUNCTION +#else /* if ( portUSING_MPU_WRAPPERS == 1 ) */ +StackType_t * pxPortInitialiseStack( StackType_t * pxTopOfStack, + TaskFunction_t pxCode, + void * pvParameters ) PRIVILEGED_FUNCTION +#endif /* if ( portUSING_MPU_WRAPPERS == 1 ) */ +{ + UBaseType_t mstatus; + extern StackType_t * pxPortAsmInitialiseStack( StackType_t *, TaskFunction_t, void *, UBaseType_t); + + /* Generate the value to set in mstatus. */ + #if( portUSING_MPU_WRAPPERS == 1 ) + /** + * Generate the value: + * - 0x80 (MPIE set and MPP = usermode) if run in unprivilege mode (xRunPrivileged == 0). + * - 0x1880 (MPIE set and MPP = machinemode) if run in privilege mode (xRunPrivileged != 0). + */ + __asm__ __volatile__ ( + " csrr t0, mstatus \n" /* Obtain current mstatus value. */ + " andi t0, t0, ~0x8 \n" /* Ensure interrupts are disabled when the stack is restored within an ISR. Required when a task is created after the schedulre has been started, otherwise interrupts would be disabled anyway. */ + " addi t1, x0, 0x188 \n" /* Generate the value 0x1880, which are the MPIE and MPP bits to set in mstatus. */ + " slli t1, t1, 4 \n" + " not t2, t1 \n" /* reset previous value */ + " and t0, t0, t2 \n" + " mv t1, x0 \n" + " beqz a3, 1f \n" + " addi t1, t1, 0x180 \n" + " 1: \n" + " ori t1, t1, 0x8 \n" + " slli t1, t1, 4 \n" + " or %0, t0, t1 \n" + :"=r" ( mstatus )::"t0", "t1", "t2", "a3" + ); + #else + __asm__ __volatile__ ( + " csrr t0, mstatus \n" /* Obtain current mstatus value. */ + " andi t0, t0, ~0x8 \n" /* Ensure interrupts are disabled when the stack is restored within an ISR. Required when a task is created after the schedulre has been started, otherwise interrupts would be disabled anyway. */ + " addi t1, x0, 0x188 \n" /* Generate the value 0x1880, which are the MPIE and MPP bits to set in mstatus. */ + " slli t1, t1, 4 \n" + " not t2, t1 \n" /* reset previous value */ + " and t0, t0, t2 \n" + " addi t1, x0, 0x188 \n" + " slli t1, t1, 4 \n" + " or %0, t0, t1 \n" + :"=r" ( mstatus )::"t0", "t1", "t2" + ); + #endif /* ( portUSING_MPU_WRAPPERS == 1 ) */ + + return pxPortAsmInitialiseStack(pxTopOfStack, pxCode, pvParameters, mstatus); +} +/*-----------------------------------------------------------*/ + +#if( configENABLE_FPU == 1 ) + void prvSetupFPU( void ) /* PRIVILEGED_FUNCTION */ + { + __asm__ __volatile__ ( + "csrr t0, misa \n" /* Get misa */ + "li t1, 0x10028 \n" /* 0x10028 = Q,F,D Quad, Single or Double precission floating point */ + "and t0, t0, t1 \n" + "beqz t0, 1f \n" /* check if Q,F or D is present into misa */ + "csrr t0, mstatus \n" /* Floating point unit is present so need to put it into initial state */ + "lui t1, 0x2 \n" /* t1 = 0x1 << 12 */ + "or t0, t0, t1 \n" + "csrw mstatus, t0 \n" /* Set FS to initial state */ + "csrwi fcsr, 0 \n" /* Clear Floating-point Control and Status Register */ + "1: \n" + ::: + ); + } +#endif /* configENABLE_FPU */ +/*-----------------------------------------------------------*/ \ No newline at end of file diff --git a/freertos/portable/GCC/RISC-V/portASM.S b/freertos/portable/GCC/RISC-V/portASM.S new file mode 100644 index 00000000..ad19d045 --- /dev/null +++ b/freertos/portable/GCC/RISC-V/portASM.S @@ -0,0 +1,897 @@ +/* + * FreeRTOS Kernel V10.2.1 + * Copyright (C) 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * http://www.FreeRTOS.org + * http://aws.amazon.com/freertos + * + * 1 tab == 4 spaces! + */ + +/* + * The FreeRTOS kernel's RISC-V port is split between the the code that is + * common across all currently supported RISC-V chips (implementations of the + * RISC-V ISA), and code which tailors the port to a specific RISC-V chip: + * + * + The code that is common to all RISC-V chips is implemented in + * FreeRTOS\Source\portable\GCC\RISC-V-RV32\portASM.S. There is only one + * portASM.S file because the same file is used no matter which RISC-V chip is + * in use. + * + * + The code that tailors the kernel's RISC-V port to a specific RISC-V + * chip is implemented in freertos_risc_v_chip_specific_extensions.h. There + * is one freertos_risc_v_chip_specific_extensions.h that can be used with any + * RISC-V chip that both includes a standard CLINT and does not add to the + * base set of RISC-V registers. There are additional + * freertos_risc_v_chip_specific_extensions.h files for RISC-V implementations + * that do not include a standard CLINT or do add to the base set of RISC-V + * registers. + * + * CARE MUST BE TAKEN TO INCLUDE THE CORRECT + * freertos_risc_v_chip_specific_extensions.h HEADER FILE FOR THE CHIP + * IN USE. To include the correct freertos_risc_v_chip_specific_extensions.h + * header file ensure the path to the correct header file is in the assembler's + * include path. + * + * This freertos_risc_v_chip_specific_extensions.h is for use on RISC-V chips + * that include a standard CLINT and do not add to the base set of RISC-V + * registers. + * + */ + +/* ------------------------------------------------------------------ + * This file is part of the FreeRTOS distribution and was contributed + * to the project by SiFive + * ------------------------------------------------------------------ + */ + +#ifndef __riscv_xlen +#error Missing __riscv_xlen +#endif /* __riscv_xlen */ + +#if __riscv_xlen == 32 + #define store_x sw + #define load_x lw +#elif __riscv_xlen == 64 + #define store_x sd + #define load_x ld +#else + #error __riscv_xlen with an unsupported value +#endif /* __riscv_xlen == ?? */ + +#include "FreeRTOSConfig.h" + +#include "freertos_risc_v_chip_specific_extensions.h" + +/* integer registers */ +#ifdef __riscv_32e +#define portasmNB_REGS_SAVED (16) +#else +#define portasmNB_REGS_SAVED (32) +#endif /* __riscv_32e */ +#define portWORD_SIZE (__riscv_xlen / 8) + +#define PORT_CONTEXT_mepcIDX (portasmNB_REGS_SAVED) +#define PORT_CONTEXT_mstatusIDX (portasmNB_REGS_SAVED + 1) + +#define portasmLAST_BASE_REGS (portasmNB_REGS_SAVED + 2) + +/* Number of FPU register */ +#ifdef __riscv_fdiv + #define MSTATUS_FS 0x00006000 /* Floating-point Status */ + #define MSTATUS_FS_OFF 0x00000000 + #define MSTATUS_FS_INITIAL 0x00002000 + #define MSTATUS_FS_CLEAN 0x00004000 + #define MSTATUS_FS_DIRTY 0x00006000 + + #if __riscv_flen == 32 + #define store_fpu fsw + #define load_fpu flw + #endif /* __riscv_flen == 32 */ + + #if __riscv_flen == 64 + #define store_fpu fsd + #define load_fpu fld + #endif /* __riscv_flen == 64 */ + + #define portasmFPU_CONTEXT_SIZE (32) + #define portFPUWORD_SIZE (__riscv_flen / 8) +#else + #define portasmFPU_CONTEXT_SIZE (0) + #define portFPUWORD_SIZE (0) +#endif /* __riscv_fdiv */ + +/* used in assembler, as byte offsets from the start of the context */ +#define PORT_CONTEXT_xIDX(X) (X) /* index into "raw" for register x? */ +#define PORT_CONTEXT_xOFFSET(X) (PORT_CONTEXT_xIDX(X) * portWORD_SIZE) +#define PORT_CONTEXT_mepcOFFSET (PORT_CONTEXT_mepcIDX * portWORD_SIZE) +#define PORT_CONTEXT_mstatusOFFSET (PORT_CONTEXT_mstatusIDX * portWORD_SIZE) +#define PORT_CONTEXT_arsOFFSET (PORT_CONTEXT_arsIDX * portWORD_SIZE) +#define PORT_CONTEXT_fpursOFFSET (PORT_CONTEXT_fpursIDX * portWORD_SIZE) + +#define PORT_CONTEXT_fpuOFFSET(X) ((X) * portFPUWORD_SIZE) +/* total size of the structure usable in ASM. */ + +#define portasmREGISTER_CONTEXT_WORDSIZE ((portasmLAST_BASE_REGS) * (portWORD_SIZE)) +#define portasmADDITIONAL_CONTEXT_WORDSIZE ((portasmADDITIONAL_CONTEXT_SIZE) * (portWORD_SIZE)) +#define portasmFPU_CONTEXT_WORDSIZE ((portasmFPU_CONTEXT_SIZE) * (portFPUWORD_SIZE)) + +/* Check the freertos_risc_v_chip_specific_extensions.h and/or command line +definitions. */ +#ifndef portasmHAS_CLINT + #error freertos_risc_v_chip_specific_extensions.h must define portasmHAS_CLINT to either 1 (CLINT present) or 0 (clint not present). +#endif + +#ifndef portHANDLE_INTERRUPT + #error portHANDLE_INTERRUPT must be defined to the function to be called to handle external/peripheral interrupts. +#endif + +#ifndef portHANDLE_EXCEPTION + #error portHANDLE_EXCEPTION must be defined to the function to be called to handle execption. +#endif + +.global xPortStartFirstTask +.global freertos_risc_v_trap_handler +.global xPortMoveISRStackTop +.global xPortRestoreBeforeFirstTask +.global pxPortAsmInitialiseStack + +.extern pxCurrentTCB +.extern vTaskSwitchContext +.extern xTaskIncrementTick +.extern vPortPmpSwitch +.extern vPortUpdatePrivilegeStatus +.extern pullMachineTimerCompareRegister +.extern pullNextTime +.extern uxTimerIncrementsForOneTick /* size_t type so 32-bit on 32-bit core and 64-bits on 64-bit core. */ +.extern xISRStackTop +.extern portHANDLE_INTERRUPT +.extern portHANDLE_EXCEPTION +/*------------------------------------------------------------------*/ + +/** + * Register ABI Name Description Saver + * x0 zero Hard-wired zero - + * x1 ra Return address Caller + * x2 sp Stack pointer Callee + * x3 gp Global pointer - + * x4 tp Thread pointer - + * x5-7 t0-2 Temporaries Caller + * x8 s0/fp Saved register/Frame pointer Callee + * x9 s1 Saved register Callee + * x10-11 a0-1 Function arguments/return values Caller + * x12-17 a2-7 Function arguments Caller + * x18-27 s2-11 Saved registers Callee + * x28-31 t3-6 Temporaries Caller + */ + +/*-----------------------------------------------------------------*/ +.macro portSAVE_FpuReg + /* get FS field from mstatus */ + li t0, MSTATUS_FS + csrr t1, mstatus + and t0, t1, t0 + li t2, MSTATUS_FS_DIRTY + bne t2, t0, 1f + /* FS == dirty */ + /* Make room for the additional FPU registers. */ + addi sp, sp, -portasmFPU_CONTEXT_WORDSIZE + store_fpu f0, PORT_CONTEXT_fpuOFFSET(0)(sp) /* f0(ft0) FP temporary register */ + store_fpu f1, PORT_CONTEXT_fpuOFFSET(1)(sp) /* f1(ft1) FP temporary register */ + store_fpu f2, PORT_CONTEXT_fpuOFFSET(2)(sp) /* f2(ft2) FP temporary register */ + store_fpu f3, PORT_CONTEXT_fpuOFFSET(3)(sp) /* f3(ft3) FP temporary register */ + store_fpu f4, PORT_CONTEXT_fpuOFFSET(4)(sp) /* f4(ft4) FP temporary register */ + store_fpu f5, PORT_CONTEXT_fpuOFFSET(5)(sp) /* f5(ft5) FP temporary register */ + store_fpu f6, PORT_CONTEXT_fpuOFFSET(6)(sp) /* f6(ft6) FP temporary register */ + store_fpu f7, PORT_CONTEXT_fpuOFFSET(7)(sp) /* f7(ft7) FP temporary register */ + + store_fpu f8, PORT_CONTEXT_fpuOFFSET(8)(sp) /* f8(fs0) FP Saved register */ + store_fpu f9, PORT_CONTEXT_fpuOFFSET(9)(sp) /* f9(fs0) FP Saved register */ + + store_fpu f10, PORT_CONTEXT_fpuOFFSET(10)(sp) /* f10(fa0) FP arguments/return values register */ + store_fpu f11, PORT_CONTEXT_fpuOFFSET(11)(sp) /* f11(fa1) FP arguments/return values register */ + + store_fpu f12, PORT_CONTEXT_fpuOFFSET(12)(sp) /* f12(fa2) FP arguments register */ + store_fpu f13, PORT_CONTEXT_fpuOFFSET(13)(sp) /* f13(fa3) FP arguments register */ + store_fpu f14, PORT_CONTEXT_fpuOFFSET(14)(sp) /* f14(fa4) FP arguments register */ + store_fpu f15, PORT_CONTEXT_fpuOFFSET(15)(sp) /* f15(fa5) FP arguments register */ + store_fpu f16, PORT_CONTEXT_fpuOFFSET(16)(sp) /* f16(fa6) FP arguments register */ + store_fpu f17, PORT_CONTEXT_fpuOFFSET(17)(sp) /* f17(fa7) FP arguments register */ + + store_fpu f18, PORT_CONTEXT_fpuOFFSET(18)(sp) /* f18(fs2) FP Saved register */ + store_fpu f19, PORT_CONTEXT_fpuOFFSET(19)(sp) /* f19(fs3) FP Saved register */ + store_fpu f20, PORT_CONTEXT_fpuOFFSET(20)(sp) /* f20(fs4) FP Saved register */ + store_fpu f21, PORT_CONTEXT_fpuOFFSET(21)(sp) /* f21(fs5) FP Saved register */ + store_fpu f22, PORT_CONTEXT_fpuOFFSET(22)(sp) /* f22(fs6) FP Saved register */ + store_fpu f23, PORT_CONTEXT_fpuOFFSET(23)(sp) /* f23(fs7) FP Saved register */ + store_fpu f24, PORT_CONTEXT_fpuOFFSET(24)(sp) /* f24(fs8) FP Saved register */ + store_fpu f25, PORT_CONTEXT_fpuOFFSET(25)(sp) /* f25(fs9) FP Saved register */ + store_fpu f26, PORT_CONTEXT_fpuOFFSET(26)(sp) /* f26(fs10) FP Saved register */ + store_fpu f27, PORT_CONTEXT_fpuOFFSET(27)(sp) /* f27(fs11) FP Saved register */ + + store_fpu f28, PORT_CONTEXT_fpuOFFSET(28)(sp) /* f28(ft8) FP temporary register */ + store_fpu f29, PORT_CONTEXT_fpuOFFSET(29)(sp) /* f29(ft9) FP temporary register */ + store_fpu f30, PORT_CONTEXT_fpuOFFSET(30)(sp) /* f30(ft10) FP temporary register */ + store_fpu f31, PORT_CONTEXT_fpuOFFSET(31)(sp) /* f31(ft11) FP temporary register */ + + /* must set FS to clean */ + csrc mstatus, t0 + li t1, MSTATUS_FS_CLEAN + csrs mstatus, t1 +1: + .endm +/*************************************************************/ + +.macro portRESTORE_FpuReg + /* Assume that a0 contain the @ of saved core register */ + load_x t1, PORT_CONTEXT_mstatusOFFSET(a0) + /* get FS field from mstatus */ + li t0, MSTATUS_FS + and t0, t1, t0 + li t2, MSTATUS_FS_OFF + beq t2, t0, 1f + /* FS != off */ + csrs mstatus, t0 + /* Remove space added for additional fpu registers. */ + addi sp, sp, portasmFPU_CONTEXT_WORDSIZE + load_fpu f0, PORT_CONTEXT_fpuOFFSET(0)(sp) /* f0(ft0) FP temporary register */ + load_fpu f1, PORT_CONTEXT_fpuOFFSET(1)(sp) /* f1(ft1) FP temporary register */ + load_fpu f2, PORT_CONTEXT_fpuOFFSET(2)(sp) /* f2(ft2) FP temporary register */ + load_fpu f3, PORT_CONTEXT_fpuOFFSET(3)(sp) /* f3(ft3) FP temporary register */ + load_fpu f4, PORT_CONTEXT_fpuOFFSET(4)(sp) /* f4(ft4) FP temporary register */ + load_fpu f5, PORT_CONTEXT_fpuOFFSET(5)(sp) /* f5(ft5) FP temporary register */ + load_fpu f6, PORT_CONTEXT_fpuOFFSET(6)(sp) /* f6(ft6) FP temporary register */ + load_fpu f7, PORT_CONTEXT_fpuOFFSET(7)(sp) /* f7(ft7) FP temporary register */ + + load_fpu f8, PORT_CONTEXT_fpuOFFSET(8)(sp) /* f8(fs0) FP Saved register */ + load_fpu f9, PORT_CONTEXT_fpuOFFSET(9)(sp) /* f9(fs0) FP Saved register */ + + load_fpu f10, PORT_CONTEXT_fpuOFFSET(10)(sp) /* f10(fa0) FP arguments/return values register */ + load_fpu f11, PORT_CONTEXT_fpuOFFSET(11)(sp) /* f11(fa1) FP arguments/return values register */ + + load_fpu f12, PORT_CONTEXT_fpuOFFSET(12)(sp) /* f12(fa2) FP arguments register */ + load_fpu f13, PORT_CONTEXT_fpuOFFSET(13)(sp) /* f13(fa3) FP arguments register */ + load_fpu f14, PORT_CONTEXT_fpuOFFSET(14)(sp) /* f14(fa4) FP arguments register */ + load_fpu f15, PORT_CONTEXT_fpuOFFSET(15)(sp) /* f15(fa5) FP arguments register */ + load_fpu f16, PORT_CONTEXT_fpuOFFSET(16)(sp) /* f16(fa6) FP arguments register */ + load_fpu f17, PORT_CONTEXT_fpuOFFSET(17)(sp) /* f17(fa7) FP arguments register */ + + load_fpu f18, PORT_CONTEXT_fpuOFFSET(18)(sp) /* f18(fs2) FP Saved register */ + load_fpu f19, PORT_CONTEXT_fpuOFFSET(19)(sp) /* f19(fs3) FP Saved register */ + load_fpu f20, PORT_CONTEXT_fpuOFFSET(20)(sp) /* f20(fs4) FP Saved register */ + load_fpu f21, PORT_CONTEXT_fpuOFFSET(21)(sp) /* f21(fs5) FP Saved register */ + load_fpu f22, PORT_CONTEXT_fpuOFFSET(22)(sp) /* f22(fs6) FP Saved register */ + load_fpu f23, PORT_CONTEXT_fpuOFFSET(23)(sp) /* f23(fs7) FP Saved register */ + load_fpu f24, PORT_CONTEXT_fpuOFFSET(24)(sp) /* f24(fs8) FP Saved register */ + load_fpu f25, PORT_CONTEXT_fpuOFFSET(25)(sp) /* f25(fs9) FP Saved register */ + load_fpu f26, PORT_CONTEXT_fpuOFFSET(26)(sp) /* f26(fs10) FP Saved register */ + load_fpu f27, PORT_CONTEXT_fpuOFFSET(27)(sp) /* f27(fs11) FP Saved register */ + + load_fpu f28, PORT_CONTEXT_fpuOFFSET(28)(sp) /* f28(ft8) FP temporary register */ + load_fpu f29, PORT_CONTEXT_fpuOFFSET(29)(sp) /* f29(ft9) FP temporary register */ + load_fpu f30, PORT_CONTEXT_fpuOFFSET(30)(sp) /* f30(ft10) FP temporary register */ + load_fpu f31, PORT_CONTEXT_fpuOFFSET(31)(sp) /* f31(ft11) FP temporary register */ + + /* must set FS to clean */ + csrc mstatus, t0 + li t1, MSTATUS_FS_CLEAN + csrs mstatus, t1 +1: + .endm +/*************************************************************/ + +.macro portSAVE_BaseReg + /* Make room for the registers. */ + addi sp, sp, -portasmREGISTER_CONTEXT_WORDSIZE + store_x x1, PORT_CONTEXT_xOFFSET(1)(sp) /* x1(ra) Return address */ + /* x2(sp) ***** Should be save ouside this macro */ + store_x x3, PORT_CONTEXT_xOFFSET(3)(sp) /* x3(gp) Global pointer */ + store_x x4, PORT_CONTEXT_xOFFSET(4)(sp) /* x4(tp) Thread pointer */ + store_x x5, PORT_CONTEXT_xOFFSET(5)(sp) /* x5(t0) Temporary register */ + store_x x6, PORT_CONTEXT_xOFFSET(6)(sp) /* x6(t1) Temporary register*/ + store_x x7, PORT_CONTEXT_xOFFSET(7)(sp) /* x7(t2) Temporary register */ + store_x x8, PORT_CONTEXT_xOFFSET(8)(sp) /* x8(s0/fp) Saved register/Frame pointer */ + store_x x9, PORT_CONTEXT_xOFFSET(9)(sp) /* x9(s1) Saved register */ + store_x x10, PORT_CONTEXT_xOFFSET(10)(sp) /* x10(a0) Function argument */ + store_x x11, PORT_CONTEXT_xOFFSET(11)(sp) /* x11(a1) Function argument */ + store_x x12, PORT_CONTEXT_xOFFSET(12)(sp) /* x12(a2) Function argument */ + store_x x13, PORT_CONTEXT_xOFFSET(13)(sp) /* x13(a3) Function argument */ + store_x x14, PORT_CONTEXT_xOFFSET(14)(sp) /* x14(a4) Function argument */ + store_x x15, PORT_CONTEXT_xOFFSET(15)(sp) /* x15(a5) Function argument */ +#ifndef __riscv_32e + store_x x16, PORT_CONTEXT_xOFFSET(16)(sp) /* x16(a6) Function arguments */ + store_x x17, PORT_CONTEXT_xOFFSET(17)(sp) /* x17(a7) Function arguments */ + store_x x18, PORT_CONTEXT_xOFFSET(18)(sp) /* x18(s2) Saved register */ + store_x x19, PORT_CONTEXT_xOFFSET(19)(sp) /* x19(s3) Saved register */ + store_x x20, PORT_CONTEXT_xOFFSET(20)(sp) /* x20(s4) Saved register */ + store_x x21, PORT_CONTEXT_xOFFSET(21)(sp) /* x21(s5) Saved register */ + store_x x22, PORT_CONTEXT_xOFFSET(22)(sp) /* x22(s6) Saved register */ + store_x x23, PORT_CONTEXT_xOFFSET(23)(sp) /* x23(s7) Saved register */ + store_x x24, PORT_CONTEXT_xOFFSET(24)(sp) /* x24(s8) Saved register */ + store_x x25, PORT_CONTEXT_xOFFSET(25)(sp) /* x25(s9) Saved register */ + store_x x26, PORT_CONTEXT_xOFFSET(26)(sp) /* x26(s10) Saved register */ + store_x x27, PORT_CONTEXT_xOFFSET(27)(sp) /* x27(s11) Saved register */ + store_x x28, PORT_CONTEXT_xOFFSET(28)(sp) /* x28(t3) Temporary register */ + store_x x29, PORT_CONTEXT_xOFFSET(29)(sp) /* x29(t4) Temporary register */ + store_x x30, PORT_CONTEXT_xOFFSET(30)(sp) /* x30(t5) Temporary register */ + store_x x31, PORT_CONTEXT_xOFFSET(31)(sp) /* x31(t6) Temporary register */ +#endif /* __riscv_32e */ + /* Save mcause, mepc & mstatus state */ + csrr a4, mepc + csrr a5, mstatus /* Required for MPIE bit. */ + store_x a4, PORT_CONTEXT_mepcOFFSET(sp) + store_x a5, PORT_CONTEXT_mstatusOFFSET(sp) + .endm +/*************************************************************/ + +.macro portRESTORE_BaseReg + /* Restore mepc & mstatus state */ + load_x t0, PORT_CONTEXT_mepcOFFSET(sp) + load_x t1, PORT_CONTEXT_mstatusOFFSET(sp) + csrw mepc, t0 + csrw mstatus, t1 + + load_x x1, PORT_CONTEXT_xOFFSET(1)(sp) /* x1(ra) Return address */ + /* x2(sp) ***** Should be save ouside this macro */ + load_x x3, PORT_CONTEXT_xOFFSET(3)(sp) /* x3(gp) Global pointer */ + load_x x4, PORT_CONTEXT_xOFFSET(4)(sp) /* x4(tp) Thread pointer */ + load_x x5, PORT_CONTEXT_xOFFSET(5)(sp) /* x5(t0) Temporary register */ + load_x x6, PORT_CONTEXT_xOFFSET(6)(sp) /* x6(t1) Temporary register*/ + load_x x7, PORT_CONTEXT_xOFFSET(7)(sp) /* x7(t2) Temporary register */ + load_x x8, PORT_CONTEXT_xOFFSET(8)(sp) /* x8(s0/fp) Saved register/Frame pointer */ + load_x x9, PORT_CONTEXT_xOFFSET(9)(sp) /* x9(s1) Saved register */ + load_x x10, PORT_CONTEXT_xOFFSET(10)(sp) /* x10(a0) Function argument */ + load_x x11, PORT_CONTEXT_xOFFSET(11)(sp) /* x11(a1) Function argument */ + load_x x12, PORT_CONTEXT_xOFFSET(12)(sp) /* x12(a2) Function argument */ + load_x x13, PORT_CONTEXT_xOFFSET(13)(sp) /* x13(a3) Function argument */ + load_x x14, PORT_CONTEXT_xOFFSET(14)(sp) /* x14(a4) Function argument */ + load_x x15, PORT_CONTEXT_xOFFSET(15)(sp) /* x15(a5) Function argument */ +#ifndef __riscv_32e + load_x x16, PORT_CONTEXT_xOFFSET(16)(sp) /* x16(a6) Function arguments */ + load_x x17, PORT_CONTEXT_xOFFSET(17)(sp) /* x17(a7) Function arguments */ + load_x x18, PORT_CONTEXT_xOFFSET(18)(sp) /* x18(s2) Saved register */ + load_x x19, PORT_CONTEXT_xOFFSET(19)(sp) /* x19(s3) Saved register */ + load_x x20, PORT_CONTEXT_xOFFSET(20)(sp) /* x20(s4) Saved register */ + load_x x21, PORT_CONTEXT_xOFFSET(21)(sp) /* x21(s5) Saved register */ + load_x x22, PORT_CONTEXT_xOFFSET(22)(sp) /* x22(s6) Saved register */ + load_x x23, PORT_CONTEXT_xOFFSET(23)(sp) /* x23(s7) Saved register */ + load_x x24, PORT_CONTEXT_xOFFSET(24)(sp) /* x24(s8) Saved register */ + load_x x25, PORT_CONTEXT_xOFFSET(25)(sp) /* x25(s9) Saved register */ + load_x x26, PORT_CONTEXT_xOFFSET(26)(sp) /* x26(s10) Saved register */ + load_x x27, PORT_CONTEXT_xOFFSET(27)(sp) /* x27(s11) Saved register */ + load_x x28, PORT_CONTEXT_xOFFSET(28)(sp) /* x28(t3) Temporary register */ + load_x x29, PORT_CONTEXT_xOFFSET(29)(sp) /* x29(t4) Temporary register */ + load_x x30, PORT_CONTEXT_xOFFSET(30)(sp) /* x30(t5) Temporary register */ + load_x x31, PORT_CONTEXT_xOFFSET(31)(sp) /* x31(t6) Temporary register */ +#endif /* __riscv_32e */ + .endm +/*************************************************************/ + + +.align 8 +.type freertos_risc_v_trap_handler, @function +freertos_risc_v_trap_handler: + /* We do not know if this is an ASYNC or SYNC + * If ASYNC, it is a normal interrupt + * and the stack pointer is assumed good. + * else (SYNC) + * We could be here due to a bus fault. + */ + csrw mscratch, t0 + csrr t0, mcause + blt t0, x0, handle_interrupt + +handle_exception: + /* mscratch = old t0 + * t0 = mcause + * mcause = small number 0..16 + * 0 Instruction address misaligned + * 1 Instruction access fault + * 2 Illegal instruction + * 3 Breakpoint + * 4 Load address misaligned + * 5 Load access fault + * 6 Store/AMO address misaligned + * 7 Store/AMO access fault + * 8 Environment call from U-mode + * 9 Environment call from S-mode + * 10 Reserved + * 11 Environment call from M-mode + * 12 Instruction page fault + * 13 Load page fault + * 14 Reserved + * 15 Store/AMO page fault + * ≥16 Reserved + * + * if( mcause between 8 and 11 ) we are good - ecall + * else: problem + */ + + addi t0, t0, -8 + blt t0, x0, is_exception /* mcause < 8, must be fault */ + addi t0, t0, -4 + blt t0, x0, environment_switch + +is_exception: + /* restore t0 and save sp in mscratch. */ + csrr t0, mscratch + csrw mscratch, sp + /* Switch to ISR stack before function call. */ + load_x sp, xISRStackTop + portSAVE_BaseReg + csrrw t0, mscratch, t0 + /* SP = X2, so save it */ + store_x t0, PORT_CONTEXT_xOFFSET(2)(sp) + + /* Defined in freertos_risc_v_chip_specific_extensions.h to save any registers unique to the RISC-V implementation. */ + portasmSAVE_ADDITIONAL_REGISTERS + +#ifdef __riscv_fdiv + portSAVE_FpuReg +#endif /* __riscv_fdiv */ + + /* Execption is treated by freedom metal functions */ + jal portHANDLE_EXCEPTION + +#ifdef __riscv_fdiv + load_x t0, xISRStackTop + addi a0, t0, -portasmREGISTER_CONTEXT_WORDSIZE + portRESTORE_FpuReg +#endif /* __riscv_fdiv */ + portasmRESTORE_ADDITIONAL_REGISTERS + portRESTORE_BaseReg + + store_x x5, PORT_CONTEXT_xOFFSET(5)(sp) + csrr x5, mepc + addi x5,x5,4 + csrw mepc, x5 + load_x x5, PORT_CONTEXT_xOFFSET(5)(sp) + + load_x x2, PORT_CONTEXT_xOFFSET(2)(sp) + mret + +environment_switch: + li t0, 4 + bgt a0, t0, ecall_end + la t0, 1f + slli a0, a0, 2 + add t0, t0, a0 + jr t0 +1: + jal x0, ecall_yield + jal x0, ecall_disable_interrupt + jal x0, ecall_enable_interrupt + jal x0, ecall_switch_to_machine + jal x0, ecall_switch_to_user + +ecall_disable_interrupt: + /* Clear mpie */ + li a0, 0x80 + csrc mstatus, a0 + j ecall_mret + +ecall_enable_interrupt: + /* Set mpie */ + li a0, 0x80 + csrs mstatus, a0 + j ecall_mret + +ecall_switch_to_machine: + /* Set mpp */ + li a0, 0x1800 + csrs mstatus, a0 + j ecall_mret + +ecall_switch_to_user: + /* Clear mpp */ + li a0, 0x1800 + csrc mstatus, a0 + j ecall_mret + +ecall_mret: + /* Synchronous so updated exception return address to the instruction after the instruction that generated the exeption. */ + csrr t0, mepc + addi t0, t0, 4 + csrw mepc, t0 + /* restore t0 */ + csrr t0, mscratch + mret + +ecall_end: + /* unauthorized ecall */ + j unrecoverable_error + +ecall_yield: + /* restore t0 */ + csrr t0, mstatus + portSAVE_BaseReg + /* a4 = mepc + * a5 = mstatus + * s0 will be use for pxCurrentTCB + * s1 will be use to save sp + */ + /* Synchronous so updated exception return address to the instruction after the instruction that generated the exeption. */ + addi t0, a4, 4 + store_x t0, PORT_CONTEXT_mepcOFFSET(sp) + /* Store the value of sp when the interrupt occur */ + addi t0, sp, portasmREGISTER_CONTEXT_WORDSIZE + store_x t0, PORT_CONTEXT_xOFFSET(2)(sp) + + /* Defined in freertos_risc_v_chip_specific_extensions.h to save any registers unique to the RISC-V implementation. */ + portasmSAVE_ADDITIONAL_REGISTERS + +#ifdef __riscv_fdiv + portSAVE_FpuReg +#endif /* __riscv_fdiv */ + + /* Load pxCurrentTCB and update first TCB member(pxTopOfStack) with sp. */ + load_x s0, pxCurrentTCB + store_x sp, 0( s0 ) + + /* Save sp into s1 */ + mv s1, sp + load_x sp, xISRStackTop /* Switch to ISR stack before function call. */ + + j switch_context + +handle_interrupt: + portSAVE_BaseReg + /* a4 = mepc + * a5 = mstatus + * s0 will be use for pxCurrentTCB + * s1 will be use to save sp + */ + + /* Store the value of sp when the interrupt occur */ + addi t0, sp, portasmREGISTER_CONTEXT_WORDSIZE + store_x t0, PORT_CONTEXT_xOFFSET(2)(sp) + + /* Defined in freertos_risc_v_chip_specific_extensions.h to save any registers unique to the RISC-V implementation. */ + portasmSAVE_ADDITIONAL_REGISTERS + +#ifdef __riscv_fdiv + portSAVE_FpuReg +#endif /* __riscv_fdiv */ + + /* Load pxCurrentTCB and update first TCB member(pxTopOfStack) with sp. */ + load_x s0, pxCurrentTCB + store_x sp, 0( s0 ) + + /* Save sp into s1 */ + mv s1, sp + load_x sp, xISRStackTop /* Switch to ISR stack before function call. */ + +#if( portasmHAS_CLINT != 0 ) + addi t0, x0, 1 + + slli t0, t0, __riscv_xlen - 1 /* LSB is already set, shift into MSB. Shift 31 on 32-bit or 63 on 64-bit cores. */ + addi t1, t0, 7 /* 0x8000[]0007 == machine timer interrupt. */ + csrr t2, mcause + bne t2, t1, test_if_external_interrupt + + load_x t0, pullMachineTimerCompareRegister /* Load address of compare register into t0. */ + load_x t1, pullNextTime /* Load the address of ullNextTime into t1. */ + +# if __riscv_xlen == 32 + /* Update the 64-bit mtimer compare match value in two 32-bit writes. */ + lw a0, 0(t1) /* Load the low word of ullNextTime into a0. */ + lw a1, 4(t1) /* Load the high word of ullNextTime into a1. */ + li t2, -1 + sw t2, 4(t0) /* Store low word of ullNextTime into compare register. */ + sw a0, 0(t0) /* Store low word of ullNextTime into compare register. */ + sw a1, 4(t0) /* Store high word of ullNextTime into compare register. */ + lw t0, uxTimerIncrementsForOneTick /* Load the value of ullTimerIncrementForOneTick into t0 (could this be optimized by storing in an array next to pullNextTime?). */ + add a2, t0, a0 /* Add the low word of ullNextTime to the timer increments for one tick (assumes timer increment for one tick fits in 32-bits). */ + sltu t2, a2, a0 /* See if the sum of low words overflowed (what about the zero case?). */ + add a3, a1, t2 /* Add overflow to high word of ullNextTime. */ + sw a2, 0(t1) /* Store new low word of ullNextTime. */ + sw a3, 4(t1) /* Store new high word of ullNextTime. */ +# endif /* __riscv_xlen == 32 */ +# if __riscv_xlen == 64 + /* Update the 64-bit mtimer compare match value. */ + ld a0, 0(t1) /* Load ullNextTime into a0. */ + sd a0, 0(t0) /* Store ullNextTime into compare register. */ + ld t0, uxTimerIncrementsForOneTick /* Load the value of ullTimerIncrementForOneTick into t0 (could this be optimized by storing in an array next to pullNextTime?). */ + add a2, t0, a0 /* Add ullNextTime to the timer increments for one tick. */ + sd a2, 0(t1) /* Store ullNextTime. */ +# endif /* __riscv_xlen == 64 */ + + jal xTaskIncrementTick + beqz a0, restore_before_exit /* Don't switch context if incrementing tick didn't unblock a task. */ + j switch_context + +restore_before_exit: + mv sp, s1 + j end_trap_handler + +test_if_external_interrupt: /* If there is a CLINT and the mtimer interrupt is not pending then check to see if an external interrupt is pending. */ + addi t1, t1, 4 /* 0x80000007 + 4 = 0x8000000b == Machine external interrupt. */ + csrr t2, mcause + bne t2, t1, unrecoverable_error /* Something as yet unhandled. */ + j unrecoverable_error + +#endif /* portasmHAS_CLINT */ + +external_interrupt: + /* Switch to ISR stack before function call. */ + load_x sp, xISRStackTop + jal portHANDLE_INTERRUPT + mv sp, s1 + j end_trap_handler + +unrecoverable_error: + csrr t0, mcause /* For viewing in the debugger only. */ + csrr t1, mepc /* For viewing in the debugger only. */ + csrr t2, mstatus + wfi + j unrecoverable_error + +switch_context: + jal vTaskSwitchContext + load_x s0, pxCurrentTCB /* Load pxCurrentTCB. */ + load_x sp, 0( s0 ) /* Read sp from first TCB member. */ + jal vPortPmpSwitch /* Call C function that will return if not MPU setting */ + +end_trap_handler: + load_x s0, pxCurrentTCB /* Load pxCurrentTCB. */ + + /* Update privilege_status if needed */ + load_x a0, PORT_CONTEXT_mstatusOFFSET(sp) + jal vPortUpdatePrivilegeStatus + + /* restore registers */ +#ifdef __riscv_fdiv + addi a0, sp, portasmFPU_CONTEXT_WORDSIZE + addi a0, a0, portasmADDITIONAL_CONTEXT_WORDSIZE + portRESTORE_FpuReg +#endif /* __riscv_fdiv */ + + portasmRESTORE_ADDITIONAL_REGISTERS + + load_x s0, pxCurrentTCB /* Load pxCurrentTCB. */ + load_x t1, PORT_CONTEXT_xOFFSET(2)(sp) + store_x t1, 0( s0 ) /* Write sp saved value to first TCB member. */ + + portRESTORE_BaseReg + load_x x2, PORT_CONTEXT_xOFFSET(2)(sp) + mret +/*-----------------------------------------------------------*/ + +.align 8 +.type xPortStartFirstTask, @function +xPortStartFirstTask: + /* + * Save all register to space before xISRStackTop (it should be prepared by vPortFreeRTOSInit). + * It will be restored when vPortEndScheduler will be call + */ + csrw mscratch, sp + + load_x sp, xISRStackTop + #ifdef __riscv_fdiv + addi sp, sp, portasmFPU_CONTEXT_WORDSIZE + #endif /* __riscv_fdiv */ + addi sp, sp, portasmADDITIONAL_CONTEXT_WORDSIZE + addi sp, sp, portasmREGISTER_CONTEXT_WORDSIZE + + portSAVE_BaseReg + + csrrw t0, mscratch, t0 + /* SP = X2, so save it */ + store_x t0, PORT_CONTEXT_xOFFSET(2)(sp) + /* x1 ra Return address */ + store_x x1, PORT_CONTEXT_xOFFSET(1)(sp) + + /* Defined in freertos_risc_v_chip_specific_extensions.h to save any registers unique to the RISC-V implementation. */ + portasmSAVE_ADDITIONAL_REGISTERS + +#ifdef __riscv_fdiv + portSAVE_FpuReg +#endif /* __riscv_fdiv */ + + /** Set all register to the FirstTask context */ + load_x t2, pxCurrentTCB /* Load pxCurrentTCB. */ + load_x sp, 0( t2 ) /* Read sp from first TCB member. */ + + /* Update privilege_status if needed */ + load_x a0, PORT_CONTEXT_mstatusOFFSET(sp) + jal vPortUpdatePrivilegeStatus + + jal vPortPmpSwitch + + load_x t2, pxCurrentTCB /* Load pxCurrentTCB. */ + load_x sp, 0( t2 ) /* Read sp from first TCB member. */ + +#ifdef __riscv_fdiv + addi a0, sp, portasmFPU_CONTEXT_WORDSIZE + addi a0, a0, portasmADDITIONAL_CONTEXT_WORDSIZE + portRESTORE_FpuReg +#endif /* __riscv_fdiv */ + + portasmRESTORE_ADDITIONAL_REGISTERS + + /* Restore first TCB member */ + load_x t2, pxCurrentTCB /* Load pxCurrentTCB. */ + load_x t1, PORT_CONTEXT_xOFFSET(2)(sp) + store_x t1, 0( t2 ) /* Write sp saved value to first TCB member. */ + + /* enable interrupt */ + #if( portasmHAS_CLINT != 0 ) + li t0, 0x880 + csrs mie, t0 + #else + li t0, 0x800 + csrs mie, t0 + #endif /* ( portasmHAS_CLINT != 0 ) */ + + portRESTORE_BaseReg + + load_x ra, PORT_CONTEXT_mepcOFFSET(sp) /* Note for starting the scheduler the exception return address is used as the function return address. */ + load_x x2, PORT_CONTEXT_xOFFSET(2)(sp) + mret +/*-----------------------------------------------------------*/ + +/** + * StackType_t * pxPortAsmInitialiseStack( StackType_t *, TaskFunction_t, void *, UBaseType_t); + */ +.align 8 +.type pxPortAsmInitialiseStack, @function +pxPortAsmInitialiseStack: + /* Make room for the registers. */ + addi t2, a0, -portasmREGISTER_CONTEXT_WORDSIZE + + store_x x0, PORT_CONTEXT_xOFFSET(1)(t2) /* x1(ra) Return address */ + store_x a0, PORT_CONTEXT_xOFFSET(2)(t2) /* x2(sp) Stack pointer */ + store_x x3, PORT_CONTEXT_xOFFSET(3)(t2) /* x3(gp) Global pointer */ + store_x x4, PORT_CONTEXT_xOFFSET(4)(t2) /* x4(tp) Thread pointer */ + store_x x0, PORT_CONTEXT_xOFFSET(5)(t2) /* x5(t0) Temporaries */ + store_x x0, PORT_CONTEXT_xOFFSET(6)(t2) /* x6(t1) Temporaries */ + store_x x0, PORT_CONTEXT_xOFFSET(7)(t2) /* x7(t2) Temporaries */ + store_x x0, PORT_CONTEXT_xOFFSET(8)(t2) /* x8(s0/fp) Saved register/Frame pointer */ + store_x x0, PORT_CONTEXT_xOFFSET(9)(t2) /* x9(s1) Saved register */ + store_x a2, PORT_CONTEXT_xOFFSET(10)(t2) /* x10(a0) Function arguments */ + store_x x0, PORT_CONTEXT_xOFFSET(11)(t2) /* x11(a1) Function arguments */ + store_x x0, PORT_CONTEXT_xOFFSET(12)(t2) /* x12(a2) Function arguments */ + store_x x0, PORT_CONTEXT_xOFFSET(13)(t2) /* x13(a3) Function arguments */ + store_x x0, PORT_CONTEXT_xOFFSET(14)(t2) /* x14(a4) Function arguments */ + store_x x0, PORT_CONTEXT_xOFFSET(15)(t2) /* x15(a5) Function arguments */ +#ifndef __riscv_32e + store_x x0, PORT_CONTEXT_xOFFSET(16)(t2) /* x16(a6) Function arguments */ + store_x x0, PORT_CONTEXT_xOFFSET(17)(t2) /* x17(a7) Function arguments */ + store_x x0, PORT_CONTEXT_xOFFSET(18)(t2) /* x18(s2) Saved registers */ + store_x x0, PORT_CONTEXT_xOFFSET(19)(t2) /* x19(s3) Saved registers */ + store_x x0, PORT_CONTEXT_xOFFSET(20)(t2) /* x20(s4) Saved registers */ + store_x x0, PORT_CONTEXT_xOFFSET(21)(t2) /* x21(s5) Saved registers */ + store_x x0, PORT_CONTEXT_xOFFSET(22)(t2) /* x22(s6) Saved registers */ + store_x x0, PORT_CONTEXT_xOFFSET(23)(t2) /* x23(s7) Saved registers */ + store_x x0, PORT_CONTEXT_xOFFSET(24)(t2) /* x24(s8) Saved registers */ + store_x x0, PORT_CONTEXT_xOFFSET(25)(t2) /* x25(s9) Saved registers */ + store_x x0, PORT_CONTEXT_xOFFSET(26)(t2) /* x26(s10) Saved registers */ + store_x x0, PORT_CONTEXT_xOFFSET(27)(t2) /* x27(s11) Saved registers */ + store_x x0, PORT_CONTEXT_xOFFSET(28)(t2) /* x28(t3) Temporaries */ + store_x x0, PORT_CONTEXT_xOFFSET(29)(t2) /* x29(t4) Temporaries */ + store_x x0, PORT_CONTEXT_xOFFSET(30)(t2) /* x30(t5) Temporaries */ + store_x x0, PORT_CONTEXT_xOFFSET(31)(t2) /* x31(t6) Temporaries */ +#endif /* __riscv_32e */ + + store_x a1, PORT_CONTEXT_mepcOFFSET(t2) + store_x a3, PORT_CONTEXT_mstatusOFFSET(t2) + + /* The number of additional registers. */ + addi t0, x0, portasmADDITIONAL_CONTEXT_SIZE +chip_specific_stack_frame: /* First add any chip specific registers to the stack frame being created. */ + beq t0, x0, 1f /* No more chip specific registers to save. */ + addi t2, t2, -portWORD_SIZE /* Make space for chip specific register. */ + store_x x0, 0(t2) /* Give the chip specific register an initial value of zero. */ + addi t0, t0, -1 /* Decrement the count of chip specific registers remaining. */ + j chip_specific_stack_frame /* Until no more chip specific registers. */ +1: + + #ifdef __riscv_fdiv + /* Make room for the fpu registers. */ + /* Here we use the memory space needed for all fpu registers instead of using the number of fpu registers */ + /* Thanks to it we usually manage any xxbits core with yybits fpu */ + addi t0, x0, portasmFPU_CONTEXT_WORDSIZE + fpu_specific_stack_frame: + beq t0, x0, 1f /* No more space is needed. */ + addi t2, t2, -portWORD_SIZE + store_x x0, 0(t2) /* Give an initial value of zero. */ + addi t0, t0, -portWORD_SIZE /* Decrement the count space remaining. */ + j fpu_specific_stack_frame /* Until no more space is needed. */ + 1: + #endif /* __riscv_fdiv */ + + mv a0, t2 + ret +/*-----------------------------------------------------------*/ + +/** + * void xPortRestoreBeforeFirstTask(void) + */ +.align 8 +.type xPortRestoreBeforeFirstTask, @function +xPortRestoreBeforeFirstTask: + load_x sp, xISRStackTop +#ifdef __riscv_fdiv + addi a0, sp, portasmFPU_CONTEXT_WORDSIZE + addi a0, a0, portasmADDITIONAL_CONTEXT_WORDSIZE + portRESTORE_FpuReg +#endif /* __riscv_fdiv */ + portasmRESTORE_ADDITIONAL_REGISTERS + portRESTORE_BaseReg + load_x x2, PORT_CONTEXT_xOFFSET(2)(sp) + ret +/*-----------------------------------------------------------*/ + +/** + * BaseType_t xPortMoveISRStackTop( StackType_t *xISRStackTop); + */ +.align 8 +.type xPortMoveISRStackTop, @function +xPortMoveISRStackTop: + load_x t0, 0(a0) + beqz t0, 1f + addi t1, x0, portasmREGISTER_CONTEXT_WORDSIZE + addi t2, t1, portasmADDITIONAL_CONTEXT_WORDSIZE + bltu t2, t1, 1f + #ifdef __riscv_fdiv + addi t1, t2, portasmFPU_CONTEXT_WORDSIZE + bltu t1, t2, 1f + mv t2, t1 + #endif /* __riscv_fdiv */ + bgtu t1, t0, 1f + sub t1, t0, t1 + store_x t1, 0(a0) + mv a0, t1 + j 2f +1: + li a0, 0 +2: + ret +/*-----------------------------------------------------------*/ + +/* + * Unlike other ports pxPortInitialiseStack() is written in assembly code as it + * needs access to the portasmADDITIONAL_CONTEXT_SIZE constant. The prototype + * for the function is as per the other ports: + * StackType_t *pxPortInitialiseStack( StackType_t *pxTopOfStack, TaskFunction_t pxCode, void *pvParameters ); + * + * As per the standard RISC-V ABI pxTopcOfStack is passed in in a0, pxCode in + * a1, and pvParameters in a2. The new top of stack is passed out in a0. + * + * RISC-V maps registers to ABI names as follows (X1 to X31 integer registers + * for the 'I' profile, X1 to X15 for the 'E' profile, currently I assumed). + * + * Register ABI Name Description Saver + * x0 zero Hard-wired zero - + * x1 ra Return address Caller + * x2 sp Stack pointer Callee + * x3 gp Global pointer - + * x4 tp Thread pointer - + * x5-7 t0-2 Temporaries Caller + * x8 s0/fp Saved register/Frame pointer Callee + * x9 s1 Saved register Callee + * x10-11 a0-1 Function Arguments/return values Caller + * x12-17 a2-7 Function arguments Caller + * x18-27 s2-11 Saved registers Callee + * x28-31 t3-6 Temporaries Caller + * + */ + +/*-----------------------------------------------------------*/ diff --git a/freertos/portable/GCC/RISC-V/portmacro.h b/freertos/portable/GCC/RISC-V/portmacro.h new file mode 100644 index 00000000..7a2b1a58 --- /dev/null +++ b/freertos/portable/GCC/RISC-V/portmacro.h @@ -0,0 +1,349 @@ +/* + * FreeRTOS Kernel V10.2.1 + * Copyright (C) 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * http://www.FreeRTOS.org + * http://aws.amazon.com/freertos + * + * 1 tab == 4 spaces! + */ + +#ifndef PORTMACRO_H +#define PORTMACRO_H + +#ifdef __cplusplus +extern "C" { +#endif + +/*----------------------------------------------------------- + * Port specific definitions. + * + * The settings in this file configure FreeRTOS correctly for the + * given hardware and compiler. + * + * These settings should not be altered. + *----------------------------------------------------------- + */ + +/* Type definitions. */ +#if __riscv_xlen == 64 + #define portSTACK_TYPE uint64_t + #define portBASE_TYPE int64_t + #define portUBASE_TYPE uint64_t + #define portMAX_DELAY ( TickType_t ) 0xffffffffffffffffUL + #define portPOINTER_SIZE_TYPE uint64_t +#elif __riscv_xlen == 32 + #define portSTACK_TYPE uint32_t + #define portBASE_TYPE int32_t + #define portUBASE_TYPE uint32_t + #define portMAX_DELAY ( TickType_t ) 0xffffffffUL +#else + #error Assembler did not define __riscv_xlen +#endif + +typedef portSTACK_TYPE StackType_t; +typedef portBASE_TYPE BaseType_t; +typedef portUBASE_TYPE UBaseType_t; +typedef portUBASE_TYPE TickType_t; + +#if __riscv_xlen == 64 +/* 64-bit tick type on a 64-bit architecture, so reads of the tick count do +not need to be guarded with a critical section. */ + #define portTICK_TYPE_IS_ATOMIC 1 +#elif __riscv_xlen == 32 +/* 64-bit tick type on a 32-bit architecture, so reads of the tick count need +to be guarded with a critical section. */ + #define portTICK_TYPE_IS_ATOMIC 0 +#else + #error __riscv_xlen is not defined +#endif + +/******************************************************************************/ +/****************************** PMP settings ********************************/ +/******************************************************************************/ + +#if( portUSING_MPU_WRAPPERS == 1 ) +#include "pmp.h" + +/* Privilege bit used to declare a Task as Privileged */ +#define portPRIVILEGE_BIT ( 0x80000000UL ) + +/**************************/ +/* System call commands */ +/**************************/ + +/* Command to indicate system call handler that yield is requested */ +#define portSVC_YIELD 0 +/* Command to indicate system call handler that interrupts should be disabled */ +#define portSVC_DISABLE_INTERRUPTS 1 +/* Command to indicate system call handler that interrupts should be disabled */ +#define portSVC_ENABLE_INTERRUPTS 2 +/* Command to indicate system call handler to switch to machine mode */ +#define portSVC_SWITCH_TO_MACHINE 3 +/* Command to indicate system call handler to switch to user mode */ +#define portSVC_SWITCH_TO_USER 4 + +/**********************/ +/* PMP configs rights */ +/**********************/ + +/* Used to give a PMP region read right */ +#define portPMP_REGION_READ_ONLY ( 0x01UL ) +/* Used to give a PMP region read/write right */ +#define portPMP_REGION_READ_WRITE ( 0x03UL ) +/* Used to give a PMP region execute right */ +#define portPMP_REGION_EXECUTE ( 0x04UL ) +/** + * Used to indicate that a PMP region as no right, and therefore only + * Machine/Supervisor mode access is possible + */ +#define portPMP_REGION_PRIVILEGED_ACCESS_ONLY ( 0x00UL ) + + +/********************/ +/* PMP configs mode */ +/********************/ + +/* Used to indicate that a PMP region should be disable */ +#define portPMP_REGION_OFF ( 0x00UL ) +/* Used to indicate that a PMP region should use TOR mode */ +#define portPMP_REGION_ADDR_MATCH_TOR ( 0x08UL ) +/* Used to indicate that a PMP region should use NA4 mode */ +#define portPMP_REGION_ADDR_MATCH_NA4 ( 0x10UL ) +/* Used to indicate that a PMP region should use NAPOT mode */ +#define portPMP_REGION_ADDR_MATCH_NAPOT ( 0x18UL ) + +/********************/ +/* PMP configs lock */ +/********************/ + +/** + * Lock a PMP region (apply right on Machine mode too, can only be unlock + * by reset) + */ +#define portPMP_REGION_LOCK ( 0x80UL ) + +/****************************/ +/* PMP regions for FreeRTOS */ +/****************************/ + +/* Start of unprivileged section that contain code to execute */ +#define portUNPRIVILEGED_EXECUTE_REGION_START ( 0UL ) +/* End of unprivileged section that contain code to execute */ +#define portUNPRIVILEGED_EXECUTE_REGION_END ( 1UL ) +/* Privilege status that allow FreeRTOS to save current execution mode */ +#define portPRIVILEGE_STATUS_REGION ( 2UL ) +/* Start of task stack region */ +#define portSTACK_REGION_START ( 3UL ) +/* End of task stack region */ +#define portSTACK_REGION_END ( 4UL ) +/* Fisrt configurable region */ +#define portFIRST_CONFIGURABLE_REGION ( 5UL ) +/** + * End configurable region (base on the Maxiumum number of PMP regions that + * RiscV allow available regions). + * Should be use only when static allocation is required, otherwise use + * portLAST_CONFIGURABLE_REGION_REAL(max_nb_pmp) (after init_pmp() execution) + */ +#define portLAST_CONFIGURABLE_REGION ( MAX_PMP_REGION - 1) +/** + * Maximum number of configurable regions + * Should be use only when static allocation is required, otherwise use + * portNUM_CONFIGURABLE_REGIONS_REAL(max_nb_pmp) (after init_pmp() execution) + */ +#define portNUM_CONFIGURABLE_REGIONS ( ( portLAST_CONFIGURABLE_REGION - portFIRST_CONFIGURABLE_REGION ) + 1 ) +#define portTOTAL_NUM_REGIONS ( portNUM_CONFIGURABLE_REGIONS + 2 ) // Plus 2 to make space for the stack region. +#define portTOTAL_NUM_CFG_REG ( NB_PMP_CFG_REG ) + +/** + * the number of pmp available for one hart(core) is dynamically determinated + * so we need to reevaluate the last configurable region available + */ +#define portLAST_CONFIGURABLE_REGION_REAL(max_nb_pmp) ( (max_nb_pmp) - 1 ) +/** + * the number of pmp available for one hart(core) is dynamically determinated + * so we need to reevaluate the number of configurable regions + */ +#define portNUM_CONFIGURABLE_REGIONS_REAL(max_nb_pmp) ( ( portLAST_CONFIGURABLE_REGION_REAL( max_nb_pmp ) - portFIRST_CONFIGURABLE_REGION ) + 1 ) + +/** + * Bit shift to apply on a PMP config to reach the region specified in parameter + */ +#define portPMPCFG_BIT_SHIFT(region) ( ((region) % SIZE_PMP_CFG_REG) << 3 ) +/** + * Get the pmpcfgx register index associated to the region input + */ +#define portGET_PMPCFG_IDX(region) ((region) / SIZE_PMP_CFG_REG) + +/* minimal number of pmp to use FreeRTOS with pmp */ +#define portMINIMAL_NB_PMP ( 5UL ) + +/** + * @brief PMP settings used to store PMP configs in task TCB + */ +typedef struct MPU_SETTINGS +{ + /* Configuration register configuration (pmpcfgx values) */ + UBaseType_t uxPmpConfigRegAttribute [portTOTAL_NUM_CFG_REG]; + /* Configuration register mask (pmpcfgx mask)*/ + UBaseType_t uxPmpConfigRegMask [portTOTAL_NUM_CFG_REG]; + /* Address register configuration (pmpaddrx values) */ + UBaseType_t uxRegionBaseAddress [ portTOTAL_NUM_REGIONS ]; +} xMPU_SETTINGS; + +extern __attribute__((naked)) void vPortSyscall( unsigned int ); + +/** + * @brief Do ecall to raise privilege + */ +void vRaisePrivilege( void ); +/** + * @brief Do ecall to reset privilege state + */ +void vResetPrivilege( void ); + +/** + * @brief Supported execution modes + */ +enum ePortPRIVILEGE_MODE { + /* User mode */ + ePortUSER_MODE = 0, + /* Supervisor mode */ + ePortSUPERVISOR_MODE =1, + /* Machine mode */ + ePortMACHINE_MODE = 3, +}; + +/** + * @brief Determine the current execution mode of the hart + * + * @return true (1) if the hart execute in machine mode + * @return false (0) otherwise + */ +BaseType_t xIsPrivileged( void ); + +/** + * @brief Determine the current execution mode of the hart + * + * @return true (1) if the hart execute in machine mode + * @return false (0) otherwise + */ +#define portIS_PRIVILEGED() xIsPrivileged() + +/** + * @brief Do ecall to raise privilege + */ +#define portRAISE_PRIVILEGE() vRaisePrivilege() + +/** + * @brief Do ecall to lower privilege level + */ +#define portRESET_PRIVILEGE() vResetPrivilege() + +#endif /* portUSING_MPU_WRAPPERS */ +/*-----------------------------------------------------------*/ + +/* Architecture specifics. */ +/* We use decreasing stack */ +#define portSTACK_GROWTH ( -1 ) +/* Number of tick per milliseconds */ +#define portTICK_PERIOD_MS ( ( TickType_t ) 1000 / configTICK_RATE_HZ ) + +#define portBYTE_ALIGNMENT 16 +/*-----------------------------------------------------------*/ + +void vPortFreeRTOSInit( StackType_t xTopOfStack ); + +extern void vPortFreeRTOSInit( StackType_t ); + +/* Scheduler utilities. */ +extern void vTaskSwitchContext( void ); +#if( portUSING_MPU_WRAPPERS == 1 ) +#define portYIELD() vPortSyscall(portSVC_YIELD) +#else +#define portYIELD() __asm volatile ( "mv a0, x0 \necall" ); +#endif +#define portEND_SWITCHING_ISR( xSwitchRequired ) if( xSwitchRequired ) vTaskSwitchContext() +#define portYIELD_FROM_ISR( x ) portEND_SWITCHING_ISR( x ) +/*-----------------------------------------------------------*/ + +/* Critical section management. */ +#define portCRITICAL_NESTING_IN_TCB 1 +extern void vTaskEnterCritical( void ); +extern void vTaskExitCritical( void ); + +#define portSET_INTERRUPT_MASK_FROM_ISR() 0 +#define portCLEAR_INTERRUPT_MASK_FROM_ISR( uxSavedStatusValue ) ( void ) uxSavedStatusValue +#if( portUSING_MPU_WRAPPERS == 1 ) +#define portDISABLE_INTERRUPTS() vPortSyscall(portSVC_DISABLE_INTERRUPTS) +#define portENABLE_INTERRUPTS() vPortSyscall(portSVC_ENABLE_INTERRUPTS) +#else +#define portDISABLE_INTERRUPTS() __asm volatile ( "csrc mstatus, 8" ) +#define portENABLE_INTERRUPTS() __asm volatile ( "csrs mstatus, 8" ) +#endif +#define portENTER_CRITICAL() vTaskEnterCritical() +#define portEXIT_CRITICAL() vTaskExitCritical() +/*-----------------------------------------------------------*/ + +/* Architecture specific optimisations. */ +#ifndef configUSE_PORT_OPTIMISED_TASK_SELECTION + #define configUSE_PORT_OPTIMISED_TASK_SELECTION 1 +#endif + +#if( configUSE_PORT_OPTIMISED_TASK_SELECTION == 1 ) + + /* Check the configuration. */ + #if( configMAX_PRIORITIES > 32 ) + #error configUSE_PORT_OPTIMISED_TASK_SELECTION can only be set to 1 when configMAX_PRIORITIES is less than or equal to 32. It is very rare that a system requires more than 10 to 15 difference priorities as tasks that share a priority will time slice. + #endif + + /* Store/clear the ready priorities in a bit map. */ + #define portRECORD_READY_PRIORITY( uxPriority, uxReadyPriorities ) ( uxReadyPriorities ) |= ( 1UL << ( uxPriority ) ) + #define portRESET_READY_PRIORITY( uxPriority, uxReadyPriorities ) ( uxReadyPriorities ) &= ~( 1UL << ( uxPriority ) ) + + /*-----------------------------------------------------------*/ + + #define portGET_HIGHEST_PRIORITY( uxTopPriority, uxReadyPriorities ) uxTopPriority = ( 31UL - __builtin_clz( uxReadyPriorities ) ) + +#endif /* configUSE_PORT_OPTIMISED_TASK_SELECTION */ +/*-----------------------------------------------------------*/ + +/* Task function macros as described on the FreeRTOS.org WEB site. These are +not necessary for to use this port. They are defined so the common demo files +(which build with all the ports) will build. */ +#define portTASK_FUNCTION_PROTO( vFunction, pvParameters ) void vFunction( void *pvParameters ) +#define portTASK_FUNCTION( vFunction, pvParameters ) void vFunction( void *pvParameters ) +/*-----------------------------------------------------------*/ + +#define portNOP() __asm volatile ( " nop " ) + +#define portINLINE __inline + +#ifndef portFORCE_INLINE + #define portFORCE_INLINE inline __attribute__(( always_inline, )) +#endif + +#define portMEMORY_BARRIER() __asm volatile ( "" ::: "memory" ) + +#ifdef __cplusplus +} +#endif + +#endif /* PORTMACRO_H */ \ No newline at end of file