diff --git a/.github/workflows/build-apps-job/build-apps.sh b/.github/workflows/build-apps-job/build-apps.sh index 9684b332e..c7a47d2a5 100755 --- a/.github/workflows/build-apps-job/build-apps.sh +++ b/.github/workflows/build-apps-job/build-apps.sh @@ -12,23 +12,39 @@ LONG_R="${RED}================================================================== LONG_W="${WHITE}================================================================================${RESET}" # Error vars are not defined if there is problem! -APPS=$(\ls sw/applications/) &&\ +APPS_GCC=$(\ls sw/applications/) &&\ + +# Applications that should only be compiled with GCC +ONLY_GCC="example_cpp" + +# Initialize APPS_CLANG with the same content as APPS_GCC +APPS_CLANG="$APPS_GCC" + +# Loop through ONLY_GCC to filter out the specified applications from APPS_CLANG +for app in $ONLY_GCC; do + # Remove the app from APPS_CLANG + APPS_CLANG=${APPS_CLANG//$app/} +done + declare -i FAILURES=0 &&\ FAILED='' &&\ echo -e ${LONG_W} echo -e "Will try building the following apps:${RESET}" -echo -e $APPS | tr " " "\n" +echo -e "----> GCC" +echo -e $APPS_GCC | tr " " "\n" +echo -e "----> CLANG" +echo -e $APPS_CLANG | tr " " "\n" echo -e ${LONG_W} -if [ -z "$APPS" ]; then +if [ -z "$APPS_GCC" ]; then echo -e ${LONG_R} echo -e "${RED}No apps found${RESET}" echo -e ${LONG_R} exit 2 fi -for APP in $APPS +for APP in $APPS_GCC do # Build the app with GCC @@ -44,7 +60,10 @@ do FAILURES=$(( FAILURES + 1 )) FAILED="$FAILED(gcc)\t$APP " fi - +done + +for APP in $APPS_CLANG +do # Build the app with Clang make app-clean if make app PROJECT=$APP COMPILER=clang ; then @@ -58,7 +77,6 @@ do FAILURES=$(( FAILURES + 1 )) FAILED="$FAILED(clang)\t$APP " fi - done diff --git a/.github/workflows/sim-apps-job/test_apps.py b/.github/workflows/sim-apps-job/test_apps.py index 0d3cdfce7..0b4e1516b 100755 --- a/.github/workflows/sim-apps-job/test_apps.py +++ b/.github/workflows/sim-apps-job/test_apps.py @@ -31,7 +31,7 @@ class BColors: # Blacklist of apps to skip blacklist = [ "example_spi_read", - "example_spi_host_dma_power_gate", + "example_spidma_powergate", "example_spi_write", ] diff --git a/.github/workflows/simulate.yml b/.github/workflows/simulate.yml index ffb167220..8e11e0851 100644 --- a/.github/workflows/simulate.yml +++ b/.github/workflows/simulate.yml @@ -17,5 +17,6 @@ jobs: conda activate core-v-mini-mcu make clean-all sed 's/is_included: "no",/is_included: "yes",/' -i mcu_cfg.hjson + sed 's/num_channels: 0x1,/num_channels: 0x4,/' -i mcu_cfg.hjson make mcu-gen MEMORY_BANKS=6 python3 .github/workflows/sim-apps-job/test_apps.py \ No newline at end of file diff --git a/.gitignore b/.gitignore index 710c28c7f..d369ddf84 100644 --- a/.gitignore +++ b/.gitignore @@ -18,6 +18,7 @@ run_verif_rtl_log.txt # ignore the following hw automatically generated files environment.yml core-v-mini-mcu.upf +core-v-mini-mcu.dc.upf tb/tb_util.svh hw/core-v-mini-mcu/include/core_v_mini_mcu_pkg.sv hw/core-v-mini-mcu/system_bus.sv @@ -40,6 +41,7 @@ hw/fpga/scripts/generate_sram.tcl # same for the C header file and linker scripts and assembly files sw/device/lib/crt/crt0.S sw/device/lib/runtime/core_v_mini_mcu.h +sw/device/lib/runtime/core_v_mini_mcu_memory.h sw/linker/link.ld sw/linker/link_flash_exec.ld sw/linker/link_flash_load.ld diff --git a/Makefile b/Makefile index d0579fcc0..ca1a02b0c 100644 --- a/Makefile +++ b/Makefile @@ -33,6 +33,8 @@ endif # Project options are based on the app to be build (default - hello_world) PROJECT ?= hello_world +LINK_FOLDER ?= $(mkfile_path)/sw/linker + # Linker options are 'on_chip' (default),'flash_load','flash_exec','freertos' LINKER ?= on_chip @@ -111,8 +113,10 @@ mcu-gen: $(PYTHON) util/mcu_gen.py --config $(X_HEEP_CFG) --cfg_peripherals $(MCU_CFG_PERIPHERALS) --pads_cfg $(PAD_CFG) --outdir hw/core-v-mini-mcu/ --bus $(BUS) --memorybanks $(MEMORY_BANKS) --memorybanks_il $(MEMORY_BANKS_IL) --tpl-sv hw/core-v-mini-mcu/core_v_mini_mcu.sv.tpl $(PYTHON) util/mcu_gen.py --config $(X_HEEP_CFG) --cfg_peripherals $(MCU_CFG_PERIPHERALS) --pads_cfg $(PAD_CFG) --outdir hw/system/ --bus $(BUS) --memorybanks $(MEMORY_BANKS) --memorybanks_il $(MEMORY_BANKS_IL) --tpl-sv hw/system/x_heep_system.sv.tpl $(PYTHON) util/mcu_gen.py --config $(X_HEEP_CFG) --cfg_peripherals $(MCU_CFG_PERIPHERALS) --pads_cfg $(PAD_CFG) --outdir sw/device/lib/runtime --cpu $(CPU) --bus $(BUS) --memorybanks $(MEMORY_BANKS) --memorybanks_il $(MEMORY_BANKS_IL) --external_domains $(EXTERNAL_DOMAINS) --header-c sw/device/lib/runtime/core_v_mini_mcu.h.tpl - $(PYTHON) util/mcu_gen.py --config $(X_HEEP_CFG) --cfg_peripherals $(MCU_CFG_PERIPHERALS) --pads_cfg $(PAD_CFG) --outdir sw/linker --bus $(BUS) --memorybanks $(MEMORY_BANKS) --memorybanks_il $(MEMORY_BANKS_IL) --linker_script sw/linker/link.ld.tpl + $(PYTHON) util/mcu_gen.py --config $(X_HEEP_CFG) --cfg_peripherals $(MCU_CFG_PERIPHERALS) --pads_cfg $(PAD_CFG) --outdir sw/device/lib/runtime --cpu $(CPU) --bus $(BUS) --memorybanks $(MEMORY_BANKS) --memorybanks_il $(MEMORY_BANKS_IL) --external_domains $(EXTERNAL_DOMAINS) --header-c sw/device/lib/runtime/core_v_mini_mcu_memory.h.tpl + $(PYTHON) util/mcu_gen.py --config $(X_HEEP_CFG) --cfg_peripherals $(MCU_CFG_PERIPHERALS) --pads_cfg $(PAD_CFG) --outdir $(LINK_FOLDER) --bus $(BUS) --memorybanks $(MEMORY_BANKS) --memorybanks_il $(MEMORY_BANKS_IL) --linker_script $(LINK_FOLDER)/link.ld.tpl $(PYTHON) util/mcu_gen.py --config $(X_HEEP_CFG) --cfg_peripherals $(MCU_CFG_PERIPHERALS) --pads_cfg $(PAD_CFG) --outdir . --bus $(BUS) --memorybanks $(MEMORY_BANKS) --memorybanks_il $(MEMORY_BANKS_IL) --pkg-sv ./core-v-mini-mcu.upf.tpl + $(PYTHON) util/mcu_gen.py --config $(X_HEEP_CFG) --cfg_peripherals $(MCU_CFG_PERIPHERALS) --pads_cfg $(PAD_CFG) --outdir . --bus $(BUS) --memorybanks $(MEMORY_BANKS) --memorybanks_il $(MEMORY_BANKS_IL) --pkg-sv ./core-v-mini-mcu.dc.upf.tpl $(PYTHON) util/mcu_gen.py --config $(X_HEEP_CFG) --cfg_peripherals $(MCU_CFG_PERIPHERALS) --pads_cfg $(PAD_CFG) --outdir hw/ip/power_manager/rtl --bus $(BUS) --memorybanks $(MEMORY_BANKS) --memorybanks_il $(MEMORY_BANKS_IL) --external_domains $(EXTERNAL_DOMAINS) --pkg-sv hw/ip/power_manager/data/power_manager.sv.tpl $(PYTHON) util/mcu_gen.py --config $(X_HEEP_CFG) --cfg_peripherals $(MCU_CFG_PERIPHERALS) --pads_cfg $(PAD_CFG) --outdir hw/ip/power_manager/data --bus $(BUS) --memorybanks $(MEMORY_BANKS) --memorybanks_il $(MEMORY_BANKS_IL) --external_domains $(EXTERNAL_DOMAINS) --pkg-sv hw/ip/power_manager/data/power_manager.hjson.tpl bash -c "cd hw/ip/power_manager; source power_manager_gen.sh; cd ../../../" @@ -120,8 +124,8 @@ mcu-gen: $(PYTHON) util/mcu_gen.py --config $(X_HEEP_CFG) --cfg_peripherals $(MCU_CFG_PERIPHERALS) --pads_cfg $(PAD_CFG) --outdir hw/system/pad_control/data --bus $(BUS) --memorybanks $(MEMORY_BANKS) --memorybanks_il $(MEMORY_BANKS_IL) --external_pads $(EXT_PAD_CFG) --pkg-sv hw/system/pad_control/data/pad_control.hjson.tpl $(PYTHON) util/mcu_gen.py --config $(X_HEEP_CFG) --cfg_peripherals $(MCU_CFG_PERIPHERALS) --pads_cfg $(PAD_CFG) --outdir hw/system/pad_control/rtl --bus $(BUS) --memorybanks $(MEMORY_BANKS) --memorybanks_il $(MEMORY_BANKS_IL) --external_pads $(EXT_PAD_CFG) --pkg-sv hw/system/pad_control/rtl/pad_control.sv.tpl bash -c "cd hw/system/pad_control; source pad_control_gen.sh; cd ../../../" - $(PYTHON) util/mcu_gen.py --config $(X_HEEP_CFG) --cfg_peripherals $(MCU_CFG_PERIPHERALS) --pads_cfg $(PAD_CFG) --outdir sw/linker --bus $(BUS) --memorybanks $(MEMORY_BANKS) --memorybanks_il $(MEMORY_BANKS_IL) --linker_script sw/linker/link_flash_exec.ld.tpl - $(PYTHON) util/mcu_gen.py --config $(X_HEEP_CFG) --cfg_peripherals $(MCU_CFG_PERIPHERALS) --pads_cfg $(PAD_CFG) --outdir sw/linker --bus $(BUS) --memorybanks $(MEMORY_BANKS) --memorybanks_il $(MEMORY_BANKS_IL) --linker_script sw/linker/link_flash_load.ld.tpl + $(PYTHON) util/mcu_gen.py --config $(X_HEEP_CFG) --cfg_peripherals $(MCU_CFG_PERIPHERALS) --pads_cfg $(PAD_CFG) --outdir $(LINK_FOLDER) --bus $(BUS) --memorybanks $(MEMORY_BANKS) --memorybanks_il $(MEMORY_BANKS_IL) --linker_script $(LINK_FOLDER)/link_flash_exec.ld.tpl + $(PYTHON) util/mcu_gen.py --config $(X_HEEP_CFG) --cfg_peripherals $(MCU_CFG_PERIPHERALS) --pads_cfg $(PAD_CFG) --outdir $(LINK_FOLDER) --bus $(BUS) --memorybanks $(MEMORY_BANKS) --memorybanks_il $(MEMORY_BANKS_IL) --linker_script $(LINK_FOLDER)/link_flash_load.ld.tpl $(PYTHON) ./util/structs_periph_gen.py $(PYTHON) util/mcu_gen.py --config $(X_HEEP_CFG) --cfg_peripherals $(MCU_CFG_PERIPHERALS) --pads_cfg $(PAD_CFG) --outdir hw/fpga/ --bus $(BUS) --memorybanks $(MEMORY_BANKS) --memorybanks_il $(MEMORY_BANKS_IL) --tpl-sv hw/fpga/sram_wrapper.sv.tpl $(PYTHON) util/mcu_gen.py --config $(X_HEEP_CFG) --cfg_peripherals $(MCU_CFG_PERIPHERALS) --pads_cfg $(PAD_CFG) --outdir hw/fpga/scripts/ --bus $(BUS) --memorybanks $(MEMORY_BANKS) --memorybanks_il $(MEMORY_BANKS_IL) --tpl-sv hw/fpga/scripts/generate_sram.tcl.tpl @@ -146,7 +150,15 @@ verible: ## @param COMPILER_PREFIX=riscv32-unknown-(default) ## @param ARCH=rv32imc(default), app: clean-app - $(MAKE) -C sw PROJECT=$(PROJECT) TARGET=$(TARGET) LINKER=$(LINKER) COMPILER=$(COMPILER) COMPILER_PREFIX=$(COMPILER_PREFIX) ARCH=$(ARCH) SOURCE=$(SOURCE) + $(MAKE) -C sw PROJECT=$(PROJECT) TARGET=$(TARGET) LINKER=$(LINKER) LINK_FOLDER=$(LINK_FOLDER) COMPILER=$(COMPILER) COMPILER_PREFIX=$(COMPILER_PREFIX) ARCH=$(ARCH) SOURCE=$(SOURCE) \ + || { \ + echo "\033[0;31mHmmm... seems like the compilation failed...\033[0m"; \ + echo "\033[0;31mIf you do not understand why, it is likely that you either:\033[0m"; \ + echo "\033[0;31m a) offended the Leprechaun of Electronics\033[0m"; \ + echo "\033[0;31m b) forgot to run make mcu-gen\033[0m"; \ + echo "\033[0;31mI would start by checking b) if I were you!\033[0m"; \ + exit 1; \ + } ## Just list the different application names available app-list: diff --git a/README.md b/README.md index 7d2a32734..1a5394645 100644 --- a/README.md +++ b/README.md @@ -12,7 +12,7 @@ and you can focus on building your special HW supported by the microcontroller. The block diagram below shows the `X-HEEP` MCU -

+

> :bookmark_tabs: Please refer to the documentation in [Read the Docs](https://x-heep.readthedocs.io/en/latest/index.html) diff --git a/block_diagrams/core_v_mini_mcu.svg b/block_diagrams/core_v_mini_mcu.svg deleted file mode 100644 index dbdc7cecf..000000000 --- a/block_diagrams/core_v_mini_mcu.svg +++ /dev/null @@ -1,3553 +0,0 @@ - -image/svg+xml - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -MMMSSSMSSSSSSMSSS_FLASHM_FLASHSSMMMSSSSSSSSSSSSSSSSPOWERMANAGERTIMER 2TIMER 3DMABOOT ROMSOC CTRLSPI HOSTTIMER 0TIMER 1SPISUBSYSTEMUARTGPIO AOFAST INTRCTRLPLICPERIPHERAL SUBSYSTEMAO PERIPHERAL SUBSYSTEMI2CGPIOBUS SUBSYSTEMCPUSUBSYSTEMMEMORY SUBSYSTEMCORE_V_MINI_MCUDEBUGSUBSYSTEMRAM 0INSTRDATASSRAM 1RAM 2RAM 3TCKTMSTDIRAM 4RAM 5RAM 6RAM 7TDOTRSTEXT PERIPHBOOTSELECTEXECUTEFROMFLASHEXTMASTERSCORE INSTRIO[7:0]TXRXSCK_FCS_FSD_FSDCSSCKIO[31:8]SDASCLEXIT VALUEEXIT VALIDMPAD PERIPHMMMMMCORE DATADEBUGDMA READDMA WRITEMDMA ADDR diff --git a/block_diagrams/x-heep.svg b/block_diagrams/x-heep.svg deleted file mode 100644 index 93b2ed0d8..000000000 --- a/block_diagrams/x-heep.svg +++ /dev/null @@ -1,2461 +0,0 @@ - -image/svg+xml - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -CORE_V_MINI_MCUPAD CONTROLX-HEEPPADRINGTXRXSCKCSSDSCK_FEXIT VALIDEXIT VALIDCS_FSD_FIO[29:0]IO[30]IO[31]MSSCLSDATXRXSCK_FCS_FSD_FSCKCSSDIO[29:0]SDASCLBOOTSELECTEXECUTEFROMFLASHEXIT VALUE diff --git a/configs/ci.hjson b/configs/ci.hjson index 02ea10d82..51a278b7f 100644 --- a/configs/ci.hjson +++ b/configs/ci.hjson @@ -13,11 +13,11 @@ name: code start: 0 #minimum size for freeRTOS and clang - size: 0x00000C800 + size: 0x00000D800 }, { name: data - start: 0x00000C800 + start: 0x00000D800 } ] } \ No newline at end of file diff --git a/configs/example_interleaved.hjson b/configs/example_interleaved.hjson index e831d7978..aac0801e5 100644 --- a/configs/example_interleaved.hjson +++ b/configs/example_interleaved.hjson @@ -30,11 +30,11 @@ name: code start: 0 // minimum size for freeRTOS and clang - size: 0x00000C800 + size: 0x00000D800 }, { name: data - start: 0x00000C800 + start: 0x00000D800 } ] } \ No newline at end of file diff --git a/configs/general.hjson b/configs/general.hjson index d3465c500..f70406401 100644 --- a/configs/general.hjson +++ b/configs/general.hjson @@ -14,11 +14,11 @@ name: code start: 0 #minimum size for freeRTOS and clang - size: 0x00000C800 + size: 0x00000D800 }, { name: data - start: 0x00000C800 + start: 0x00000D800 } ] } \ No newline at end of file diff --git a/configs/testall.hjson b/configs/testall.hjson index 07a96b645..36828dc81 100644 --- a/configs/testall.hjson +++ b/configs/testall.hjson @@ -13,11 +13,11 @@ name: code start: 0 #minimum size for freeRTOS and clang - size: 0x00000C800 + size: 0x00000D800 }, { name: data - start: 0x00000C800 + start: 0x00000D800 } ] } \ No newline at end of file diff --git a/core-v-mini-mcu.core b/core-v-mini-mcu.core index 8dac12332..aef1f20e2 100644 --- a/core-v-mini-mcu.core +++ b/core-v-mini-mcu.core @@ -30,6 +30,7 @@ filesets: - x-heep:obi_spimemio:0.1.0 - x-heep:ip:boot_rom - x-heep:ip:dma + - x-heep:ip:dma_subsystem - x-heep:ip:i2s - x-heep:ip:power_manager - x-heep:ip:fast_intr_ctrl @@ -84,6 +85,7 @@ filesets: - hw/ip/boot_rom/boot_rom.vlt - hw/ip/obi_spimemio/obi_spimemio.vlt - hw/ip/dma/dma.vlt + - hw/ip/dma_subsystem/dma_subsystem.vlt - hw/ip/pdm2pcm/pdm2pcm.vlt - hw/ip_examples/pdm2pcm_dummy/pdm2pcm_dummy.vlt - hw/ip/power_manager/power_manager.vlt @@ -235,6 +237,10 @@ parameters: datatype: bool paramtype: vlogdefine default: false + FPGA_SYNTHESIS: + datatype: bool + paramtype: vlogdefine + default: false FPGA_NEXYS: datatype: bool paramtype: vlogdefine @@ -419,6 +425,7 @@ targets: - X_EXT - SYNTHESIS=true - REMOVE_OBI_FIFO + - FPGA_SYNTHESIS=true - FPGA_NEXYS=true tools: vivado: @@ -440,6 +447,7 @@ targets: - X_EXT - SYNTHESIS=true - REMOVE_OBI_FIFO + - FPGA_SYNTHESIS=true tools: vivado: part: xc7z020clg400-1 @@ -460,6 +468,7 @@ targets: - X_EXT - SYNTHESIS=true - REMOVE_OBI_FIFO + - FPGA_SYNTHESIS=true - FPGA_ZCU104=true tools: vivado: diff --git a/core-v-mini-mcu.dc.upf.tpl b/core-v-mini-mcu.dc.upf.tpl new file mode 100644 index 000000000..b08abad80 --- /dev/null +++ b/core-v-mini-mcu.dc.upf.tpl @@ -0,0 +1,159 @@ +upf_version 2.1 + +set_design_top core_v_mini_mcu +set_scope . + + +<%text> +##################### +## POWER DOMAINS ## +##################### +\ + +create_power_domain PD_TOP -include_scope +create_power_domain PD_CPU -elements {cpu_subsystem_i} +create_power_domain PD_PERIP_SUBS -elements {peripheral_subsystem_i} +% for bank in xheep.iter_ram_banks(): +create_power_domain PD_MEM_BANK_${bank.name()} -elements {memory_subsystem_i/ram${bank.name()}_i} +% endfor + + +<%text> +#################### +## POWER STATES ## +#################### +\ + +add_power_state PD_TOP.primary -state TOP_ON <%text>\ + {-supply_expr {power == `{FULL_ON, 1.2} && ground == `{FULL_ON, 0.0}}} + +add_power_state PD_CPU.primary -state CPU_ON <%text>\ + {-supply_expr {power == `{FULL_ON, 1.2} && ground == `{FULL_ON, 0.0}}} + +add_power_state PD_CPU.primary -state CPU_OFF <%text>\ + {-supply_expr {power == `{OFF} && ground == `{FULL_ON, 0.0}}} -simstate CORRUPT + +add_power_state PD_PERIP_SUBS.primary -state PERIP_SUBS_ON <%text>\ + {-supply_expr {power == `{FULL_ON, 1.2} && ground == `{FULL_ON, 0.0}}} + +add_power_state PD_PERIP_SUBS.primary -state PERIP_SUBS_OFF <%text>\ + {-supply_expr {power == `{OFF} && ground == `{FULL_ON, 0.0}}} -simstate CORRUPT + +% for bank in xheep.iter_ram_banks(): +add_power_state PD_MEM_BANK_${bank.name()}.primary -state MEM_BANK_${bank.name()}_ON <%text>\ + {-supply_expr {power == `{FULL_ON, 1.2} && ground == `{FULL_ON, 0.0}}} + +add_power_state PD_MEM_BANK_${bank.name()}.primary -state MEM_BANK_${bank.name()}_OFF <%text>\ + {-supply_expr {power == `{OFF} && ground == `{FULL_ON, 0.0}}} -simstate CORRUPT + +% endfor + +<%text> +################### +## SUPPLY NETS ## +################### +\ + +create_supply_port VDD -direction in +create_supply_port VSS -direction in + +create_supply_net VDD +create_supply_net VSS + +connect_supply_net VDD -ports VDD +connect_supply_net VSS -ports VSS + +create_supply_set PD_TOP.primary -function {power VDD} -function {ground VSS} -update + +create_supply_net VDD_CPU +create_supply_set PD_CPU.primary -function {power VDD_CPU} -function {ground VSS} -update + +create_supply_net VDD_PERIP_SUBS +create_supply_set PD_PERIP_SUBS.primary -function {power VDD_PERIP_SUBS} -function {ground VSS} -update + +% for bank in xheep.iter_ram_banks(): +create_supply_net VDD_MEM_BANK_${bank.name()} +create_supply_set PD_MEM_BANK_${bank.name()}.primary -function {power VDD_MEM_BANK_${bank.name()}} -function {ground VSS} -update + +% endfor + +<%text> +################ +## SWITCHES ## +################ +\ + +create_power_switch switch_PD_CPU <%text>\ + -supply_set PD_TOP.primary <%text>\ + -domain PD_CPU <%text>\ + -input_supply_port {sw_in VDD} <%text>\ + -output_supply_port {sw_out VDD_CPU} <%text>\ + -control_port {sw_ctrl ao_peripheral_subsystem_i/cpu_subsystem_pwr_ctrl_o<%text>\[pwrgate_en_n<%text>\]} <%text>\ + -ack_port {sw_ack ao_peripheral_subsystem_i/cpu_subsystem_pwr_ctrl_i<%text>\[pwrgate_ack_n<%text>\]} <%text>\ + -on_state {on_state sw_in {sw_ctrl}} <%text>\ + -off_state {off_state {!sw_ctrl}} + +create_power_switch switch_PD_PERIP_SUBS <%text>\ + -supply_set PD_TOP.primary <%text>\ + -domain PD_PERIP_SUBS <%text>\ + -input_supply_port {sw_in VDD} <%text>\ + -output_supply_port {sw_out VDD_PERIP_SUBS} <%text>\ + -control_port {sw_ctrl ao_peripheral_subsystem_i/peripheral_subsystem_pwr_ctrl_o<%text>\[pwrgate_en_n<%text>\]} <%text>\ + -ack_port {sw_ack ao_peripheral_subsystem_i/peripheral_subsystem_pwr_ctrl_i<%text>\[pwrgate_ack_n<%text>\]} <%text>\ + -on_state {on_state sw_in {sw_ctrl}} <%text>\ + -off_state {off_state {!sw_ctrl}} + +% for bank in xheep.iter_ram_banks(): +create_power_switch switch_PD_MEM_BANK_${bank.name()} <%text>\ + -supply_set PD_TOP.primary <%text>\ + -domain PD_MEM_BANK_${bank.name()} <%text>\ + -input_supply_port {sw_in VDD} <%text>\ + -output_supply_port {sw_out VDD_MEM_BANK_${bank.name()}} <%text>\ + -control_port {sw_ctrl ao_peripheral_subsystem_i/memory_subsystem_pwr_ctrl_o[${bank.name()}]<%text>\[pwrgate_en_n<%text>\]} <%text>\ + -ack_port {sw_ack ao_peripheral_subsystem_i/memory_subsystem_pwr_ctrl_i[${bank.name()}]<%text>\[pwrgate_ack_n<%text>\]} <%text>\ + -on_state {on_state sw_in {sw_ctrl}} <%text>\ + -off_state {off_state {!sw_ctrl}} + +% endfor + +<%text> +################# +## ISOLATION ## +################# +\ + +set_isolation cpu_iso <%text>\ + -domain PD_CPU <%text>\ + -isolation_power_net VDD <%text>\ + -isolation_ground_net VSS <%text>\ + -isolation_signal ao_peripheral_subsystem_i/cpu_subsystem_pwr_ctrl_o<%text>\[isogate_en_n<%text>\] <%text>\ + -isolation_sense low <%text>\ + -clamp_value 0 <%text>\ + -applies_to outputs <%text>\ + -name_prefix cpu_iso_cell <%text>\ + -location parent + +set_isolation perip_subs_iso <%text>\ + -domain PD_PERIP_SUBS <%text>\ + -isolation_power_net VDD <%text>\ + -isolation_ground_net VSS <%text>\ + -isolation_signal ao_peripheral_subsystem_i/peripheral_subsystem_pwr_ctrl_o<%text>\[isogate_en_n<%text>\] <%text>\ + -isolation_sense low <%text>\ + -clamp_value 0 <%text>\ + -applies_to outputs <%text>\ + -name_prefix cpu_iso_cell <%text>\ + -location parent + +% for bank in xheep.iter_ram_banks(): +set_isolation mem_bank_${bank.name()}_iso <%text>\ + -domain PD_MEM_BANK_${bank.name()} <%text>\ + -isolation_power_net VDD <%text>\ + -isolation_ground_net VSS <%text>\ + -isolation_signal ao_peripheral_subsystem_i/memory_subsystem_pwr_ctrl_o[${bank.name()}]<%text>\[isogate_en_n<%text>\] <%text>\ + -isolation_sense low <%text>\ + -clamp_value 0 <%text>\ + -elements {memory_subsystem_i/ram${bank.name()}_i/rdata_o} <%text>\ + -name_prefix cpu_iso_cell <%text>\ + -location parent + +% endfor diff --git a/core-v-mini-mcu.upf.tpl b/core-v-mini-mcu.upf.tpl index 378a74a68..d7e0f6f1f 100644 --- a/core-v-mini-mcu.upf.tpl +++ b/core-v-mini-mcu.upf.tpl @@ -109,8 +109,8 @@ create_power_switch switch_PD_MEM_BANK_${bank.name()} <%text>\ -domain PD_MEM_BANK_${bank.name()} <%text>\ -input_supply_port {sw_in VDD} <%text>\ -output_supply_port {sw_out VDD_MEM_BANK_${bank.name()}} <%text>\ - -control_port {sw_ctrl memory_subsystem_banks_powergate_switch_no[${bank.name()}]} <%text>\ - -ack_port {sw_ack memory_subsystem_banks_powergate_switch_ack_ni[${bank.name()}]} <%text>\ + -control_port {sw_ctrl memory_subsystem_banks_powergate_switch_n[${bank.name()}]} <%text>\ + -ack_port {sw_ack memory_subsystem_i.ram${bank.name()}_i.pwrgate_ack_no} <%text>\ -on_state {on_state sw_in {sw_ctrl}} <%text>\ -off_state {off_state {!sw_ctrl}} @@ -152,7 +152,7 @@ set_isolation mem_bank_${bank.name()}_iso <%text>\ -isolation_signal memory_subsystem_banks_powergate_iso_n[${bank.name()}] <%text>\ -isolation_sense low <%text>\ -clamp_value 0 <%text>\ - -applies_to outputs <%text>\ + -elements {memory_subsystem_i/ram${bank.name()}_i/rdata_o} <%text>\ -name_prefix cpu_iso_cell <%text>\ -location parent diff --git a/docs/source/How_to/AnalogMixedSignal.md b/docs/source/How_to/AnalogMixedSignal.md index 841b8a882..5e2d0bb4e 100644 --- a/docs/source/How_to/AnalogMixedSignal.md +++ b/docs/source/How_to/AnalogMixedSignal.md @@ -68,7 +68,7 @@ The example AMS peripheral used by simulations of X-HEEP is located in `hw/ip_ex ### The repository's example SPICE files -

+

An example `adc.sp` file can be found in `hw/ip_examples/ams/analog`. This is a 1-bit ADC with a threshold that is configured through the 2-bit wide SEL input: an input of 00, 01, 10 and 11 will provide a threshold of 20%, 40%, 60% and 80% of VDD (1.2V) respectively. The input signal of the ADC is a sine wave with a peak-to-peak amplitude of 1.2V directly placed inside the SPICE netlist. diff --git a/docs/source/How_to/eXtendingHEEP.md b/docs/source/How_to/eXtendingHEEP.md index 0f875a5f1..59c1b1e98 100644 --- a/docs/source/How_to/eXtendingHEEP.md +++ b/docs/source/How_to/eXtendingHEEP.md @@ -401,3 +401,10 @@ If you plan to store source files in a different location that the one proposed, make app PROJECT=your_app SOURCE= ``` Consider that inside this `sw` folder the same structure than the one proposed is required. + + +## Inter-process communication using Verilator's DPI + +The following [repository](https://github.com/specs-feup/x-heep) uses X-HEEP and the Verilator simulator to model a CPU-CGRA hybrid system. This architecture simulates the CPU integrated into the X-HEEP system, and an external Java process simulates the accelerator. Both components require a communication channel to exchange instructions and data. Using the existing infrastructure to to interact with an external OS process is not feasible at first sight, given that the X-HEEP ecosystem's pipeline encapsulates most of the simulation build and execution, with all modules supplied directly to Verilator. + +To circumvent this issue, this project uses [Direct Programming Interface (DPI)](https://verilator.org/guide/latest/connecting.html) calls (defined in `hw/ip_examples/cgraitf/cgraitfdpi.c`) to establish a connection and communicate with an external process through a Unix Domain Socket. This behavior mirrors the UART module (used as the skeleton code) that connects and outputs _printf_ information to the pseudo-terminal. These calls are embedded in a mock CGRA peripheral/interface, located in `hw/ip_examples/cgraitf/cgraitf.sv`. The module overrides reads and writes to the specified peripheral address, with the proper socket-based mechanism (_send_ or _recv_). The _simple_accelerator_ module could also be similarly customized to perform the same operations, using X-HEEP's interfaces and memory access protocols. A given user program executed in the CPU (such as `sw/applications/cgra_itf/main.c`) must then select assignments to or from the address to trigger the appropriate action. \ No newline at end of file diff --git a/docs/source/Peripherals/DMA.md b/docs/source/Peripherals/DMA.md index 2838a83bf..6b57642f2 100644 --- a/docs/source/Peripherals/DMA.md +++ b/docs/source/Peripherals/DMA.md @@ -1,176 +1,225 @@ + # DMA The **Direct Memory Access (DMA)** peripheral allows data transfers with reduced CPU interaction. It can perform *transactions* of data between peripherals and memory, or memory-to-memory (as a `memcpy` would). The CPU is required to configure the transaction, but once launched it is free to go to sleep, process the incoming data or do anything else. - + The DMA **Hardware Abstraction Layer (HAL)** facilitates the configuration of transactions from the users application. Furthermore, it adds an additional layer of safety checks to reduce the risk of faulty memory accesses, data override or infinite loops. - - + + ## Previous Definitions The implementation of this software layer introduced some concepts that need to be understood in order to make proper use of the DMA's functionalities . - + ### Transaction -A transaction is an operation to be performed by the DMA. It implies copying bytes from a source pointer into a destination pointer. The transaction configuration can be loaded into the DMA registers once it has been cross-checked and it only starts when the size of the transaction is written in its corresponding register. The transaction is finished once the DMA has sent all its bytes (which not necessarily means they have been received by the final destination). +A transaction is an operation to be performed by the DMA. It implies copying bytes from a source pointer into a destination pointer. The transaction configuration can be loaded into the DMA registers once it has been cross-checked and it only starts when the size along the *first dimension* of the transaction is written in its corresponding register. The transaction is finished once the DMA has sent all its bytes (which not necessarily means they have been received by the final destination). Transactions cannot be stopped once they were launched. While a transaction is running, new transactions can be validated, but not launched or loaded into the DMA. Transactions can be re-launched automatically in `circular mode`. Once the transaction has finished, a status bit is changed (that can be monitored through polling) and a fast interrupt is triggered. - + ### Source and destination Sources and destinations are the two pointers that will exchange data. Bytes will be copied from the source and into the destination address. - + ### Data type The DMA allows transactions in chunks of 1, 2 or 4 Bytes (`Byte`, `Half-Word` and `Word` respectively). The size in bytes of the chosen data type is called _data unit_ (usually abbreviated as `du`). For example, 16 bytes can be 16 data units if the data type is `Byte`, but 8 data units if the data type is `Half Word`. Source and destination can have different data types, if the destination type is wider than the source type, data can be sign extended. - ### Sign extension If specified (setting the bit in the corresponding register) and if the destination data type is wider than the source type, sign of the source data is extended to fill the size of the destination data type. +### Dimensionality +The DMA can perform both **1D transactions** and **2D transactions**. +When set to perform 1D transactions, the DMA copies the defined number of elements from the source pointer to the destination pointer using a 1D increment. +When set to perform 2D transactions, the DMA copies data from the source pointer to the destination pointer using both a 1D increment and an additional 2D increment. +In this way, two-dimensional data manipulations (e.g. matrix manipulations) can be performed in a single DMA transaction, greatly reducing the CPU time. ### Increment -In the case that source and/or destination data are not to be consecutively read/written, a certain increment can be defined. +In the 1D configuration, if source and/or destination data are not to be consecutively read/written, a certain increment can be defined. For instance, if you have an array of 4-bytes-words, but only want to copy the first 2 bytes of each word, you could define the transaction with a data type of half word, an increment of 2 data units in the source, and 1 data unit in the destination. This way, after each read operation the DMA will increment the read pointer in 4 bytes (2 data units), but the write pointer by only 2 bytes. +In the 2D configuration, a second increment has to be set in order to enable the 2D transaction. +In the context of matrix manipulation, the 2D increment is the number of words that the DMA has to "skip" to move to the next row. +For instance, let's examine the extraction of a continuous 2x2 matrix from a 4x4 matrix, stored in a continuous 2x2 matrix. +``` +| 3 | 5 | 7 | 9 | +| 2 | 4 | 6 | 8 | -> | 3 | 5 | +| 1 | 3 | 5 | 7 | | 2 | 4 | +| 0 | 2 | 4 | 6 | +``` +The total number of elements copied from the source is 4 elements. The increments are: + - For the source: + - 1D increment set to 1 word + - 2D increment set to 3 words + - For the destination: + - 1D increment set to 1 word + - 2D increment set to 1 word +By exploiting the 2D increment, it's possible to implement a non-continuous read and/or write by computing the correct 2D increment. Detailed formulas for the computation of 2D increments are reported in the *example_dma_2d* folder. + +### Zero padding + +The DMA is capable of performing zero padding on the extracted data, both in 1D and 2D transactions. +This is done by by setting four padding parameters: +- **T**op +- **B**ottom +- **L**eft +- **R**ight + +e.g. + +``` +| T | T | T | T | T | +| L | x | x | x | R | +| L | x | x | x | R | +| B | B | B | B | B | +``` +It's important to highlight the fact that the padding is performed (conceptually) only *after* the matrix has been extracted. +Let's examine the previous 2x2 extraction example, adding a left and top padding of 1 word: + +``` +| 3 | 5 | 7 | 9 | | 0 | 0 | 0 | +| 2 | 4 | 6 | 8 | -> | 0 | 3 | 5 | +| 1 | 3 | 5 | 7 | | 0 | 2 | 4 | +| 0 | 2 | 4 | 6 | +``` ### Alignment When doing transactions with bytes, the DMA can read/write from any pointer. However, if the data type is larger, words should be aligned so the DMA can perform a read/write operation and affect only the chosen bytes. If a word or half-word's pointer is not a multiple of 4 or 2 (respectively), the pointer is _misaligned_. In some cases the DMA HAL can overcome this problem reducing the data type (which will reflect on an efficiency loss). - + ### Environment An environment is a region of memory that can optionally be defined by the user to let the HAL know that it is allowed to read/write on that region. It is useful to make sure the DMA will not affect reserved memory regions. Right now, read and write permissions are not supported by environments, meaning that if it is defined, the DMA will be able to read AND write on it. - + ### Triggers and Slots If the source or destination pointer is a peripheral, there are lines connecting the peripheral and the DMA that can be used to control the data flow (they behave as _triggers_). These lines are connected to _slots_ on the DMA and they allow/stop the DMA from reading/writing data. - + ### Target A target is either a region of memory or a peripheral to which the DMA will be able to read/write. When targets are pointing to memory, they can be assigned an environment to make sure that they will comply with memory restrictions. Targets include a pointer (a point in the memory, or the Rx/Tx buffer in case of peripherals), a size to be copied (if its going to be used as a source), a data type and an increment. - + ### Configuration flags During the creation or configuration of environments, targets or transactions, there could be inconsistencies or threatening situations (like writing outside the boundaries of a defined region). To provide the user with information about this potentially harmful situations, configuration flags are set while creating each of these entities. They can be unmasked and checked. In some cases, when the threat is too risky, a _crucial error_ might be raised and the operation of the configuration is halted. If senseless configurations are input to functions, assertions may halt the whole program. This is reserved for extreme situations that mean the program was not properly coded (e.g. a slot value is provided and is not among the available ones). - + ### Transaction modes There are three different transaction modes: **Single Mode:** The default mode, where the DMA will perform the copy from the source target to the destination, and trigger an interrupt once done. **Circular mode:** To take full advantage of the speed and transparency of the DMA, a _circular_ mode was implemented. When selected, the DMA will relaunch the exactly same transaction upon finishing. This cycle only stops if by the end of a transaction the _transaction mode_ was changed to _single_. The CPU receives a fast interrupt on every transaction finished. -**Address Mode:** Instead of using the destination pointer and increment to decide where to copy information, an _address list_ must be provided, containing addresses for each data unit being copied. It is only carried out in _single_ mode. - +**Address Mode:** Instead of using the destination pointer and increment to decide where to copy information, an _address list_ must be provided, containing addresses for each data unit being copied. It is only carried out in _single_ mode. +In this mode it's possible to perform only 1D transactions. + ### Windows In order to process information as it arrives, the application can define a _window size_ (smaller than the _transaction size_. Every time the DMA has finished sending that given amount of information will trigger an interrupt through the PLIC. > :warning: If the window size is a multiple of the transaction size, upon finishing the transaction there will be first an interrupt for the whole transaction (through the FIC), and then an interrupt for the window (through the PLIC, which is slower). - - + + ### Checks and Validations The DMA HAL's interface functions perform two types of checks: -* **Sanity checks**: Make sure that each individual value passed as an argument is reasonable and belongs to the proper domain. This errors will raise an _assertion_ and, depending on how assertions are managed in the application, may result in the program crashing. -* **Integrity checks**: Arguments are cross-checked to make sure that they abide by the rules of the DMA. If configurations are incompatible, contradictory, or a risk for the programs integrity, warnings are raised through the _configuration flags_. In some special cases, a _critical error_ can be raised, which blocks the deployment of the configuration into the DMA registers. - +* **Sanity checks**: Make sure that each individual value passed as an argument is reasonable and belongs to the proper domain. This errors will raise an _assertion_ and, depending on how assertions are managed in the application, may result in the program crashing. +* **Integrity checks**: Arguments are cross-checked to make sure that they abide by the rules of the DMA. If configurations are incompatible, contradictory, or a risk for the programs integrity, warnings are raised through the _configuration flags_. In some special cases, a _critical error_ can be raised, which blocks the deployment of the configuration into the DMA registers. + > :warning: Integrity checks can be disabled to speed up configuration time. Do this only if you have previously checked the configuration and are sure it will not cause any trouble. - + Checks and validations are performed during the transactions creation, loading and launching. - + A transaction is validated if it went through the creation-checks without raising critical errors. - + ### End events The DMA considers a certain amount of bytes to have been transferred once it has sent them. It does not wait for a confirmation from the recipient. When a transaction/window is finished the DMA performs a series of event. These may include: * Changing its status register. * Raising a _transaction done_ interrupt. * Raising a _window done_ interrupt. - + The DMA HAL can follow up on these changes or let the application be in charge. For this purpose, three different types of _end events_ are defined: -* **Polling**: The HAL will disable interrupts from the DMA. The application will need to frequently query the status of the DMA to know when a transaction has finished. -* **Interrupt**: Interrupts will be enabled. The _window done interrupt_ is enabled if a window size is provided. -* **Interrupt wait**: The DMA HAL will block the program in a `wfi()` state until the _transaction done interrupt_ is triggered. - - +* **Polling**: The HAL will disable interrupts from the DMA. The application will need to frequently query the status of the DMA to know when a transaction has finished. +* **Interrupt**: Interrupts will be enabled. The _window done interrupt_ is enabled if a window size is provided. +* **Interrupt wait**: The DMA HAL will block the program in a `wfi()` state until the _transaction done interrupt_ is triggered. + + ## Operation This section will explain the operation of the DMA through the DMA HAL. There is a DMA instance inside X-HEEP, but others can be connected outside through the bus (see the `example_external_peripheral` application in `sw/aplications/example_external_peripheral/main.c`). As long as the DMA instance is the same and the registers are memory mapped with the same structure, the DMA HAL can be used. - + The DMA HAL adds an extra computational overhead to transactions in order to check the consistency of the transaction configurations. By-passing this layer (and the steps here described) is disadvised. For the efficiency-hungry applications, doing at least one pass with the whole validation process is recommended. The HAL allows to load and launch transactions with minimum overhead afterwards. - + The following explanation makes use of Figure 1. - + ![DMA HAL-HW + addresses](https://github.com/esl-epfl/x-heep/assets/54960111/3092faa5-c72c-4cd9-a4d4-4c87de63d1c7) -

Figure 1: Example operation of the DMA and its HAL

- +

Figure 1: Example operation of the DMA and its HAL

+ --- - -The use of the DMA starts with the application creating a set of targets (**a**) . In the figure, the source target is a peripheral connected to an SPI and to the DMA. The address of the reception FIFO (_Rx FIFO_) of this SPI is `0x70` (**b**) . The destination target is a region of memory of address `0x16` (**c**). It is located inside an environment that spans from `0x12` to `0x35`. Any number of environments and targets can be created, and not used. - + +The use of the DMA starts with the application creating a set of targets (**a**) . In the figure, the source target is a peripheral connected to an SPI and to the DMA. The address of the reception FIFO (_Rx FIFO_) of this SPI is `0x70` (**b**) . The destination target is a region of memory of address `0x16` (**c**). It is located inside an environment that spans from `0x12` to `0x35`. Any number of environments and targets can be created, and not used. + Additionally, in the application the transaction is created. The _operation mode_ and _window size_ are selected. - -The application calls the validation function of the HAL. If the configurations do not raise a critical flag, it then calls the loading function. By doing so, the desired values are written into their corresponding registers (**d**). The only register that is not immediately written is the _transaction size_, as it is the one responsible for launching the transaction when changed from zero to a non-zero value. It is only written once the application calls the launching function (**e**). - + +The application calls the validation function of the HAL. If the configurations do not raise a critical flag, it then calls the loading function. By doing so, the desired values are written into their corresponding registers (**d**). The only register that is not immediately written is the _transaction size_, as it is the one responsible for launching the transaction when changed from zero to a non-zero value. It is only written once the application calls the launching function (**e**). + Note that there could be some changes between the configuration input in the application and the values written in the registers. For example, a _window size_ of 2 refers to _2 data units_ (i.e. 2 half-words, as such is the data type of the source in Figure 1); however, when writing on the register, this is translated to bytes, so 4 is written instead. - -Once launched, the transaction will execute completely (it cannot be stopped). Upon finishing, it will check the _operation mode_ and _transaction size_ registers. If any of both is non-zero, it will relaunch. Note that the selecting _circular mode_ during the configuration loading does not launch the transaction (despite what step (**f**) might suggest, which is only illustrative). - - -Once the transaction is launched, the DMA will take care of copying as many data units as were requested from the source target (**g**), and pasting them into the destination target (**h**). The data width of the transaction is determined by the _data type_ of the source target (**i**). However, this might be changed (for a smaller width) in case of misalignment. It is possible to reject changes by the DMA HAL and raise an error in case of misalignments instead. - -The selected slot will query the state of a trigger from the peripheral (**j**). In case the peripheral's FIFO is empty/full (for reception/transmission respectively), the transaction is paused until the trigger enables it again. - + +Once launched, the transaction will execute completely (it cannot be stopped). Upon finishing, it will check the _operation mode_ and _transaction size_ registers. If any of both is non-zero, it will relaunch. Note that the selecting _circular mode_ during the configuration loading does not launch the transaction (despite what step (**f**) might suggest, which is only illustrative). + + +Once the transaction is launched, the DMA will take care of copying as many data units as were requested from the source target (**g**), and pasting them into the destination target (**h**). The data width of the transaction is determined by the _data type_ of the source target (**i**). However, this might be changed (for a smaller width) in case of misalignment. It is possible to reject changes by the DMA HAL and raise an error in case of misalignments instead. + +The selected slot will query the state of a trigger from the peripheral (**j**). In case the peripheral's FIFO is empty/full (for reception/transmission respectively), the transaction is paused until the trigger enables it again. + The source and destination increments will determine the amount of steps the pointer should jump after every read and write, respectively. For peripherals it should always be zero (as it should always take the first element of the FIFO). - -The DMA will consider a data unit was transferred once it is sent (**k**). It does not wait for an acknowledge by the destination target. - -The _interrupts_ register controls whether the events triggered by the DMA upon finishing a window or a whole transaction should be propagated as interrupts (**l**). - -Every time _window size_ data units have been transferred the window count is incremented and a PLIC interrupt is triggered (if enabled) (**m**). - -Every time a transaction is finished a FIC interrupt is triggered and the restart condition is evaluated (**f**). - + +The DMA will consider a data unit was transferred once it is sent (**k**). It does not wait for an acknowledge by the destination target. + +The _interrupts_ register controls whether the events triggered by the DMA upon finishing a window or a whole transaction should be propagated as interrupts (**l**). + +Every time _window size_ data units have been transferred the window count is incremented and a PLIC interrupt is triggered (if enabled) (**m**). + +Every time a transaction is finished a FIC interrupt is triggered and the restart condition is evaluated (**f**). + If the window size is a multiple of the transaction size, upon finishing the transaction there will be first an interrupt for the whole transaction (through the FIC), and then an interrupt for the window (through the PLIC, which is slower). - - -The DMA HAL has weak implementations to handle each interrupt. It is up to the application to do something useful with this. the HAL will only forward the interrupt (**n**). - + + +The DMA HAL has weak implementations to handle each interrupt. It is up to the application to do something useful with this. the HAL will only forward the interrupt (**n**). + > :warning: If the window size is too small (i.e. the time the DMA requires to make the copy is smaller than the time required to attend the interrupt handling), data might be lost. The HAL implements a warning in case the transaction-window size ratio is to small. The warning can be re-configured to an appropriate threshold by overriding a weak implementation. The user should do its own testing and choose this threshold accordingly. The same type of issue might be found on circular mode. The user should be sure it can attend an interrupt before the next one occurs. There is no warning from the HAL in this case. - -The same result from this example could have been achieved by setting the transaction mode to _address_. It requires an array of destination addresses (**p**) that must be provided as the destination target pointer. Instead of copying information to that pointer, the DMA will read from there and copy the information into the addresses stored in each word (**o**). + +The same result from this example could have been achieved by setting the transaction mode to _address_. It requires an array of destination addresses (**p**) that must be provided as the destination target pointer. Instead of copying information to that pointer, the DMA will read from there and copy the information into the addresses stored in each word (**o**). This use case is very impractical as it doubles the memory usage. It is intended to be used along In-Memory-Computing architectures and algorithms. - + ## Usage This section will explain a basic usage of the DMA as a `memcpy`, and a slightly more complex situation involving a peripheral connected via an SPI. - + ### Basic application This example will provide a simplified code for copying data from one region of memory to another. For a real implementation please refer to the `dma_example` application in `sw/applications/dma_example/main.c`. - + The objective of this app would be to copy the content of an array into another. - + --- - + Start the application by calling ```C dma_init( NULL ); ``` - + This will reset the DMA registers. The `NULL` parameter tells the HAL that the devices internal DMA is to be used. - + The most basic implementation requires the creation of **two targets** and a **transaction** relating them both. - + A target will include the information of the source or destination pointer and the characteristics of the expected transaction. - + Theoretically, there is no required difference between a _source_ and _destination_ target. They will only be differentiated once the transaction is created. Eventually, they could be interchanged from one transaction to the next. In this example they will be given explicit names and will take different arguments for the sake of clarity. - + ```C static dma_target_t tgt_src = { - .ptr = copy_buffer, - .inc_du = 1, - .size_du = sizeof(copy_buffer), - .type = DMA_DATA_TYPE_WORD - }; + .ptr = copy_buffer, + .inc_du = 1, + .size_du = sizeof(copy_buffer), + .type = DMA_DATA_TYPE_WORD + }; ``` - + Here, `ptr = copy_buffer` is a `uint32_t` pointer from where the information will be extracted. `inc_du = 1` is telling the DMA that for each word copied, the pointer should be incremented by 1 unit, therefore words will be copied consecutively without gaps. - + This configuration is implicitly initializing the rest of the target configurations as zero (as of [C99](https://gcc.gnu.org/onlinedocs/gcc/Designated-Inits.html)). This means that: * No environment is set. * Data type is set to _word_ (32-bits). * The trigger is set to _memory_ (vs. a peripheral). - ```C static dma_target_t tgt_dst = { .ptr = copy_buffer, @@ -179,156 +228,149 @@ static dma_target_t tgt_dst = { .type = DMA_DATA_TYPE_WORD }; ``` - -Both destination and source targets has to contain a data type (they can be different) and size in data units (they should be the same). - +Both destination and source targets have to contain a data type (they can be different) and size in data units (they should be the same). Finally, a transaction is created to relate both targets: - + ```C -static dma_trans_t trans = { - .src = &tgt_src, - .dst = &tgt_dst, - }; +static dma_trans_t trans = { + .src = &tgt_src, + .dst = &tgt_dst, + }; ``` - + This will also imply some configurations set to zero: * Mode is set to singular (only one transaction will be executed). * There will be no window interrupts. * The application will have to do polling to know when the transaction has finished. - + The transaction can now be created (some extra configurations are computed from the targets), loaded into the DMA and launched. - + ```C dma_validate_transaction( &trans, DMA_ENABLE_REALIGN, DMA_PERFORM_CHECKS_INTEGRITY ); dma_load_transaction( &trans ); dma_launch( &trans ); - + ``` - + When creating the transaction, the two last parameters are allowing the DMA to perform integrity checks and try some fixes (at the expense of efficiency) if misalignments are found. - + As there will be no interrupts set, the application has to check by itself the status of the transaction. - + ```C while( ! dma_is_ready() ){} // The transaction has finished! ``` - - + + ### Complete Application The objective of this example is to show various special cases, precautions and considerations that can be taken. For a real example refer to the `spi_flash_write` application in `sw/applications/spi_flash_write/main.c`. - + This example will copy every other byte from a buffer in RAM to a FLASH connected via an SPI to the DMA. Then, it will copy information from an SPI peripheral continuously into a small buffer. - + This explanation assumes you have read the previous example. - + --- - + Start the application by calling ```C dma_init( NULL ); ``` - + The first source target will be pointing to a `uint16_t` buffer in memory. Data will be copied in chunks of 1 byte. For every half-word (16 bits), the only the second half (second 8 bits) will be copied. - + ```C -static dma_target_t tgt1= { - .ptr = (uint8_t*)(copy_buffer + 1), - .inc_du = 2, - .size_du = sizeof(copy_buffer), - .type = DMA_DATA_TYPE_BYTE, -}; - +static dma_target_t tgt1= { + .ptr = (uint8_t*)(copy_buffer + 1), + .inc_du = 2, + .size_du = sizeof(copy_buffer), + .type = DMA_DATA_TYPE_BYTE, + }; ``` - + To start copying information from the second byte of `copy_buffer`, the source pointer is set to the address of `copy_buffer` plus one byte. In order to copy one byte and skip another, the data type is set to byte, and the increment is set to two data units (i.e. two bytes). As for every half-word of the buffer a data unit will be copied, the transaction size is the same as the size of the `copy_buffer`. - - + + The second target will point to the address of the SPI FLASH transmission FIFO. On this example, this value was already computed and stored in a constant `ADDRESS_SPI_FLASH_TX_FIFO`. - - + + ```C -static uint32_t *spi_flash_fifo_tx = ADDRESS_SPI_FLASH_TX_FIFO; - - -static dma_target_t tgt2= { - .inc_du = 0, - .trig = DMA_TRIG_SLOT_SPI_FLASH_TX, -}; - +static uint32_t *spi_flash_fifo_tx = ADDRESS_SPI_FLASH_TX_FIFO; + + +static dma_target_t tgt2= { + .inc_du = 0, + .trig = DMA_TRIG_SLOT_SPI_FLASH_TX, + }; tgt2.ptr = spi_flash_fifo_tx; - ``` - + Because structure initializers need to be constants, the pointer value (which is a non-constant variable) needs to be initialized outside the designated initializer. - - + + There is no need to assign a value of transaction size or data type. By default, the DMA will use the source's values. The data type could eventually be modified by the HAL if there is a misalignment and `DMA_ENABLE_REALIGN` is set when creating the transaction. - + The increment needs to be set to zero as the pointer should always be set to the FIFO address. - + Because the SPI FLASH transmission FIFO has a line connected to slot number 4 (codified as a `1` in the fourth bit of the `trig` element) to let the DMA know if the FIFO is full, a trigger is set in that position by passing `.trig = DMA_TRIG_SLOT_SPI_FLASH_TX`. - - + + The transaction is formed by selecting the source and destination targets - + ```C -static dma_trans_t trans = { - .src = &tgt1, - .dst = &tgt2, - .end = DMA_TRANS_END_INTR, - }; +static dma_trans_t trans = { + .src = &tgt1, + .dst = &tgt2, + .end = DMA_TRANS_END_INTR, + }; ``` - + To enable interrupts, the end event is set to `DMA_TRANS_END_INTR`. - - + + To create the transaction allowing the DMA to perform necessary realignments and integrity checks such arguments are passed to the creation function along with a pointer to the transaction. - + ```C dma_config_flags_t res; res = dma_validate_transaction( &trans, DMA_ENABLE_REALIGN, DMA_PERFORM_CHECKS_INTEGRITY ); ``` - + The result variable `res` contains configuration flags that can be used to check that no errors or warnings were raised. These are codified as bits in the 16-bit `res` variable. If only one flag was raised, the value of `res` will be equal to it. Otherwise, they can be checked by selecting individual bits, with inequalities, or comparing the result to a bitwise-or of flags. - + ```C if( res == DMA_CONFIG_OK ) // Everything was ok. if( res & DMA_CONFIG_CRITICAL_ERROR ) // A critical error was found. It has to be fixed or the transaction will not be loaded. if( res >= DMA_CONFIG_OVERLAP && res =< DMA_CONFIG_TRANS_OVERRIDE ) // One or more warning flags were raised between those two. if( res & ( DMA_CONFIG_SRC | DMA_CONFIG_DST) ) // Either one of the flags was raised. ``` - + The transaction can then be loaded into the DMA registers by calling the load function ```C res = dma_load_transaction( &trans ); ``` - + and launched ```C res = dma_load_transaction( &trans ); ``` - + Because the DMA will raise an interrupt as soon as it has sent all of its information, it is recommended to wait for the SPI interrupt. ```C -while( spi_intr_flag == 0 ) -{ - wait_for_interrupt(); +while( spi_intr_flag == 0 ) { + wait_for_interrupt(); } ``` - + If something is to be done as soon as the DMA finishes (like preparing a new transaction) it can be triggered by the interrupt attention routine: ```C -void dma_intr_handler_trans_done() -{ - // Raise a flag to trigger an action +void dma_intr_handler_trans_done() { +// Raise a flag to trigger an action } ``` This function is a strong implementation defined in the application of the weak one in `dma.c`. - - + + During this transaction * The DMA grabbed the first byte of from the pointer (i.e. the second of the buffer, letter `B` in the chart below). * Sent it to the SPI, where it will be treated as a 32-bit word. @@ -336,216 +378,212 @@ During this transaction * The source pointer was increased by two data units (i.e. two bytes) to point letter `D` from the chart. * The DMA grabbed that byte and sent it to the SPI. * The process went on until the the last byte (letter `L`) is sent to the SPI. - - - - +
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
RAMFLASH
ABCD000B
EFGH000D
IJKL000F
........
RAMFLASH
ABCD000B
EFGH000D
IJKL000F
........
- - + + --- - - + + For the second part of this example, the targets will be modified and their role switched. - + The source of the data will be a peripheral connected to the SPI, which will be continuously sending information in half-word format, in streams of 4096 bytes (2048 half-words). Every 1024 half-words there should be a `MILESTONE-CHARACTER`, if there is not, the data flow should be stopped. - + ```C -tgt2.ptr = spi_peripheral_fifo_rx; -tgt2.size_du = 2048; -tgt2.type = DMA_DATA_TYPE_HALF_WORD; -tgt2.trig = DMA_TRIG_SLOT_SPI_RX; +tgt2.ptr = spi_peripheral_fifo_rx; +tgt2.size_du = 2048; +tgt2.type = DMA_DATA_TYPE_HALF_WORD; +tgt2.trig = DMA_TRIG_SLOT_SPI_RX; ``` - + The source pointer is set to the reception FIFO of the SPI, and the appropriate slot is chosen. The increment is kept in zero. - + The destination pointer will be given by a function. - + ```C -tgt1.ptr = (uint8_t*) myApp_getDestination(); +tgt1.ptr = (uint8_t*) myApp_getDestination(); ``` - + To guarantee that the result of this function will not cause the DMA to write in undesired regions of memory, an environment can be created. It requires two pointers to the first and last byte where the DMA will be authorized to make any action. ```C -static dma_env_t safe_zone = { - .start = ADDRESS_START_OF_SAFE_ZONE, - .end = ADDRESS_END_OF_SAFE_ZONE, +static dma_env_t safe_zone = { + .start = ADDRESS_START_OF_SAFE_ZONE, + .end = ADDRESS_END_OF_SAFE_ZONE, }; ``` - + The environment can be assigned to the target. ```C -tgt1.env = &safe_zone; +tgt1.env = &safe_zone; tgt1.inc_du = 1; ``` - + Size and data type is up to the source, and the rest of the configurations are inherited from the last transaction. - + The transaction is conformed as: ```C -window_size_du = 1024; - -trans.src = &tgt2; -trans.dst = &tgt1; -trans.mode = DMA_TRANS_MODE_CIRCULAR; -trans.win_du = window_size_du; - +window_size_du = 1024; + +trans.src = &tgt2; +trans.dst = &tgt1; +trans.mode = DMA_TRANS_MODE_CIRCULAR; +trans.win_du = window_size_du; + ``` - + Meaning that the information will start to flow into the target 1 from target 2, and every 1024 half-words transferred the application will get an interrupt to check if the `MILESTONE_SYMBOL` is present. Upon finishing the transaction, it shall start again to refill the buffer as it was set in `CIRCULAR_MODE`. - + The amount of times the buffer was filled will be updated on every _transaction done_ interrupt. ```C -void dma_intr_handler_trans_done() -{ - transaction_count++; +void dma_intr_handler_trans_done() { + transaction_count++; } ``` - + The search for the `MILESTONE_SYMBOL` can be done inside the _window done_ interrupt handling. ```C -void dma_intr_handler_window_done() -{ - /* The current window is obtained. The count is zero when no windows have yet been written. When it is set to one, the window zero is ready. */ - window_count = dma_get_window_count() -1; - /* The pointer to the symbol is obtained from the destination pointer + the amount of half-words that have been written. this assumes the symbol is on the first element of each chunk.*/ - address = (uint16_t *)trans.dst->ptr + window_count*window_size_du; - symbol = *address; - if( symbol != MILESTONE_SYMBOL ) - { - /* If the symbol was not the expected one, future transactions should not be carried out.*/ - dma_stop_circular(); - /* The number of the first window with error is saved to analyze it later.*/ - error_window = error_window == 0 ? window_count : error_window; - } +void dma_intr_handler_window_done() { + /* The current window is obtained. The count is zero when no windows have yet been written. When it is set to one, the window zero is ready. */ + window_count = dma_get_window_count() -1; + /* The pointer to the symbol is obtained from the destination pointer + the amount of half-words that have been written. this assumes the symbol is on the first element of each chunk.*/ + address = (uint16_t *)trans.dst->ptr + window_count*window_size_du; + symbol = *address; + if( symbol != MILESTONE_SYMBOL ){ + /* If the symbol was not the expected one, future transactions should not be carried out.*/ + dma_stop_circular(); + /* The number of the first window with error is saved to analyze it later.*/ + error_window = error_window == 0 ? window_count : error_window; + } } ``` > :warning: Interrupt attention routines are advised to be kept as short as possible. This example is merely illustrative. - + Once the `dma_stop_circular()` function is called, the DMA will still finish the transaction it is currently executing (which could be a whole transaction if the faulty window was the last one). It is important to save valuable information that might be overridden by the ongoing transaction (like the faulty window number). - - + + ## Testing This section will describe the available example application in X-HEEP using the DMA, and will outline the testing that needs to be performed in order to validate its proper operation. - - + + ### Available Applications - + There are 6 applications using the DMA: -* `dma_example`: Tests memory-to-memory transfer, the blocking of transactions while another one is in progress, and window interrupts. -* `example_external_peripheral`: Tests the use of the DMA HAL one a DMA instance external to X-HEEP. Only available for simulation. -* `example_virtual_flash`: Tests the transfer to/from an external flash through the DMA. -* `spi_flash_write`: Tests the transfer to/from the flash. Tests circular mode. Not available on FPGA if linker is `flash-exec`. Should be used with `mcu gen BUS=NtoM CPU=cv32e40p` to test circular mode. -* `spi_host_dma_exampe`: Test the transfer of data through the SPI host. Not available on Verilator. -* `spi_host_dma_power_gate_example`: Test the transfer of data through the SPI host. Not available on Verilator. - - +* `dma_example`: Tests memory-to-memory transfer, the blocking of transactions while another one is in progress, and window interrupts. +* `example_external_peripheral`: Tests the use of the DMA HAL one a DMA instance external to X-HEEP. Only available for simulation. +* `example_virtual_flash`: Tests the transfer to/from an external flash through the DMA. +* `spi_flash_write`: Tests the transfer to/from the flash. Tests circular mode. Not available on FPGA if linker is `flash-exec`. Should be used with `mcu gen BUS=NtoM CPU=cv32e40p` to test circular mode. +* `spi_host_dma_exampe`: Test the transfer of data through the SPI host. Not available on Verilator. +* `spi_host_dma_power_gate_example`: Test the transfer of data through the SPI host. Not available on Verilator. + + ## 😎 X-pert Zone: - + If you know what you are doing and want to minimize the overhead of using the DMA, you can try by-passing the HAL and writing directly on the configuration registers. - + ```c - - /* We will copy a set of 25 half-words of 16 bits into a buffer of 32-bit words. - Each word in the destination buffer will have its 16 MSB set to 0, and the 16 LSB with the corresponding value from the source.*/ - #define HALF_WORDS_TO_COPY 25 - static uint16_t src_buffer[HALF_WORDS_TO_COPY]; // The source buffer - static uint32_t dst_buffer[HALF_WORDS_TO_COPY]; // The destination buffer - - /* Set the DMA's control block's peripheral structure to point to the address defined in core_v_mini_mcu.h */ - dma_cb.peri = dma_peri; - /* Activate interrupts*/ - dma_cb.peri->INTERRUPT_EN |= INTR_EN_TRANS_DONE; - /* Set the source and destination pointers*/ - dma_cb.peri->SRC_PTR = (uint16_t*) source_buffer; - dma_cb.peri->DST_PTR = (uint32_t*) dst_buffer; - - /* Set the source increment as 2 bytes (because the source buffer is uint16_t). - Set the destination increment as 4 bytes (because the destination buffer is uint32_t). - We write 1026 = 0000 0100 0000 0010, - as the first 8 LSB refer to the source, and the next 8 bits for the destination. */ - dma_cb.peri->PTR_INC = (uint32_t) 1026; - - /* Make sure that the DMA will point to memory.*/ - dma_cb.peri->SLOT = DMA_TRIG_MEMORY; - - /* Set the data transfer type as half-words.*/ - dma_cb.peri->TYPE = DMA_DATA_TYPE_HALF_WORD; - - /* Set the transaction size, this will launch the transaction. - If you want to restart the same transaction again, just run from here.*/ - dma_cb.peri->SIZE = HALF_WORDS_TO_COPY; - - /* Go to sleep until the DMA finishes.*/ - while( dma_cb.peri->STATUS == 0 ) { - /* Disable the interrupts MSTATUS to avoid going to sleep AFTER the interrupt - was triggered.*/ - CSR_CLEAR_BITS(CSR_REG_MSTATUS, 0x8); - /* If the transaction has not yet finished, go to sleep*/ - if (dma_cb.peri->STATUS == 0) { - /* If a interrupt happened before, the core would still wake-up, - but will not jump to the interrupt handler MSTATUS is not re-set. */ - { asm volatile("wfi"); } - } - /* Restore the interrupts. */ - CSR_SET_BITS(CSR_REG_MSTATUS, 0x8); - } - - -``` \ No newline at end of file +/* We will copy a set of 25 half-words of 16 bits into a buffer of 32-bit words. +Each word in the destination buffer will have its 16 MSB set to 0, and the 16 LSB with the corresponding value from the source.*/ +#define HALF_WORDS_TO_COPY 25 +static uint16_t src_buffer[HALF_WORDS_TO_COPY]; // The source buffer +static uint32_t dst_buffer[HALF_WORDS_TO_COPY]; // The destination buffer + +/* Set the DMA's control block's peripheral structure to point to the address defined in core_v_mini_mcu.h */ +dma_cb.peri = dma_peri; +/* Activate interrupts*/ +dma_cb.peri->INTERRUPT_EN |= INTR_EN_TRANS_DONE; +/* Set the source and destination pointers*/ +dma_cb.peri->SRC_PTR = (uint16_t*) source_buffer; +dma_cb.peri->DST_PTR = (uint32_t*) dst_buffer; + +/* Set the source increment as 2 bytes (because the source buffer is uint16_t). +Set the destination increment as 4 bytes (because the destination buffer is uint32_t). +We write 1026 = 0000 0100 0000 0010, +as the first 8 LSB refer to the source, and the next 8 bits for the destination. */ +dma_cb.peri->PTR_INC = (uint32_t) 1026; + +/* Make sure that the DMA will point to memory.*/ +dma_cb.peri->SLOT = DMA_TRIG_MEMORY; + +/* Set the data transfer type as half-words.*/ +dma_cb.peri->TYPE = DMA_DATA_TYPE_HALF_WORD; + +/* Set the transaction size, this will launch the transaction. +If you want to restart the same transaction again, just run from here.*/ +dma_cb.peri->SIZE = HALF_WORDS_TO_COPY; + +/* Go to sleep until the DMA finishes.*/ +while( dma_cb.peri->STATUS == 0 ) { + /* Disable the interrupts MSTATUS to avoid going to sleep AFTER the interrupt + was triggered.*/ + CSR_CLEAR_BITS(CSR_REG_MSTATUS, 0x8); + /* If the transaction has not yet finished, go to sleep*/ + if (dma_cb.peri->STATUS == 0) { + /* If a interrupt happened before, the core would still wake-up, + but will not jump to the interrupt handler MSTATUS is not re-set. */ + { asm volatile("wfi"); } + } + /* Restore the interrupts. */ + CSR_SET_BITS(CSR_REG_MSTATUS, 0x8); +} + + +``` diff --git a/docs/source/Peripherals/Timer.md b/docs/source/Peripherals/Timer.md new file mode 100644 index 000000000..052faf0de --- /dev/null +++ b/docs/source/Peripherals/Timer.md @@ -0,0 +1,107 @@ + +# Timer SDK + +This SDK provides utilities for execution time measurements using HW timers. It includes functions to start, stop, reset, and configure timers, as well as to enable timer interrupts and measure elapsed time. + +## Usage + +The SDK provides a set of functions to interact with the HW Timer for various timing operations. + +### Initialize Timer for Counting Cycles + +This function configures the counter at the running clock frequency to count the number of clock cycles. Call this function before any of the other timer SDK functions. + +```c +void timer_cycles_init(); +``` + +### Start Timer + +Start the HW timer. + +```c +void timer_start(); +``` + +### Get Current Timer Value + +Retrieve the current value of the HW timer without stopping it. + +```c +uint32_t timer_get_cycles(); +``` + +### Complete timer reset + +Completely resets the HW counter, disabling all IRQs, counters, and comparators. +```c +void timer_reset(); +``` + +### Stop and Reset Timer + +Retrieve the current value of the HW timer and stop it. + +```c +uint32_t timer_stop(); +``` + +### Set Timer Threshold + +Set the timer to go off once the counter value reaches the specified threshold. If the timer interrupts and the timer IRQ have been enabled, when the timer reaches that value an interrupt will be called. + +```c +void timer_arm_set(uint32_t threshold); +``` + +### Set Timer Threshold and Start + +Set the timer to go off once the counter value reaches the specified threshold, and start the timer. If the timer interrupts and the timer IRQ have been enabled, when the timer reaches that value an interrupt will be called. + +```c +void timer_arm_start(uint32_t threshold); +``` + +### Enable Timer IRQ + +Enable the timer interrupt request. + +```c +void timer_irq_enable(); +``` + +### Clear Timer IRQ + +Clear the timer interrupt request. + +```c +void timer_irq_clear(); +``` + +### Enable Timer Machine-level Interrupts + +Enable the timer machine-level interrupts for the X-Heep platform. + +```c +void enable_timer_interrupt(); +``` + +### Wait for Microseconds + +Block execution for a specified number of microseconds. This function is not precise for small numbers of microseconds. Enable timer interrupts with `enable_timer_interrupt()` before using this function. + +```c +void timer_wait_us(uint32_t ms); +``` + +### Get Execution Time in Microseconds + +Get the time taken to execute a certain number of cycles, returned as a float representing the time in microseconds. + +```c +float get_time_from_cycles(uint32_t cycles); +``` + +## Example Usage + +An example of utilization of the timer SDK can be found in `sw/applications/example_timer_sdk/main.c`. diff --git a/block_diagrams/example_adc.svg b/docs/source/images/example_adc.svg similarity index 100% rename from block_diagrams/example_adc.svg rename to docs/source/images/example_adc.svg diff --git a/docs/source/images/xheep_diagram.png b/docs/source/images/xheep_diagram.png new file mode 100644 index 000000000..681962c06 Binary files /dev/null and b/docs/source/images/xheep_diagram.png differ diff --git a/hw/core-v-mini-mcu/ao_peripheral_subsystem.sv b/hw/core-v-mini-mcu/ao_peripheral_subsystem.sv index b7ca8adc1..dd9246dfc 100644 --- a/hw/core-v-mini-mcu/ao_peripheral_subsystem.sv +++ b/hw/core-v-mini-mcu/ao_peripheral_subsystem.sv @@ -104,8 +104,10 @@ module ao_peripheral_subsystem output reg_req_t ext_peripheral_slave_req_o, input reg_rsp_t ext_peripheral_slave_resp_i, - input logic ext_dma_slot_tx_i, - input logic ext_dma_slot_rx_i + input logic [core_v_mini_mcu_pkg::DMA_CH_NUM-1:0] ext_dma_slot_tx_i, + input logic [core_v_mini_mcu_pkg::DMA_CH_NUM-1:0] ext_dma_slot_rx_i, + + input logic [core_v_mini_mcu_pkg::DMA_CH_NUM-1:0] ext_dma_stop_i ); import core_v_mini_mcu_pkg::*; @@ -312,23 +314,33 @@ module ao_peripheral_subsystem .intr_timer_expired_1_0_o(rv_timer_1_intr_o) ); - parameter DMA_TRIGGER_SLOT_NUM = 7; - logic [DMA_TRIGGER_SLOT_NUM-1:0] dma_trigger_slots; - assign dma_trigger_slots[0] = spi_rx_valid_i; - assign dma_trigger_slots[1] = spi_tx_ready_i; - assign dma_trigger_slots[2] = spi_flash_rx_valid; - assign dma_trigger_slots[3] = spi_flash_tx_ready; - assign dma_trigger_slots[4] = i2s_rx_valid_i; - assign dma_trigger_slots[5] = ext_dma_slot_tx_i; - assign dma_trigger_slots[6] = ext_dma_slot_rx_i; - - dma #( - .reg_req_t (reg_pkg::reg_req_t), - .reg_rsp_t (reg_pkg::reg_rsp_t), - .obi_req_t (obi_pkg::obi_req_t), + parameter DMA_GLOBAL_TRIGGER_SLOT_NUM = 5; + parameter DMA_EXT_TRIGGER_SLOT_NUM = core_v_mini_mcu_pkg::DMA_CH_NUM * 2; + + logic [DMA_GLOBAL_TRIGGER_SLOT_NUM-1:0] dma_global_trigger_slots; + logic [DMA_EXT_TRIGGER_SLOT_NUM-1:0] dma_ext_trigger_slots; + + assign dma_global_trigger_slots[0] = spi_rx_valid_i; + assign dma_global_trigger_slots[1] = spi_tx_ready_i; + assign dma_global_trigger_slots[2] = spi_flash_rx_valid; + assign dma_global_trigger_slots[3] = spi_flash_tx_ready; + assign dma_global_trigger_slots[4] = i2s_rx_valid_i; + + generate + for (genvar i = 0; i < core_v_mini_mcu_pkg::DMA_CH_NUM; i++) begin : dma_trigger_slots_gen + assign dma_ext_trigger_slots[2*i] = ext_dma_slot_tx_i[i]; + assign dma_ext_trigger_slots[2*i+1] = ext_dma_slot_rx_i[i]; + end + endgenerate + + dma_subsystem #( + .reg_req_t(reg_pkg::reg_req_t), + .reg_rsp_t(reg_pkg::reg_rsp_t), + .obi_req_t(obi_pkg::obi_req_t), .obi_resp_t(obi_pkg::obi_resp_t), - .SLOT_NUM (DMA_TRIGGER_SLOT_NUM) - ) dma_i ( + .GLOBAL_SLOT_NUM(DMA_GLOBAL_TRIGGER_SLOT_NUM), + .EXT_SLOT_NUM(DMA_EXT_TRIGGER_SLOT_NUM) + ) dma_subsystem_i ( .clk_i, .rst_ni, .reg_req_i(ao_peripheral_slv_req[core_v_mini_mcu_pkg::DMA_IDX]), @@ -339,7 +351,9 @@ module ao_peripheral_subsystem .dma_write_ch0_resp_i, .dma_addr_ch0_req_o, .dma_addr_ch0_resp_i, - .trigger_slot_i(dma_trigger_slots), + .global_trigger_slot_i(dma_global_trigger_slots), + .ext_trigger_slot_i(dma_ext_trigger_slots), + .ext_dma_stop_i(ext_dma_stop_i), .dma_done_intr_o(dma_done_intr_o), .dma_window_intr_o(dma_window_intr_o) ); diff --git a/hw/core-v-mini-mcu/core_v_mini_mcu.sv b/hw/core-v-mini-mcu/core_v_mini_mcu.sv index 68de4766a..4cfb3d9d5 100644 --- a/hw/core-v-mini-mcu/core_v_mini_mcu.sv +++ b/hw/core-v-mini-mcu/core_v_mini_mcu.sv @@ -299,6 +299,8 @@ module core_v_mini_mcu output obi_req_t ext_dma_addr_ch0_req_o, input obi_resp_t ext_dma_addr_ch0_resp_i, + input logic [core_v_mini_mcu_pkg::DMA_CH_NUM-1:0] ext_dma_stop_i, + output reg_req_t ext_peripheral_slave_req_o, input reg_rsp_t ext_peripheral_slave_resp_i, @@ -313,8 +315,6 @@ module core_v_mini_mcu input logic cpu_subsystem_powergate_switch_ack_ni, output logic peripheral_subsystem_powergate_switch_no, input logic peripheral_subsystem_powergate_switch_ack_ni, - output logic [core_v_mini_mcu_pkg::NUM_BANKS-1:0] memory_subsystem_banks_powergate_switch_no, - input logic [core_v_mini_mcu_pkg::NUM_BANKS-1:0] memory_subsystem_banks_powergate_switch_ack_ni, output logic [EXT_DOMAINS_RND-1:0] external_subsystem_powergate_switch_no, input logic [EXT_DOMAINS_RND-1:0] external_subsystem_powergate_switch_ack_ni, output logic [EXT_DOMAINS_RND-1:0] external_subsystem_powergate_iso_no, @@ -325,8 +325,8 @@ module core_v_mini_mcu output logic [31:0] exit_value_o, - input logic ext_dma_slot_tx_i, - input logic ext_dma_slot_rx_i + input logic [core_v_mini_mcu_pkg::DMA_CH_NUM-1:0] ext_dma_slot_tx_i, + input logic [core_v_mini_mcu_pkg::DMA_CH_NUM-1:0] ext_dma_slot_rx_i ); import core_v_mini_mcu_pkg::*; @@ -419,6 +419,8 @@ module core_v_mini_mcu logic peripheral_subsystem_powergate_iso_n; logic peripheral_subsystem_clkgate_en_n; + logic [core_v_mini_mcu_pkg::NUM_BANKS-1:0] memory_subsystem_banks_powergate_switch_n; + logic [core_v_mini_mcu_pkg::NUM_BANKS-1:0] memory_subsystem_banks_powergate_switch_ack_n; logic [core_v_mini_mcu_pkg::NUM_BANKS-1:0] memory_subsystem_banks_set_retentive_n; logic [core_v_mini_mcu_pkg::NUM_BANKS-1:0] memory_subsystem_banks_powergate_iso_n; logic [core_v_mini_mcu_pkg::NUM_BANKS-1:0] memory_subsystem_clkgate_en_n; @@ -438,16 +440,14 @@ module core_v_mini_mcu assign peripheral_subsystem_rst_n = peripheral_subsystem_pwr_ctrl_out.rst_n; assign peripheral_subsystem_clkgate_en_n = peripheral_subsystem_pwr_ctrl_out.clkgate_en_n; - //pwrgate exposed both outside and inside to deal with memories with embedded SLEEP mode or external PWR cells - assign memory_subsystem_banks_powergate_switch_no[0] = memory_subsystem_pwr_ctrl_out[0].pwrgate_en_n; - assign memory_subsystem_pwr_ctrl_in[0].pwrgate_ack_n = memory_subsystem_banks_powergate_switch_ack_ni[0]; + assign memory_subsystem_banks_powergate_switch_n[0] = memory_subsystem_pwr_ctrl_out[0].pwrgate_en_n; + assign memory_subsystem_pwr_ctrl_in[0].pwrgate_ack_n = memory_subsystem_banks_powergate_switch_ack_n[0]; //isogate exposed outside for UPF sim flow and switch cells assign memory_subsystem_banks_powergate_iso_n[0] = memory_subsystem_pwr_ctrl_out[0].isogate_en_n; assign memory_subsystem_banks_set_retentive_n[0] = memory_subsystem_pwr_ctrl_out[0].retentive_en_n; assign memory_subsystem_clkgate_en_n[0] = memory_subsystem_pwr_ctrl_out[0].clkgate_en_n; - //pwrgate exposed both outside and inside to deal with memories with embedded SLEEP mode or external PWR cells - assign memory_subsystem_banks_powergate_switch_no[1] = memory_subsystem_pwr_ctrl_out[1].pwrgate_en_n; - assign memory_subsystem_pwr_ctrl_in[1].pwrgate_ack_n = memory_subsystem_banks_powergate_switch_ack_ni[1]; + assign memory_subsystem_banks_powergate_switch_n[1] = memory_subsystem_pwr_ctrl_out[1].pwrgate_en_n; + assign memory_subsystem_pwr_ctrl_in[1].pwrgate_ack_n = memory_subsystem_banks_powergate_switch_ack_n[1]; //isogate exposed outside for UPF sim flow and switch cells assign memory_subsystem_banks_powergate_iso_n[1] = memory_subsystem_pwr_ctrl_out[1].isogate_en_n; assign memory_subsystem_banks_set_retentive_n[1] = memory_subsystem_pwr_ctrl_out[1].retentive_en_n; @@ -608,11 +608,8 @@ module core_v_mini_mcu .clk_gate_en_ni(memory_subsystem_clkgate_en_n), .ram_req_i(ram_slave_req), .ram_resp_o(ram_slave_resp), - /* - the memory_subsystem_banks_powergate_switch_no gets wired both internally - and externally to support both macros that have and do not have SLEEP capabilities integrated in the macros - */ - .pwrgate_ni(memory_subsystem_banks_powergate_switch_no), + .pwrgate_ni(memory_subsystem_banks_powergate_switch_n), + .pwrgate_ack_no(memory_subsystem_banks_powergate_switch_ack_n), .set_retentive_ni(memory_subsystem_banks_set_retentive_n) ); @@ -682,7 +679,8 @@ module core_v_mini_mcu .ext_peripheral_slave_req_o, .ext_peripheral_slave_resp_i, .ext_dma_slot_tx_i, - .ext_dma_slot_rx_i + .ext_dma_slot_rx_i, + .ext_dma_stop_i ); peripheral_subsystem peripheral_subsystem_i ( diff --git a/hw/core-v-mini-mcu/core_v_mini_mcu.sv.tpl b/hw/core-v-mini-mcu/core_v_mini_mcu.sv.tpl index a73004496..cb5717528 100644 --- a/hw/core-v-mini-mcu/core_v_mini_mcu.sv.tpl +++ b/hw/core-v-mini-mcu/core_v_mini_mcu.sv.tpl @@ -53,6 +53,8 @@ ${pad.core_v_mini_mcu_interface} output obi_req_t ext_dma_addr_ch0_req_o, input obi_resp_t ext_dma_addr_ch0_resp_i, + input logic [core_v_mini_mcu_pkg::DMA_CH_NUM-1:0] ext_dma_stop_i, + output reg_req_t ext_peripheral_slave_req_o, input reg_rsp_t ext_peripheral_slave_resp_i, @@ -67,8 +69,6 @@ ${pad.core_v_mini_mcu_interface} input logic cpu_subsystem_powergate_switch_ack_ni, output logic peripheral_subsystem_powergate_switch_no, input logic peripheral_subsystem_powergate_switch_ack_ni, - output logic [core_v_mini_mcu_pkg::NUM_BANKS-1:0] memory_subsystem_banks_powergate_switch_no, - input logic [core_v_mini_mcu_pkg::NUM_BANKS-1:0] memory_subsystem_banks_powergate_switch_ack_ni, output logic [EXT_DOMAINS_RND-1:0] external_subsystem_powergate_switch_no, input logic [EXT_DOMAINS_RND-1:0] external_subsystem_powergate_switch_ack_ni, output logic [EXT_DOMAINS_RND-1:0] external_subsystem_powergate_iso_no, @@ -79,8 +79,8 @@ ${pad.core_v_mini_mcu_interface} output logic [31:0] exit_value_o, - input logic ext_dma_slot_tx_i, - input logic ext_dma_slot_rx_i + input logic [core_v_mini_mcu_pkg::DMA_CH_NUM-1:0] ext_dma_slot_tx_i, + input logic [core_v_mini_mcu_pkg::DMA_CH_NUM-1:0] ext_dma_slot_rx_i ); import core_v_mini_mcu_pkg::*; @@ -173,6 +173,8 @@ ${pad.core_v_mini_mcu_interface} logic peripheral_subsystem_powergate_iso_n; logic peripheral_subsystem_clkgate_en_n; + logic [core_v_mini_mcu_pkg::NUM_BANKS-1:0] memory_subsystem_banks_powergate_switch_n; + logic [core_v_mini_mcu_pkg::NUM_BANKS-1:0] memory_subsystem_banks_powergate_switch_ack_n; logic [core_v_mini_mcu_pkg::NUM_BANKS-1:0] memory_subsystem_banks_set_retentive_n; logic [core_v_mini_mcu_pkg::NUM_BANKS-1:0] memory_subsystem_banks_powergate_iso_n; logic [core_v_mini_mcu_pkg::NUM_BANKS-1:0] memory_subsystem_clkgate_en_n; @@ -193,9 +195,8 @@ ${pad.core_v_mini_mcu_interface} assign peripheral_subsystem_clkgate_en_n = peripheral_subsystem_pwr_ctrl_out.clkgate_en_n; % for bank in xheep.iter_ram_banks(): - //pwrgate exposed both outside and inside to deal with memories with embedded SLEEP mode or external PWR cells - assign memory_subsystem_banks_powergate_switch_no[${bank.name()}] = memory_subsystem_pwr_ctrl_out[${bank.name()}].pwrgate_en_n; - assign memory_subsystem_pwr_ctrl_in[${bank.name()}].pwrgate_ack_n = memory_subsystem_banks_powergate_switch_ack_ni[${bank.name()}]; + assign memory_subsystem_banks_powergate_switch_n[${bank.name()}] = memory_subsystem_pwr_ctrl_out[${bank.name()}].pwrgate_en_n; + assign memory_subsystem_pwr_ctrl_in[${bank.name()}].pwrgate_ack_n = memory_subsystem_banks_powergate_switch_ack_n[${bank.name()}]; //isogate exposed outside for UPF sim flow and switch cells assign memory_subsystem_banks_powergate_iso_n[${bank.name()}] = memory_subsystem_pwr_ctrl_out[${bank.name()}].isogate_en_n; assign memory_subsystem_banks_set_retentive_n[${bank.name()}] = memory_subsystem_pwr_ctrl_out[${bank.name()}].retentive_en_n; @@ -357,11 +358,8 @@ ${pad.core_v_mini_mcu_interface} .clk_gate_en_ni(memory_subsystem_clkgate_en_n), .ram_req_i(ram_slave_req), .ram_resp_o(ram_slave_resp), - /* - the memory_subsystem_banks_powergate_switch_no gets wired both internally - and externally to support both macros that have and do not have SLEEP capabilities integrated in the macros - */ - .pwrgate_ni(memory_subsystem_banks_powergate_switch_no), + .pwrgate_ni(memory_subsystem_banks_powergate_switch_n), + .pwrgate_ack_no(memory_subsystem_banks_powergate_switch_ack_n), .set_retentive_ni(memory_subsystem_banks_set_retentive_n) ); @@ -429,7 +427,8 @@ ${pad.core_v_mini_mcu_interface} .ext_peripheral_slave_req_o, .ext_peripheral_slave_resp_i, .ext_dma_slot_tx_i, - .ext_dma_slot_rx_i + .ext_dma_slot_rx_i, + .ext_dma_stop_i ); peripheral_subsystem peripheral_subsystem_i ( diff --git a/hw/core-v-mini-mcu/include/addr_map_rule_pkg.sv b/hw/core-v-mini-mcu/include/addr_map_rule_pkg.sv index 4e0a9a273..712164af5 100644 --- a/hw/core-v-mini-mcu/include/addr_map_rule_pkg.sv +++ b/hw/core-v-mini-mcu/include/addr_map_rule_pkg.sv @@ -10,4 +10,10 @@ package addr_map_rule_pkg; logic [31:0] end_addr; } addr_map_rule_t; + typedef struct packed { + logic [7:0] idx; + logic [7:0] start_addr; + logic [7:0] end_addr; + } addr_map_rule_8bit_t; + endpackage diff --git a/hw/core-v-mini-mcu/include/core_v_mini_mcu_pkg.sv.tpl b/hw/core-v-mini-mcu/include/core_v_mini_mcu_pkg.sv.tpl index fe0eb9dc9..ba99d50f5 100644 --- a/hw/core-v-mini-mcu/include/core_v_mini_mcu_pkg.sv.tpl +++ b/hw/core-v-mini-mcu/include/core_v_mini_mcu_pkg.sv.tpl @@ -138,6 +138,7 @@ package core_v_mini_mcu_pkg; // always-on peripherals // --------------------- localparam AO_PERIPHERALS = ${ao_peripherals_count}; + localparam DMA_CH_NUM = ${dma_ch_count}; % for peripheral, addr in ao_peripherals.items(): localparam logic [31:0] ${peripheral.upper()}_START_ADDRESS = AO_PERIPHERAL_START_ADDRESS + 32'h${addr["offset"]}; @@ -146,6 +147,7 @@ package core_v_mini_mcu_pkg; localparam logic [31:0] ${peripheral.upper()}_IDX = 32'd${loop.index}; % endfor + localparam addr_map_rule_t [AO_PERIPHERALS-1:0] AO_PERIPHERALS_ADDR_RULES = '{ % for peripheral, addr in ao_peripherals.items(): '{ idx: ${peripheral.upper()}_IDX, start_addr: ${peripheral.upper()}_START_ADDRESS, end_addr: ${peripheral.upper()}_END_ADDRESS }${"," if not loop.last else ""} @@ -154,6 +156,23 @@ package core_v_mini_mcu_pkg; localparam int unsigned AO_PERIPHERALS_PORT_SEL_WIDTH = AO_PERIPHERALS > 1 ? $clog2(AO_PERIPHERALS) : 32'd1; + // Relative DMA channels addresses +% for i in range(int(dma_ch_count)): + localparam logic [7:0] DMA_CH${i}_START_ADDRESS = 8'h${hex(int(ao_peripherals["dma"]["ch_length"], 16)*(i) >> 8)[2:]}; + localparam logic [7:0] DMA_CH${i}_SIZE = 8'h${hex(int(ao_peripherals["dma"]["ch_length"], 16) >> 8)[2:]}; + localparam logic [7:0] DMA_CH${i}_END_ADDRESS = DMA_CH${i}_START_ADDRESS + DMA_CH${i}_SIZE; + localparam logic [7:0] DMA_CH${i}_IDX = 8'd${i}; + +% endfor + + localparam addr_map_rule_8bit_t [DMA_CH_NUM-1:0] DMA_ADDR_RULES = '{ + % for i in range(int(dma_ch_count)): + '{ idx: DMA_CH${i}_IDX, start_addr: DMA_CH${i}_START_ADDRESS, end_addr: DMA_CH${i}_END_ADDRESS }${"," if not loop.last else ""} +% endfor + }; + + localparam int unsigned DMA_CH_PORT_SEL_WIDTH = DMA_CH_NUM > 1 ? $clog2(DMA_CH_NUM) : 32'd1; + ###################################################################### ## Automatically add all peripherals listed ###################################################################### diff --git a/hw/core-v-mini-mcu/memory_subsystem.sv.tpl b/hw/core-v-mini-mcu/memory_subsystem.sv.tpl index aa8f3b834..660b1b6a9 100644 --- a/hw/core-v-mini-mcu/memory_subsystem.sv.tpl +++ b/hw/core-v-mini-mcu/memory_subsystem.sv.tpl @@ -21,6 +21,7 @@ module memory_subsystem // power manager signals that goes to the ASIC macros input logic [core_v_mini_mcu_pkg::NUM_BANKS-1:0] pwrgate_ni, + output logic [core_v_mini_mcu_pkg::NUM_BANKS-1:0] pwrgate_ack_no, input logic [core_v_mini_mcu_pkg::NUM_BANKS-1:0] set_retentive_ni ); @@ -74,6 +75,7 @@ module memory_subsystem .wdata_i(ram_req_i[${i}].wdata), .be_i(ram_req_i[${i}].be), .pwrgate_ni(pwrgate_ni[${i}]), + .pwrgate_ack_no(pwrgate_ack_no[${i}]), .set_retentive_ni(set_retentive_ni[${i}]), .rdata_o(ram_resp_o[${i}].rdata) ); diff --git a/hw/core-v-mini-mcu/peripheral_subsystem.sv b/hw/core-v-mini-mcu/peripheral_subsystem.sv index fd7ac4c31..4fa663e66 100644 --- a/hw/core-v-mini-mcu/peripheral_subsystem.sv +++ b/hw/core-v-mini-mcu/peripheral_subsystem.sv @@ -452,7 +452,7 @@ module peripheral_subsystem .reg_req_t(reg_pkg::reg_req_t), .reg_rsp_t(reg_pkg::reg_rsp_t) ) i2s_i ( - .clk_i, + .clk_i(clk_cg), .rst_ni, .reg_req_i(peripheral_slv_req[core_v_mini_mcu_pkg::I2S_IDX]), .reg_rsp_o(peripheral_slv_rsp[core_v_mini_mcu_pkg::I2S_IDX]), diff --git a/hw/core-v-mini-mcu/peripheral_subsystem.sv.tpl b/hw/core-v-mini-mcu/peripheral_subsystem.sv.tpl index 12fc624fd..4025cd88a 100644 --- a/hw/core-v-mini-mcu/peripheral_subsystem.sv.tpl +++ b/hw/core-v-mini-mcu/peripheral_subsystem.sv.tpl @@ -546,7 +546,7 @@ module peripheral_subsystem .reg_req_t(reg_pkg::reg_req_t), .reg_rsp_t(reg_pkg::reg_rsp_t) ) pdm2pcm_i ( - .clk_i, + .clk_i(clk_cg), .rst_ni, .reg_req_i(peripheral_slv_req[core_v_mini_mcu_pkg::PDM2PCM_IDX]), .reg_rsp_o(peripheral_slv_rsp[core_v_mini_mcu_pkg::PDM2PCM_IDX]), @@ -569,7 +569,7 @@ module peripheral_subsystem .reg_req_t(reg_pkg::reg_req_t), .reg_rsp_t(reg_pkg::reg_rsp_t) ) i2s_i ( - .clk_i, + .clk_i(clk_cg), .rst_ni, .reg_req_i(peripheral_slv_req[core_v_mini_mcu_pkg::I2S_IDX]), .reg_rsp_o(peripheral_slv_rsp[core_v_mini_mcu_pkg::I2S_IDX]), diff --git a/hw/fpga/sram_wrapper.sv.tpl b/hw/fpga/sram_wrapper.sv.tpl index 5eeac9455..6d518136a 100644 --- a/hw/fpga/sram_wrapper.sv.tpl +++ b/hw/fpga/sram_wrapper.sv.tpl @@ -24,10 +24,14 @@ module sram_wrapper #( input logic [3:0] be_i, // power manager signals that goes to the ASIC macros input logic pwrgate_ni, + output logic pwrgate_ack_no, input logic set_retentive_ni, // output ports output logic [31:0] rdata_o ); + +assign pwrgate_ack_no = pwrgate_ni; + <%el = ""%> % for num_words in xheep.iter_bank_numwords(): ${el}if (NumWords == 32'd${num_words}) begin diff --git a/hw/ip/dma/data/dma.hjson b/hw/ip/dma/data/dma.hjson index 6be440323..58f6d3a03 100644 --- a/hw/ip/dma/data/dma.hjson +++ b/hw/ip/dma/data/dma.hjson @@ -283,6 +283,28 @@ { bits: "0", name: "TRANSACTION_DONE", desc: "Enables transaction done interrupt" } { bits: "1", name: "WINDOW_DONE", desc: "Enables window done interrupt" } ] + }, + { name: "TRANSACTION_IFR", + desc: '''Interrupt Flag Register for transactions''', + swaccess: "ro", + hwaccess: "hrw", + hwext: "true", + hwre: "true", // latched signal of software write pulse + resval: 0, + fields: [ + { bits: "0", name: "FLAG", desc: "Set for transaction done interrupt" } + ] + }, + { name: "WINDOW_IFR", + desc: '''Interrupt Flag Register for windows''', + swaccess: "ro", + hwaccess: "hrw", + hwext: "true", + hwre: "true", // latched signal of software write pulse + resval: 0, + fields: [ + { bits: "0", name: "FLAG", desc: "Set for window done interrupt" } + ] } ] } diff --git a/hw/ip/dma/dma.core b/hw/ip/dma/dma.core index 0a06c35d4..86255b7a1 100644 --- a/hw/ip/dma/dma.core +++ b/hw/ip/dma/dma.core @@ -1,7 +1,7 @@ CAPI=2: name: "x-heep:ip:dma" -description: "core-v-mini-mcu dma peripheral" +description: "core-v-mini-mcu dma channel" # Copyright 2021 OpenHW Group # Solderpad Hardware License, Version 2.1, see LICENSE.md for details. diff --git a/hw/ip/dma/rtl/dma.sv b/hw/ip/dma/rtl/dma.sv index 7664f2cfd..4bfe0219c 100644 --- a/hw/ip/dma/rtl/dma.sv +++ b/hw/ip/dma/rtl/dma.sv @@ -17,6 +17,8 @@ module dma #( input logic clk_i, input logic rst_ni, + input logic ext_dma_stop_i, + input reg_req_t reg_req_i, output reg_rsp_t reg_rsp_o, @@ -50,7 +52,8 @@ module dma #( logic [ 31:0] write_ptr_reg; logic [ 31:0] write_address; logic [ 31:0] dma_addr_cnt; - logic [ 2:0] dma_cnt_du; + logic [ 2:0] dma_src_cnt_du; + logic [ 2:0] dma_dst_cnt_du; logic dma_start; logic dma_done; logic dma_window_event; @@ -143,6 +146,14 @@ module dma #( logic data_in_gnt_virt_n; logic data_in_gnt_virt_n_n; + /* Interrupt Flag Register signals */ + logic transaction_ifr; + logic dma_done_intr_n; + logic dma_done_intr; + logic window_ifr; + logic dma_window_intr; + logic dma_window_intr_n; + /* FIFO signals */ logic fifo_flush; logic fifo_full; @@ -244,8 +255,13 @@ module dma #( assign data_out_rvalid = dma_write_ch0_resp_i.rvalid; assign data_out_rdata = dma_write_ch0_resp_i.rdata; - assign dma_done_intr_o = dma_done & reg2hw.interrupt_en.transaction_done.q; - assign dma_window_intr_o = dma_window_event & reg2hw.interrupt_en.window_done.q; + assign dma_done_intr = transaction_ifr; + assign dma_window_intr = window_ifr; + + assign dma_done_intr_o = dma_done_intr_n; + assign hw2reg.transaction_ifr.d = transaction_ifr; + assign dma_window_intr_o = dma_window_intr_n; + assign hw2reg.window_ifr.d = window_ifr; assign dst_data_type = dma_data_type_t'(reg2hw.dst_data_type.q); assign src_data_type = dma_data_type_t'(reg2hw.src_data_type.q); @@ -282,57 +298,57 @@ module dma #( }; assign idle_to_right_ex = { |reg2hw.pad_top.q == 1'b0 && |reg2hw.pad_left.q == 1'b0 && |reg2hw.pad_right.q == 1'b1 - && dma_src_cnt_d1 == ({11'h0, reg2hw.pad_right.q} + {14'h0, dma_cnt_du}) && dma_start == 1'b1 + && dma_src_cnt_d1 == ({11'h0, reg2hw.pad_right.q} + {14'h0, dma_dst_cnt_du}) }; assign idle_to_bottom_ex = { |reg2hw.pad_top.q == 1'b0 && |reg2hw.pad_left.q == 1'b0 && |reg2hw.pad_right.q == 1'b0 && |reg2hw.pad_bottom.q == 1'b1 - && dma_src_cnt_d2 == ({11'h0, reg2hw.pad_bottom.q} + {14'h0, dma_cnt_du}) && dma_src_cnt_d1 == ({14'h0, dma_cnt_du}) && dma_start == 1'b1 + && dma_src_cnt_d2 == ({11'h0, reg2hw.pad_bottom.q} + {14'h0, dma_dst_cnt_du}) && dma_src_cnt_d1 == ({14'h0, dma_dst_cnt_du}) }; assign top_ex_to_top_dn = { - dma_src_cnt_d2 == ({1'h0, reg2hw.size_d2.q} + {11'h0, reg2hw.pad_bottom.q} + {14'h0, dma_cnt_du}) && dma_src_cnt_d1 == ({14'h0, dma_cnt_du}) && |reg2hw.pad_left.q == 1'b0 + dma_src_cnt_d2 == ({1'h0, reg2hw.size_d2.q} + {11'h0, reg2hw.pad_bottom.q} + {14'h0, dma_dst_cnt_du}) && dma_src_cnt_d1 == ({14'h0, dma_dst_cnt_du}) && |reg2hw.pad_left.q == 1'b0 }; assign top_ex_to_left_ex = { - dma_src_cnt_d2 == ({1'h0, reg2hw.size_d2.q} + {11'h0, reg2hw.pad_bottom.q} + {14'h0, dma_cnt_du}) && dma_src_cnt_d1 == ({14'h0, dma_cnt_du}) && |reg2hw.pad_left.q == 1'b1 + dma_src_cnt_d2 == ({1'h0, reg2hw.size_d2.q} + {11'h0, reg2hw.pad_bottom.q} + {14'h0, dma_dst_cnt_du}) && dma_src_cnt_d1 == ({14'h0, dma_dst_cnt_du}) && |reg2hw.pad_left.q == 1'b1 }; assign top_dn_to_right_ex = { - |reg2hw.pad_left.q == 1'b0 && |reg2hw.pad_right.q == 1'b1 && dma_src_cnt_d1 == ({11'h0, reg2hw.pad_right.q} + {14'h0, dma_cnt_du}) + |reg2hw.pad_left.q == 1'b0 && |reg2hw.pad_right.q == 1'b1 && dma_src_cnt_d1 == ({11'h0, reg2hw.pad_right.q} + {14'h0, dma_dst_cnt_du}) }; assign top_dn_to_bottom_ex = { - |reg2hw.pad_left.q == 1'b0 && |reg2hw.pad_right.q == 1'b0 && |reg2hw.pad_bottom.q == 1'b1 && dma_src_cnt_d2 == ({11'h0, reg2hw.pad_bottom.q} + {14'h0, dma_cnt_du}) && dma_src_cnt_d1 == ({14'h0, dma_cnt_du}) + |reg2hw.pad_left.q == 1'b0 && |reg2hw.pad_right.q == 1'b0 && |reg2hw.pad_bottom.q == 1'b1 && dma_src_cnt_d2 == ({11'h0, reg2hw.pad_bottom.q} + {14'h0, dma_dst_cnt_du}) && dma_src_cnt_d1 == ({14'h0, dma_dst_cnt_du}) }; assign top_dn_to_idle = { |reg2hw.pad_left.q == 1'b0 && |reg2hw.pad_right.q == 1'b0 && |reg2hw.pad_bottom.q == 1'b0 && |dma_src_cnt_d2 == 1'b0 }; assign left_ex_to_left_dn = { - dma_src_cnt_d1 == ({1'h0, reg2hw.size_d1.q} + {11'h0, reg2hw.pad_right.q} + {14'h0, dma_cnt_du}) + dma_src_cnt_d1 == ({1'h0, reg2hw.size_d1.q} + {11'h0, reg2hw.pad_right.q} + {14'h0, dma_dst_cnt_du}) }; assign left_dn_to_left_ex = { - dma_src_cnt_d1 == ({14'h0, dma_cnt_du}) && dma_src_cnt_d2 != ({14'h0, dma_cnt_du} + {11'h0, reg2hw.pad_bottom.q}) && |reg2hw.pad_right.q == 1'b0 + dma_src_cnt_d1 == ({14'h0, dma_dst_cnt_du}) && dma_src_cnt_d2 != ({14'h0, dma_dst_cnt_du} + {11'h0, reg2hw.pad_bottom.q}) && |reg2hw.pad_right.q == 1'b0 }; assign left_dn_to_right_ex = { - |reg2hw.pad_right.q == 1'b1 && dma_src_cnt_d1 == ({11'h0, reg2hw.pad_right.q} + {14'h0, dma_cnt_du}) + |reg2hw.pad_right.q == 1'b1 && dma_src_cnt_d1 == ({11'h0, reg2hw.pad_right.q} + {14'h0, dma_dst_cnt_du}) }; assign left_dn_to_bottom_ex = { - |reg2hw.pad_right.q == 1'b0 && |reg2hw.pad_bottom.q == 1'b1 && dma_src_cnt_d2 == ({11'h0, reg2hw.pad_bottom.q} + {14'h0, dma_cnt_du}) && dma_src_cnt_d1 == ({14'h0, dma_cnt_du}) + |reg2hw.pad_right.q == 1'b0 && |reg2hw.pad_bottom.q == 1'b1 && dma_src_cnt_d2 == ({11'h0, reg2hw.pad_bottom.q} + {14'h0, dma_dst_cnt_du}) && dma_src_cnt_d1 == ({14'h0, dma_dst_cnt_du}) }; assign left_dn_to_idle = { |reg2hw.pad_right.q == 1'b0 && |reg2hw.pad_bottom.q == 1'b0 && |dma_src_cnt_d2 == 1'b0 }; assign right_ex_to_right_dn = { - dma_src_cnt_d1 == ({14'h0, dma_cnt_du}) && dma_src_cnt_d2 != ({11'h0, reg2hw.pad_bottom.q} + {14'h0, dma_cnt_du}) && |reg2hw.pad_left.q == 1'b0 + dma_src_cnt_d1 == ({14'h0, dma_dst_cnt_du}) && dma_src_cnt_d2 != ({11'h0, reg2hw.pad_bottom.q} + {14'h0, dma_dst_cnt_du}) && |reg2hw.pad_left.q == 1'b0 }; assign right_ex_to_left_ex = { - dma_src_cnt_d1 == ({14'h0, dma_cnt_du}) && dma_src_cnt_d2 != ({11'h0, reg2hw.pad_bottom.q} + {14'h0, dma_cnt_du}) && |reg2hw.pad_left.q == 1'b1 + dma_src_cnt_d1 == ({14'h0, dma_dst_cnt_du}) && dma_src_cnt_d2 != ({11'h0, reg2hw.pad_bottom.q} + {14'h0, dma_dst_cnt_du}) && |reg2hw.pad_left.q == 1'b1 }; assign right_ex_to_bottom_ex = { - |reg2hw.pad_bottom.q == 1'b1 && dma_src_cnt_d2 == ({11'h0, reg2hw.pad_bottom.q} + {14'h0, dma_cnt_du}) && dma_src_cnt_d1 == ({14'h0, dma_cnt_du}) + |reg2hw.pad_bottom.q == 1'b1 && dma_src_cnt_d2 == ({11'h0, reg2hw.pad_bottom.q} + {14'h0, dma_dst_cnt_du}) && dma_src_cnt_d1 == ({14'h0, dma_dst_cnt_du}) }; assign right_dn_to_right_ex = { - dma_src_cnt_d1 == ({11'h0, reg2hw.pad_right.q} + {14'h0, dma_cnt_du}) && |reg2hw.pad_left.q == 1'b0 + dma_src_cnt_d1 == ({11'h0, reg2hw.pad_right.q} + {14'h0, dma_dst_cnt_du}) && |reg2hw.pad_left.q == 1'b0 }; assign right_dn_to_idle = {|reg2hw.pad_bottom.q == 1'b0 && |dma_src_cnt_d2 == 1'b0}; assign bottom_ex_to_idle = { - dma_src_cnt_d1 == {14'h0, dma_cnt_du} && dma_src_cnt_d2 == {14'h0, dma_cnt_du} + dma_src_cnt_d1 == {14'h0, dma_dst_cnt_du} && dma_src_cnt_d2 == {14'h0, dma_dst_cnt_du} }; assign write_address = address_mode ? fifo_addr_output : write_ptr_reg; @@ -411,14 +427,14 @@ module dma #( read_ptr_reg <= read_ptr_reg + {26'h0, dma_src_d1_inc}; end else if (dma_conf_2d == 1'b1 && pad_cnt_on == 1'b0) begin if (read_ptr_update_sel == 1'b0) begin - if (dma_src_cnt_d1 == {14'h0, dma_cnt_du} && |dma_src_cnt_d2 == 1'b1) begin + if (dma_src_cnt_d1 == {14'h0, dma_src_cnt_du} && |dma_src_cnt_d2 == 1'b1) begin /* In this case, the d1 is almost finished, so we need to increment the pointer by sizeof(d1)*data_unit */ read_ptr_reg <= read_ptr_reg + {9'h0, dma_src_d2_inc}; end else begin read_ptr_reg <= read_ptr_reg + {26'h0, dma_src_d1_inc}; /* Increment of the d1 increment (stride) */ end end else begin - if (dma_src_cnt_d1 == {14'h0, dma_cnt_du} && |dma_src_cnt_d2 == 1'b1) begin + if (dma_src_cnt_d1 == {14'h0, dma_src_cnt_du} && |dma_src_cnt_d2 == 1'b1) begin /* In this case, the d1 is almost finished, so we need to increment the pointer by sizeof(d2)*data_unit */ read_ptr_reg <= src_ptr_reg; end else begin @@ -442,7 +458,7 @@ module dma #( if (dma_start == 1'b1) begin src_ptr_reg <= reg2hw.src_ptr.q + {26'h0, dma_src_d1_inc}; end else if (data_in_gnt == 1'b1 && dma_conf_2d == 1'b1 && pad_cnt_on == 1'b0 && read_ptr_update_sel == 1'b1 && - (dma_src_cnt_d1 == {14'h0, dma_cnt_du} && |dma_src_cnt_d2 == 1'b1)) begin + (dma_src_cnt_d1 == {14'h0, dma_src_cnt_du} && |dma_src_cnt_d2 == 1'b1)) begin src_ptr_reg <= src_ptr_reg + {26'h0, dma_src_d1_inc}; end end @@ -487,7 +503,7 @@ module dma #( if (dma_conf_1d == 1'b1) begin write_ptr_reg <= write_ptr_reg + {26'h0, dma_dst_d1_inc}; end else if (dma_conf_2d == 1'b1) begin - if (dma_dst_cnt_d1 == {14'h0, dma_cnt_du}) begin + if (dma_dst_cnt_d1 == {14'h0, dma_dst_cnt_du}) begin // In this case, the d1 is finished, so we need to increment the pointer by sizeof(d1)*data_unit*strides write_ptr_reg <= write_ptr_reg + {9'h0, dma_dst_d2_inc}; end else begin @@ -511,16 +527,16 @@ module dma #( end else if (data_in_gnt == 1'b1) begin if (dma_conf_1d == 1'b1) begin // 1D case - dma_src_cnt_d1 <= dma_src_cnt_d1 - {14'h0, dma_cnt_du}; + dma_src_cnt_d1 <= dma_src_cnt_d1 - {14'h0, dma_src_cnt_du}; end else if (dma_conf_2d == 1'b1) begin // 2D case - if (dma_src_cnt_d1 == {14'h0, dma_cnt_du}) begin + if (dma_src_cnt_d1 == {14'h0, dma_src_cnt_du}) begin // In this case, the d1 is finished, so we need to decrement the d2 size and reset the d2 size - dma_src_cnt_d2 <= dma_src_cnt_d2 - {14'h0, dma_cnt_du}; + dma_src_cnt_d2 <= dma_src_cnt_d2 - {14'h0, dma_src_cnt_du}; dma_src_cnt_d1 <= {1'h0, reg2hw.size_d1.q} + {11'h0, reg2hw.pad_left.q} + {11'h0, reg2hw.pad_right.q}; end else begin // In this case, the d1 isn't finished, so we need to decrement the d1 size - dma_src_cnt_d1 <= dma_src_cnt_d1 - {14'h0, dma_cnt_du}; + dma_src_cnt_d1 <= dma_src_cnt_d1 - {14'h0, dma_src_cnt_du}; end end end @@ -541,15 +557,15 @@ module dma #( end else if (data_out_gnt == 1'b1) begin if (dma_conf_1d == 1'b1) begin // 1D case - dma_dst_cnt_d1 <= dma_dst_cnt_d1 - {14'h0, dma_cnt_du}; + dma_dst_cnt_d1 <= dma_dst_cnt_d1 - {14'h0, dma_dst_cnt_du}; end else if (dma_conf_2d == 1'b1) begin // 2D case - if (dma_dst_cnt_d1 == {14'h0, dma_cnt_du}) begin + if (dma_dst_cnt_d1 == {14'h0, dma_dst_cnt_du}) begin // In this case, the d1 is finished, so we need to reset the d2 size dma_dst_cnt_d1 <= {1'h0, reg2hw.size_d1.q} + {11'h0, reg2hw.pad_left.q} + {11'h0, reg2hw.pad_right.q}; end else begin // In this case, the d1 isn't finished, so we need to decrement the d1 size - dma_dst_cnt_d1 <= dma_dst_cnt_d1 - {14'h0, dma_cnt_du}; + dma_dst_cnt_d1 <= dma_dst_cnt_d1 - {14'h0, dma_dst_cnt_du}; end end end @@ -571,9 +587,17 @@ module dma #( always_comb begin case (dst_data_type) - DMA_DATA_TYPE_WORD: dma_cnt_du = 3'h4; - DMA_DATA_TYPE_HALF_WORD: dma_cnt_du = 3'h2; - DMA_DATA_TYPE_BYTE, DMA_DATA_TYPE_BYTE_: dma_cnt_du = 3'h1; + DMA_DATA_TYPE_WORD: dma_dst_cnt_du = 3'h4; + DMA_DATA_TYPE_HALF_WORD: dma_dst_cnt_du = 3'h2; + DMA_DATA_TYPE_BYTE, DMA_DATA_TYPE_BYTE_: dma_dst_cnt_du = 3'h1; + endcase + end + + always_comb begin + case (src_data_type) + DMA_DATA_TYPE_WORD: dma_src_cnt_du = 3'h4; + DMA_DATA_TYPE_HALF_WORD: dma_src_cnt_du = 3'h2; + DMA_DATA_TYPE_BYTE, DMA_DATA_TYPE_BYTE_: dma_src_cnt_du = 3'h1; endcase end @@ -815,6 +839,14 @@ module dma #( // Pad counter flag logic always_comb begin : proc_pad_cnt_on case (pad_state_q) + PAD_IDLE: begin + if (idle_to_right_ex || idle_to_bottom_ex || idle_to_left_ex || idle_to_top_ex) begin + pad_cnt_on = 1'b1; + end else begin + pad_cnt_on = pad_fifo_on; + end + end + TOP_PAD_DONE: begin if (top_dn_to_right_ex) begin pad_cnt_on = 1'b1; @@ -934,6 +966,54 @@ module dma #( endcase end + /* Transaction IFR update */ + always_ff @(posedge clk_i, negedge rst_ni) begin : proc_ff_transaction_ifr + if (~rst_ni) begin + transaction_ifr <= '0; + end else if (reg2hw.interrupt_en.transaction_done.q == 1'b1) begin + // Enter here only if the transaction_done interrupt is enabled + if (dma_done == 1'b1) begin + transaction_ifr <= 1'b1; + end else if (reg2hw.transaction_ifr.re == 1'b1) begin + // If the IFR bit is read, we must clear the transaction_ifr + transaction_ifr <= 1'b0; + end + end + end + + /* Delayed transaction interrupt signals */ + always_ff @(posedge clk_i, negedge rst_ni) begin : proc_ff_intr + if (~rst_ni) begin + dma_done_intr_n <= '0; + end else begin + dma_done_intr_n <= dma_done_intr; + end + end + + /* Window IFR update */ + always_ff @(posedge clk_i, negedge rst_ni) begin : proc_ff_window_ifr + if (~rst_ni) begin + window_ifr <= '0; + end else if (reg2hw.interrupt_en.window_done.q == 1'b1) begin + // Enter here only if the window_done interrupt is enabled + if (dma_window_event == 1'b1) begin + window_ifr <= 1'b1; + end else if (reg2hw.window_ifr.re == 1'b1) begin + // If the IFR bit is read, we must clear the window_ifr + window_ifr <= 1'b0; + end + end + end + + /* Delayed window interrupt signals */ + always_ff @(posedge clk_i, negedge rst_ni) begin : proc_ff_window_intr + if (~rst_ni) begin + dma_window_intr_n <= '0; + end else begin + dma_window_intr_n <= dma_window_intr; + end + end + // Read master FSM always_comb begin : proc_dma_read_fsm_logic @@ -960,35 +1040,39 @@ module dma #( // Read one word DMA_READ_FSM_ON: begin // If all input data read exit - if (dma_conf_1d == 1'b1) begin - // 1D DMA case - if (|dma_src_cnt_d1 == 1'b0) begin - dma_read_fsm_n_state = DMA_READ_FSM_IDLE; - end else begin - dma_read_fsm_n_state = DMA_READ_FSM_ON; - // Wait if fifo is full, almost full (last data), or if the SPI RX does not have valid data (only in SPI mode 1). - if (fifo_full == 1'b0 && fifo_alm_full == 1'b0 && wait_for_rx == 1'b0) begin - data_in_req = 1'b1; - data_in_we = 1'b0; - data_in_be = 4'b1111; // always read all bytes - data_in_addr = read_ptr_reg; + if (ext_dma_stop_i == 1'b0) begin + if (dma_conf_1d == 1'b1) begin + // 1D DMA case + if (|dma_src_cnt_d1 == 1'b0) begin + dma_read_fsm_n_state = DMA_READ_FSM_IDLE; + end else begin + dma_read_fsm_n_state = DMA_READ_FSM_ON; + // Wait if fifo is full, almost full (last data), or if the SPI RX does not have valid data (only in SPI mode 1). + if (fifo_full == 1'b0 && fifo_alm_full == 1'b0 && wait_for_rx == 1'b0) begin + data_in_req = 1'b1; + data_in_we = 1'b0; + data_in_be = 4'b1111; // always read all bytes + data_in_addr = read_ptr_reg; + end end - end - end else if (dma_conf_2d == 1'b1) begin - // 2D DMA case: exit only if both 1d and 2d counters are at 0 - if (dma_src_cnt_d1 == {1'h0, reg2hw.size_d1.q} + {11'h0, reg2hw.pad_left.q} + {11'h0, reg2hw.pad_right.q} && |dma_src_cnt_d2 == 1'b0) begin - dma_read_fsm_n_state = DMA_READ_FSM_IDLE; - end else begin - // The read operation is the same in both cases - dma_read_fsm_n_state = DMA_READ_FSM_ON; - // Wait if fifo is full, almost full (last data), or if the SPI RX does not have valid data (only in SPI mode 1). - if (fifo_full == 1'b0 && fifo_alm_full == 1'b0 && wait_for_rx == 1'b0) begin - data_in_req = 1'b1; - data_in_we = 1'b0; - data_in_be = 4'b1111; // always read all bytes - data_in_addr = read_ptr_reg; + end else if (dma_conf_2d == 1'b1) begin + // 2D DMA case: exit only if both 1d and 2d counters are at 0 + if (dma_src_cnt_d1 == {1'h0, reg2hw.size_d1.q} + {11'h0, reg2hw.pad_left.q} + {11'h0, reg2hw.pad_right.q} && |dma_src_cnt_d2 == 1'b0) begin + dma_read_fsm_n_state = DMA_READ_FSM_IDLE; + end else begin + // The read operation is the same in both cases + dma_read_fsm_n_state = DMA_READ_FSM_ON; + // Wait if fifo is full, almost full (last data), or if the SPI RX does not have valid data (only in SPI mode 1). + if (fifo_full == 1'b0 && fifo_alm_full == 1'b0 && wait_for_rx == 1'b0) begin + data_in_req = 1'b1; + data_in_we = 1'b0; + data_in_be = 4'b1111; // always read all bytes + data_in_addr = read_ptr_reg; + end end end + end else begin + dma_read_fsm_n_state = DMA_READ_FSM_IDLE; end end endcase diff --git a/hw/ip/dma/rtl/dma_reg_pkg.sv b/hw/ip/dma/rtl/dma_reg_pkg.sv index 2f57afa3e..f2fe47617 100644 --- a/hw/ip/dma/rtl/dma_reg_pkg.sv +++ b/hw/ip/dma/rtl/dma_reg_pkg.sv @@ -94,6 +94,16 @@ package dma_reg_pkg; struct packed {logic q;} window_done; } dma_reg2hw_interrupt_en_reg_t; + typedef struct packed { + logic q; + logic re; + } dma_reg2hw_transaction_ifr_reg_t; + + typedef struct packed { + logic q; + logic re; + } dma_reg2hw_window_ifr_reg_t; + typedef struct packed { struct packed {logic d;} ready; struct packed {logic d;} window_done; @@ -104,38 +114,46 @@ package dma_reg_pkg; logic de; } dma_hw2reg_window_count_reg_t; + typedef struct packed {logic d;} dma_hw2reg_transaction_ifr_reg_t; + + typedef struct packed {logic d;} dma_hw2reg_window_ifr_reg_t; + // Register -> HW type typedef struct packed { - dma_reg2hw_src_ptr_reg_t src_ptr; // [283:252] - dma_reg2hw_dst_ptr_reg_t dst_ptr; // [251:220] - dma_reg2hw_addr_ptr_reg_t addr_ptr; // [219:188] - dma_reg2hw_size_d1_reg_t size_d1; // [187:171] - dma_reg2hw_size_d2_reg_t size_d2; // [170:154] - dma_reg2hw_status_reg_t status; // [153:150] - dma_reg2hw_src_ptr_inc_d1_reg_t src_ptr_inc_d1; // [149:144] - dma_reg2hw_src_ptr_inc_d2_reg_t src_ptr_inc_d2; // [143:121] - dma_reg2hw_dst_ptr_inc_d1_reg_t dst_ptr_inc_d1; // [120:115] - dma_reg2hw_dst_ptr_inc_d2_reg_t dst_ptr_inc_d2; // [114:92] - dma_reg2hw_slot_reg_t slot; // [91:60] - dma_reg2hw_src_data_type_reg_t src_data_type; // [59:58] - dma_reg2hw_dst_data_type_reg_t dst_data_type; // [57:56] - dma_reg2hw_sign_ext_reg_t sign_ext; // [55:55] - dma_reg2hw_mode_reg_t mode; // [54:53] - dma_reg2hw_dim_config_reg_t dim_config; // [52:52] - dma_reg2hw_dim_inv_reg_t dim_inv; // [51:51] - dma_reg2hw_pad_top_reg_t pad_top; // [50:44] - dma_reg2hw_pad_bottom_reg_t pad_bottom; // [43:37] - dma_reg2hw_pad_right_reg_t pad_right; // [36:30] - dma_reg2hw_pad_left_reg_t pad_left; // [29:23] - dma_reg2hw_window_size_reg_t window_size; // [22:10] - dma_reg2hw_window_count_reg_t window_count; // [9:2] - dma_reg2hw_interrupt_en_reg_t interrupt_en; // [1:0] + dma_reg2hw_src_ptr_reg_t src_ptr; // [287:256] + dma_reg2hw_dst_ptr_reg_t dst_ptr; // [255:224] + dma_reg2hw_addr_ptr_reg_t addr_ptr; // [223:192] + dma_reg2hw_size_d1_reg_t size_d1; // [191:175] + dma_reg2hw_size_d2_reg_t size_d2; // [174:158] + dma_reg2hw_status_reg_t status; // [157:154] + dma_reg2hw_src_ptr_inc_d1_reg_t src_ptr_inc_d1; // [153:148] + dma_reg2hw_src_ptr_inc_d2_reg_t src_ptr_inc_d2; // [147:125] + dma_reg2hw_dst_ptr_inc_d1_reg_t dst_ptr_inc_d1; // [124:119] + dma_reg2hw_dst_ptr_inc_d2_reg_t dst_ptr_inc_d2; // [118:96] + dma_reg2hw_slot_reg_t slot; // [95:64] + dma_reg2hw_src_data_type_reg_t src_data_type; // [63:62] + dma_reg2hw_dst_data_type_reg_t dst_data_type; // [61:60] + dma_reg2hw_sign_ext_reg_t sign_ext; // [59:59] + dma_reg2hw_mode_reg_t mode; // [58:57] + dma_reg2hw_dim_config_reg_t dim_config; // [56:56] + dma_reg2hw_dim_inv_reg_t dim_inv; // [55:55] + dma_reg2hw_pad_top_reg_t pad_top; // [54:48] + dma_reg2hw_pad_bottom_reg_t pad_bottom; // [47:41] + dma_reg2hw_pad_right_reg_t pad_right; // [40:34] + dma_reg2hw_pad_left_reg_t pad_left; // [33:27] + dma_reg2hw_window_size_reg_t window_size; // [26:14] + dma_reg2hw_window_count_reg_t window_count; // [13:6] + dma_reg2hw_interrupt_en_reg_t interrupt_en; // [5:4] + dma_reg2hw_transaction_ifr_reg_t transaction_ifr; // [3:2] + dma_reg2hw_window_ifr_reg_t window_ifr; // [1:0] } dma_reg2hw_t; // HW -> register type typedef struct packed { - dma_hw2reg_status_reg_t status; // [10:9] - dma_hw2reg_window_count_reg_t window_count; // [8:0] + dma_hw2reg_status_reg_t status; // [12:11] + dma_hw2reg_window_count_reg_t window_count; // [10:2] + dma_hw2reg_transaction_ifr_reg_t transaction_ifr; // [1:1] + dma_hw2reg_window_ifr_reg_t window_ifr; // [0:0] } dma_hw2reg_t; // Register offsets @@ -163,11 +181,17 @@ package dma_reg_pkg; parameter logic [BlockAw-1:0] DMA_WINDOW_SIZE_OFFSET = 7'h54; parameter logic [BlockAw-1:0] DMA_WINDOW_COUNT_OFFSET = 7'h58; parameter logic [BlockAw-1:0] DMA_INTERRUPT_EN_OFFSET = 7'h5c; + parameter logic [BlockAw-1:0] DMA_TRANSACTION_IFR_OFFSET = 7'h60; + parameter logic [BlockAw-1:0] DMA_WINDOW_IFR_OFFSET = 7'h64; // Reset values for hwext registers and their fields parameter logic [1:0] DMA_STATUS_RESVAL = 2'h1; parameter logic [0:0] DMA_STATUS_READY_RESVAL = 1'h1; parameter logic [0:0] DMA_STATUS_WINDOW_DONE_RESVAL = 1'h0; + parameter logic [0:0] DMA_TRANSACTION_IFR_RESVAL = 1'h0; + parameter logic [0:0] DMA_TRANSACTION_IFR_FLAG_RESVAL = 1'h0; + parameter logic [0:0] DMA_WINDOW_IFR_RESVAL = 1'h0; + parameter logic [0:0] DMA_WINDOW_IFR_FLAG_RESVAL = 1'h0; // Register index typedef enum int { @@ -194,11 +218,13 @@ package dma_reg_pkg; DMA_PAD_LEFT, DMA_WINDOW_SIZE, DMA_WINDOW_COUNT, - DMA_INTERRUPT_EN + DMA_INTERRUPT_EN, + DMA_TRANSACTION_IFR, + DMA_WINDOW_IFR } dma_id_e; // Register width information to check illegal writes - parameter logic [3:0] DMA_PERMIT[24] = '{ + parameter logic [3:0] DMA_PERMIT[26] = '{ 4'b1111, // index[ 0] DMA_SRC_PTR 4'b1111, // index[ 1] DMA_DST_PTR 4'b1111, // index[ 2] DMA_ADDR_PTR @@ -222,7 +248,9 @@ package dma_reg_pkg; 4'b0001, // index[20] DMA_PAD_LEFT 4'b0011, // index[21] DMA_WINDOW_SIZE 4'b0001, // index[22] DMA_WINDOW_COUNT - 4'b0001 // index[23] DMA_INTERRUPT_EN + 4'b0001, // index[23] DMA_INTERRUPT_EN + 4'b0001, // index[24] DMA_TRANSACTION_IFR + 4'b0001 // index[25] DMA_WINDOW_IFR }; endpackage diff --git a/hw/ip/dma/rtl/dma_reg_top.sv b/hw/ip/dma/rtl/dma_reg_top.sv index a57f9059e..a40048304 100644 --- a/hw/ip/dma/rtl/dma_reg_top.sv +++ b/hw/ip/dma/rtl/dma_reg_top.sv @@ -145,6 +145,10 @@ module dma_reg_top #( logic interrupt_en_window_done_qs; logic interrupt_en_window_done_wd; logic interrupt_en_window_done_we; + logic transaction_ifr_qs; + logic transaction_ifr_re; + logic window_ifr_qs; + logic window_ifr_re; // Register instances // R[src_ptr]: V(False) @@ -853,9 +857,41 @@ module dma_reg_top #( ); + // R[transaction_ifr]: V(True) + + prim_subreg_ext #( + .DW(1) + ) u_transaction_ifr ( + .re (transaction_ifr_re), + .we (1'b0), + .wd ('0), + .d (hw2reg.transaction_ifr.d), + .qre(reg2hw.transaction_ifr.re), + .qe (), + .q (reg2hw.transaction_ifr.q), + .qs (transaction_ifr_qs) + ); + + + // R[window_ifr]: V(True) + + prim_subreg_ext #( + .DW(1) + ) u_window_ifr ( + .re (window_ifr_re), + .we (1'b0), + .wd ('0), + .d (hw2reg.window_ifr.d), + .qre(reg2hw.window_ifr.re), + .qe (), + .q (reg2hw.window_ifr.q), + .qs (window_ifr_qs) + ); + + - logic [23:0] addr_hit; + logic [25:0] addr_hit; always_comb begin addr_hit = '0; addr_hit[0] = (reg_addr == DMA_SRC_PTR_OFFSET); @@ -882,6 +918,8 @@ module dma_reg_top #( addr_hit[21] = (reg_addr == DMA_WINDOW_SIZE_OFFSET); addr_hit[22] = (reg_addr == DMA_WINDOW_COUNT_OFFSET); addr_hit[23] = (reg_addr == DMA_INTERRUPT_EN_OFFSET); + addr_hit[24] = (reg_addr == DMA_TRANSACTION_IFR_OFFSET); + addr_hit[25] = (reg_addr == DMA_WINDOW_IFR_OFFSET); end assign addrmiss = (reg_re || reg_we) ? ~|addr_hit : 1'b0; @@ -912,7 +950,9 @@ module dma_reg_top #( (addr_hit[20] & (|(DMA_PERMIT[20] & ~reg_be))) | (addr_hit[21] & (|(DMA_PERMIT[21] & ~reg_be))) | (addr_hit[22] & (|(DMA_PERMIT[22] & ~reg_be))) | - (addr_hit[23] & (|(DMA_PERMIT[23] & ~reg_be))))); + (addr_hit[23] & (|(DMA_PERMIT[23] & ~reg_be))) | + (addr_hit[24] & (|(DMA_PERMIT[24] & ~reg_be))) | + (addr_hit[25] & (|(DMA_PERMIT[25] & ~reg_be))))); end assign src_ptr_we = addr_hit[0] & reg_we & !reg_error; @@ -991,6 +1031,10 @@ module dma_reg_top #( assign interrupt_en_window_done_we = addr_hit[23] & reg_we & !reg_error; assign interrupt_en_window_done_wd = reg_wdata[1]; + assign transaction_ifr_re = addr_hit[24] & reg_re & !reg_error; + + assign window_ifr_re = addr_hit[25] & reg_re & !reg_error; + // Read data return always_comb begin reg_rdata_next = '0; @@ -1094,6 +1138,14 @@ module dma_reg_top #( reg_rdata_next[1] = interrupt_en_window_done_qs; end + addr_hit[24]: begin + reg_rdata_next[0] = transaction_ifr_qs; + end + + addr_hit[25]: begin + reg_rdata_next[0] = window_ifr_qs; + end + default: begin reg_rdata_next = '1; end diff --git a/hw/ip/dma_subsystem/dma_subsystem.core b/hw/ip/dma_subsystem/dma_subsystem.core new file mode 100644 index 000000000..1fa1b17bf --- /dev/null +++ b/hw/ip/dma_subsystem/dma_subsystem.core @@ -0,0 +1,21 @@ +CAPI=2: + +name: "x-heep:ip:dma_subsystem" +description: "core-v-mini-mcu dma peripheral" + +# Copyright 2021 OpenHW Group +# Solderpad Hardware License, Version 2.1, see LICENSE.md for details. +# SPDX-License-Identifier: Apache-2.0 WITH SHL-2.1 + +filesets: + files_rtl: + depend: + - pulp-platform.org::common_cells + files: + - rtl/dma_subsystem.sv + file_type: systemVerilogSource + +targets: + default: + filesets: + - files_rtl diff --git a/hw/ip/dma_subsystem/dma_subsystem.vlt b/hw/ip/dma_subsystem/dma_subsystem.vlt new file mode 100644 index 000000000..a914d3556 --- /dev/null +++ b/hw/ip/dma_subsystem/dma_subsystem.vlt @@ -0,0 +1,10 @@ +// Copyright 2022 EPFL +// Solderpad Hardware License, Version 2.1, see LICENSE.md for details. +// SPDX-License-Identifier: Apache-2.0 WITH SHL-2.1 + +`verilator_config + +lint_off -rule UNUSED -file "*/rtl/dma_subsystem.sv" -match "Signal is not used: 'data_out_rvalid'" +lint_off -rule UNUSED -file "*/rtl/dma_subsystem.sv" -match "Signal is not used: 'data_out_rdata'" + +lint_off -rule UNUSED -file "*/rtl/dma_subsystem.sv" -match "Bits of signal are not used: *" diff --git a/hw/ip/dma_subsystem/rtl/dma_subsystem.sv b/hw/ip/dma_subsystem/rtl/dma_subsystem.sv new file mode 100644 index 000000000..22274b695 --- /dev/null +++ b/hw/ip/dma_subsystem/rtl/dma_subsystem.sv @@ -0,0 +1,198 @@ +/* + * Copyright 2024 EPFL + * Solderpad Hardware License, Version 2.1, see LICENSE.md for details. + * SPDX-License-Identifier: Apache-2.0 WITH SHL-2.1 + * + * Author: Tommaso Terzano + * + * Info: DMA subsystem, it instantiates 1 to 8 DMA channels and manages the data transfers. + */ + + +module dma_subsystem #( + parameter type reg_req_t = logic, + parameter type reg_rsp_t = logic, + parameter type obi_req_t = logic, + parameter type obi_resp_t = logic, + parameter int unsigned GLOBAL_SLOT_NUM = 0, + parameter int unsigned EXT_SLOT_NUM = 0 +) ( + input logic clk_i, + input logic rst_ni, + + input reg_req_t reg_req_i, + output reg_rsp_t reg_rsp_o, + + output obi_req_t dma_read_ch0_req_o, + input obi_resp_t dma_read_ch0_resp_i, + + output obi_req_t dma_write_ch0_req_o, + input obi_resp_t dma_write_ch0_resp_i, + + output obi_req_t dma_addr_ch0_req_o, + input obi_resp_t dma_addr_ch0_resp_i, + + input logic [GLOBAL_SLOT_NUM-1:0] global_trigger_slot_i, + input logic [EXT_SLOT_NUM-1:0] ext_trigger_slot_i, + + input logic [core_v_mini_mcu_pkg::DMA_CH_NUM-1:0] ext_dma_stop_i, + + output dma_done_intr_o, + output dma_window_intr_o +); + + + import obi_pkg::*; + import reg_pkg::*; + + /*_________________________________________________________________________________________________________________________________ */ + + /* Signals declaration */ + + /* Masters requests to the bus */ + obi_req_t [core_v_mini_mcu_pkg::DMA_CH_NUM-1:0] xbar_write_req; + obi_req_t [core_v_mini_mcu_pkg::DMA_CH_NUM-1:0] xbar_read_req; + obi_req_t [core_v_mini_mcu_pkg::DMA_CH_NUM-1:0] xbar_address_req; + + /* Masters response from the bus*/ + obi_resp_t [core_v_mini_mcu_pkg::DMA_CH_NUM-1:0] xbar_write_resp; + obi_resp_t [core_v_mini_mcu_pkg::DMA_CH_NUM-1:0] xbar_read_resp; + obi_resp_t [core_v_mini_mcu_pkg::DMA_CH_NUM-1:0] xbar_address_resp; + + /* Interrupt signals */ + logic [core_v_mini_mcu_pkg::DMA_CH_NUM-1:0] dma_trans_done; + logic [core_v_mini_mcu_pkg::DMA_CH_NUM-1:0] dma_window_done; + + /* Register interfaces from register demux to DMAs */ + reg_pkg::reg_req_t [core_v_mini_mcu_pkg::DMA_CH_NUM-1:0] submodules_req; + reg_pkg::reg_rsp_t [core_v_mini_mcu_pkg::DMA_CH_NUM-1:0] submodules_rsp; + + /*_________________________________________________________________________________________________________________________________ */ + + /* Module instantiation */ + + /* DMA modules */ + generate + for (genvar i = 0; i < core_v_mini_mcu_pkg::DMA_CH_NUM; i++) begin : dma_i_gen + dma #( + .reg_req_t (reg_pkg::reg_req_t), + .reg_rsp_t (reg_pkg::reg_rsp_t), + .obi_req_t (obi_pkg::obi_req_t), + .obi_resp_t(obi_pkg::obi_resp_t), + .SLOT_NUM (GLOBAL_SLOT_NUM + 2) + ) dma_i ( + .clk_i, + .rst_ni, + .ext_dma_stop_i(ext_dma_stop_i[i]), + .reg_req_i(submodules_req[i]), + .reg_rsp_o(submodules_rsp[i]), + .dma_read_ch0_req_o(xbar_read_req[i]), + .dma_read_ch0_resp_i(xbar_read_resp[i]), + .dma_write_ch0_req_o(xbar_write_req[i]), + .dma_write_ch0_resp_i(xbar_write_resp[i]), + .dma_addr_ch0_req_o(xbar_address_req[i]), + .dma_addr_ch0_resp_i(xbar_address_resp[i]), + .trigger_slot_i({ + ext_trigger_slot_i[2*i+1], ext_trigger_slot_i[2*i], global_trigger_slot_i + }), + .dma_done_intr_o(dma_trans_done[i]), + .dma_window_intr_o(dma_window_done[i]) + ); + end + endgenerate + + + generate + if (core_v_mini_mcu_pkg::DMA_CH_NUM > 1) begin : xbar_varlat_n_to_one_gen + + /* Register interface routing signals */ + logic [core_v_mini_mcu_pkg::DMA_CH_PORT_SEL_WIDTH-1:0] submodules_select; + + /* Read, write & address mode operations xbar*/ + xbar_varlat_n_to_one #( + .XBAR_NMASTER(core_v_mini_mcu_pkg::DMA_CH_NUM) + ) xbar_read_i ( + .clk_i (clk_i), + .rst_ni (rst_ni), + .master_req_i (xbar_read_req), + .master_resp_o(xbar_read_resp), + .slave_req_o (dma_read_ch0_req_o), + .slave_resp_i (dma_read_ch0_resp_i) + ); + + xbar_varlat_n_to_one #( + .XBAR_NMASTER(core_v_mini_mcu_pkg::DMA_CH_NUM) + ) xbar_write_i ( + .clk_i (clk_i), + .rst_ni (rst_ni), + .master_req_i (xbar_write_req), + .master_resp_o(xbar_write_resp), + .slave_req_o (dma_write_ch0_req_o), + .slave_resp_i (dma_write_ch0_resp_i) + ); + + xbar_varlat_n_to_one #( + .XBAR_NMASTER(core_v_mini_mcu_pkg::DMA_CH_NUM) + ) xbar_address_i ( + .clk_i (clk_i), + .rst_ni (rst_ni), + .master_req_i (xbar_address_req), + .master_resp_o(xbar_address_resp), + .slave_req_o (dma_addr_ch0_req_o), + .slave_resp_i (dma_addr_ch0_resp_i) + ); + + /* Internal address decoder */ + addr_decode #( + .NoIndices(core_v_mini_mcu_pkg::DMA_CH_NUM), + .NoRules(core_v_mini_mcu_pkg::DMA_CH_NUM), + .addr_t(logic [7:0]), + .rule_t(addr_map_rule_pkg::addr_map_rule_8bit_t) + ) addr_dec_i ( + .addr_i(reg_req_i.addr[15:8]), + .addr_map_i(core_v_mini_mcu_pkg::DMA_ADDR_RULES), + .idx_o(submodules_select), + .dec_valid_o(), + .dec_error_o(), + .en_default_idx_i(1'b0), + .default_idx_i('0) + ); + + /* Register demux */ + reg_demux #( + .NoPorts(core_v_mini_mcu_pkg::DMA_CH_NUM), + .req_t (reg_pkg::reg_req_t), + .rsp_t (reg_pkg::reg_rsp_t) + ) reg_demux_i ( + .clk_i, + .rst_ni, + .in_select_i(submodules_select), + .in_req_i(reg_req_i), + .in_rsp_o(reg_rsp_o), + .out_req_o(submodules_req), + .out_rsp_i(submodules_rsp) + ); + + end else begin + + /* Bus ports routing in the case of a single DMA */ + assign dma_read_ch0_req_o = xbar_read_req[0]; + assign xbar_read_resp[0] = dma_read_ch0_resp_i; + assign dma_write_ch0_req_o = xbar_write_req[0]; + assign xbar_write_resp[0] = dma_write_ch0_resp_i; + assign dma_addr_ch0_req_o = xbar_address_req[0]; + assign xbar_address_resp[0] = dma_addr_ch0_resp_i; + assign submodules_req[0] = reg_req_i; + assign reg_rsp_o = submodules_rsp[0]; + end + endgenerate + + + /*_________________________________________________________________________________________________________________________________ */ + + /* Signal assignments */ + + assign dma_done_intr_o = |(dma_trans_done); + assign dma_window_intr_o = |(dma_window_done); + +endmodule diff --git a/hw/ip/i2s/rtl/i2s_rx_channel.sv b/hw/ip/i2s/rtl/i2s_rx_channel.sv index 3c0066a77..52e48f9d5 100644 --- a/hw/ip/i2s/rtl/i2s_rx_channel.sv +++ b/hw/ip/i2s/rtl/i2s_rx_channel.sv @@ -153,7 +153,7 @@ module i2s_rx_channel #( // worst case drop even number of samples always_ff @(posedge sck_i, negedge rst_ni) begin if (~rst_ni) begin - last_data_ws <= ~start_channel_i; + last_data_ws <= 1'b0; end else begin if (~en) begin last_data_ws <= ~start_channel_i; diff --git a/hw/ip/pdm2pcm/rtl/fir.sv b/hw/ip/pdm2pcm/rtl/fir.sv index 6d22d673e..ce17a7a66 100644 --- a/hw/ip/pdm2pcm/rtl/fir.sv +++ b/hw/ip/pdm2pcm/rtl/fir.sv @@ -48,7 +48,9 @@ module fir #( genvar k; generate for (k = 0; k < TOTCOEFS; k = k + 1) begin : coeffs_mapping - assign coeffs[k] = freecoeffs[int'($floor($sqrt($pow((TOTCOEFS-1)/2.0-k, 2))))]; + // Remove $pow function to avoid errors in Synopsys DC + // assign coeffs[k] = freecoeffs[int'($floor($sqrt($pow((TOTCOEFS-1)/2.0-k, 2))))]; + assign coeffs[k] = freecoeffs[int'($floor($sqrt(((TOTCOEFS-1)/2.0-k)*((TOTCOEFS-1)/2.0-k))))]; end endgenerate diff --git a/hw/ip/pdm2pcm/rtl/halfband.sv b/hw/ip/pdm2pcm/rtl/halfband.sv index 9d9355c8d..a0710c586 100644 --- a/hw/ip/pdm2pcm/rtl/halfband.sv +++ b/hw/ip/pdm2pcm/rtl/halfband.sv @@ -63,7 +63,11 @@ module halfband #( genvar k; generate for (k = 0; k < TOTCOEFS; k = k + 1) begin : coeffs_mapping - assign coeffs[k] = lessfreecoeffs[int'($floor($sqrt($pow((TOTCOEFS-1)/2.0-k, 2))))]; + // Remove $pow function to avoid errors in Synopsys DC + // assign coeffs[k] = lessfreecoeffs[int'($floor($sqrt($pow((TOTCOEFS-1)/2.0-k, 2))))]; + assign coeffs[k] = lessfreecoeffs[int'($floor( + $sqrt(((TOTCOEFS-1)/2.0-k)*((TOTCOEFS-1)/2.0-k)) + ))]; end endgenerate diff --git a/hw/ip/power_manager/data/power_manager.hjson.tpl b/hw/ip/power_manager/data/power_manager.hjson.tpl index cdfd6b2dc..0d3362558 100644 --- a/hw/ip/power_manager/data/power_manager.hjson.tpl +++ b/hw/ip/power_manager/data/power_manager.hjson.tpl @@ -19,12 +19,22 @@ } { name: "RESTORE_ADDRESS", - desc: "Restore xddress value", + desc: "Restore address value", resval: "0x00000000" swaccess: "rw", hwaccess: "hro", fields: [ - { bits: "31:0", name: "RESTORE_XDDRESS", desc: "Restore xddress Reg, used by BOOTROM" } + { bits: "31:0", name: "RESTORE_ADDRESS", desc: "Restore address Reg, used by BOOTROM" } + ] + } + + { name: "GLOBAL_POINTER", + desc: "Global Pointer value", + resval: "0x00000000" + swaccess: "rw", + hwaccess: "hro", + fields: [ + { bits: "31:0", name: "GLOBAL_POINTER", desc: "Global Reg, used by power manager HAL" } ] } diff --git a/hw/simulation/simulation.vlt b/hw/simulation/simulation.vlt index 79672c20a..0b30dd69b 100644 --- a/hw/simulation/simulation.vlt +++ b/hw/simulation/simulation.vlt @@ -6,3 +6,4 @@ lint_off -rule UNOPTFLAT -file "*/hw/simulation/pad_cell_*.sv" -match "Signal unoptimizable*" lint_off -rule UNUSED -file "*/hw/simulation/sram_wrapper.sv" -match "Signal is not used: 'pwrgate_ni*" +lint_off -rule UNDRIVEN -file "*/hw/simulation/sram_wrapper.sv" -match "Signal is not driven: 'pwrgate_ack_no*" diff --git a/hw/simulation/sram_wrapper.sv b/hw/simulation/sram_wrapper.sv index 1d6119580..0986b3e2c 100644 --- a/hw/simulation/sram_wrapper.sv +++ b/hw/simulation/sram_wrapper.sv @@ -24,6 +24,7 @@ module sram_wrapper #( input logic [3:0] be_i, // power manager signals that goes to the ASIC macros input logic pwrgate_ni, + output logic pwrgate_ack_no, input logic set_retentive_ni, // output ports output logic [31:0] rdata_o diff --git a/hw/system/x_heep_system.sv.tpl b/hw/system/x_heep_system.sv.tpl index d268d5e4c..e77bd1b44 100644 --- a/hw/system/x_heep_system.sv.tpl +++ b/hw/system/x_heep_system.sv.tpl @@ -48,8 +48,9 @@ module x_heep_system output logic [31:0] exit_value_o, - input logic ext_dma_slot_tx_i, - input logic ext_dma_slot_rx_i, + input logic [core_v_mini_mcu_pkg::DMA_CH_NUM-1:0] ext_dma_slot_tx_i, + input logic [core_v_mini_mcu_pkg::DMA_CH_NUM-1:0] ext_dma_slot_rx_i, + input logic [core_v_mini_mcu_pkg::DMA_CH_NUM-1:0] ext_dma_stop_i, // eXtension interface if_xif.cpu_compressed xif_compressed_if, @@ -82,8 +83,6 @@ ${pad.x_heep_system_interface} logic cpu_subsystem_powergate_switch_ack_n; logic peripheral_subsystem_powergate_switch_n; logic peripheral_subsystem_powergate_switch_ack_n; - logic [core_v_mini_mcu_pkg::NUM_BANKS-1:0] memory_subsystem_banks_powergate_switch_n; - logic [core_v_mini_mcu_pkg::NUM_BANKS-1:0] memory_subsystem_banks_powergate_switch_ack_n; // PAD controller reg_req_t pad_req; @@ -102,6 +101,11 @@ ${pad.x_heep_system_interface} ${pad.internal_signals} % endfor +`ifdef FPGA_SYNTHESIS + assign cpu_subsystem_powergate_switch_ack_n = cpu_subsystem_powergate_switch_n; + assign peripheral_subsystem_powergate_switch_ack_n = peripheral_subsystem_powergate_switch_n; +`endif + core_v_mini_mcu #( .COREV_PULP(COREV_PULP), .FPU(FPU), @@ -138,6 +142,7 @@ ${pad.core_v_mini_mcu_bonding} .ext_dma_write_ch0_resp_i, .ext_dma_addr_ch0_req_o, .ext_dma_addr_ch0_resp_i, + .ext_dma_stop_i, .ext_peripheral_slave_req_o, .ext_peripheral_slave_resp_i, .ext_debug_req_o(ext_debug_req), @@ -146,8 +151,6 @@ ${pad.core_v_mini_mcu_bonding} .cpu_subsystem_powergate_switch_ack_ni(cpu_subsystem_powergate_switch_ack_n), .peripheral_subsystem_powergate_switch_no(peripheral_subsystem_powergate_switch_n), .peripheral_subsystem_powergate_switch_ack_ni(peripheral_subsystem_powergate_switch_ack_n), - .memory_subsystem_banks_powergate_switch_no(memory_subsystem_banks_powergate_switch_n), - .memory_subsystem_banks_powergate_switch_ack_ni(memory_subsystem_banks_powergate_switch_ack_n), .external_subsystem_powergate_switch_no, .external_subsystem_powergate_switch_ack_ni, .external_subsystem_powergate_iso_no, diff --git a/mcu_cfg.hjson b/mcu_cfg.hjson index 2e6f33f19..df55d1609 100644 --- a/mcu_cfg.hjson +++ b/mcu_cfg.hjson @@ -40,6 +40,8 @@ dma: { offset: 0x00030000, length: 0x00010000, + ch_length: 0x100, + num_channels: 0x1, path: "./hw/ip/dma/data/dma.hjson" }, power_manager: { diff --git a/mcu_cfg_minimal.hjson b/mcu_cfg_minimal.hjson index 271bcf7ac..0be257d7b 100644 --- a/mcu_cfg_minimal.hjson +++ b/mcu_cfg_minimal.hjson @@ -40,6 +40,8 @@ dma: { offset: 0x00030000, length: 0x00010000, + ch_length: 0x100, + num_channels: 0x1, path: "./hw/ip/dma/data/dma.hjson" }, power_manager: { diff --git a/scripts/synthesis/dc_shell/dc_script.tcl b/scripts/synthesis/dc_shell/dc_script.tcl index 8edeb7f96..3a246ab80 100644 --- a/scripts/synthesis/dc_shell/dc_script.tcl +++ b/scripts/synthesis/dc_shell/dc_script.tcl @@ -10,11 +10,15 @@ remove_design -all source ${SET_LIBS} +define_design_lib WORK -path ./work + source ${READ_SOURCES}.tcl elaborate ${TOP_MODULE} link +load_upf ../../../core-v-mini-mcu.dc.upf + write -f ddc -hierarchy -output ${REPORT_DIR}/precompiled.ddc source ${CONSTRAINTS} diff --git a/sw/CMakeLists.txt b/sw/CMakeLists.txt index 7b6b70c42..cdb2a8f76 100644 --- a/sw/CMakeLists.txt +++ b/sw/CMakeLists.txt @@ -42,7 +42,7 @@ if(NOT WIN32) endif() # set the project name -project(${PROJECT} ASM C) +project(${PROJECT} ASM C CXX) # set the required CMake standard set(CMAKE_CXX_STANDARD 14) @@ -89,17 +89,18 @@ FOREACH(file_path ${new_list}) # Add it to the string format list SET(dir_list_str ${dir_list_str} "-I ${dir_path} \ ") - string(REPLACE ";" "" dir_list_str ${dir_list_str}) - + # Add it to the header list SET(h_dir_list_ ${h_dir_list_} "${dir_path}") endif() ENDFOREACH() -LIST(REMOVE_DUPLICATES dir_list_str) +LIST(REMOVE_DUPLICATES dir_list_str) +string(REPLACE ";" "" dir_list_str ${dir_list_str}) #This has been moved here to avoid multiple includes of the same path, see: https://github.com/esl-epfl/x-heep/issues/470 LIST(REMOVE_DUPLICATES h_dir_list_) + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # Add list to the directories to be included @@ -108,8 +109,7 @@ SET(INCLUDE_FOLDERS "-I ${RISCV}/${COMPILER_PREFIX}elf/include \ -I ${RISCV}/${COMPILER_PREFIX}elf/include/ \ -I ${ROOT_PROJECT} \ -I ${SOURCE_PATH} \ - ${dir_list_str}") - + ${dir_list_str}") ####################################################################### # FIND SOURCE FILES TO BE INCLUDED @@ -124,14 +124,16 @@ FILE(GLOB_RECURSE c_files FOLLOW_SYMLINKS ${SOURCE_PATH}*.c) FILE(GLOB_RECURSE s_files FOLLOW_SYMLINKS ${SOURCE_PATH}*.s) # Make a list of the .S source files that need to be linked FILE(GLOB_RECURSE S_files FOLLOW_SYMLINKS ${SOURCE_PATH}*.S) -SET(new_list ${c_files} ${s_files} ${S_files}) +# Make a list of the .cpp source files that need to be linked +FILE(GLOB_RECURSE cpp_files FOLLOW_SYMLINKS ${SOURCE_PATH}*.cpp) +SET(new_list ${c_files} ${s_files} ${S_files} ${cpp_files}) SET( c_dir_list "" ) SET( app_found 0 ) FOREACH(file_path IN LISTS new_list) SET(add 0) # This variable is set to 1 if the file_pth needs to be added to the list if(${file_path} MATCHES "${SOURCE_PATH}device/") - if(NOT ${file_path} MATCHES "\\.S$") + if(NOT ${file_path} MATCHES ".*/crt/.*") SET(add 1) endif() elseif( (${file_path} MATCHES "${SOURCE_PATH}external/") AND ( NOT ${file_path} MATCHES "exclude" ) ) @@ -162,16 +164,18 @@ if( app_found EQUAL 0 ) FILE(GLOB_RECURSE s_files FOLLOW_SYMLINKS ${SOURCE_PATH}*.s) # Make a list of the .S source files that need to be linked FILE(GLOB_RECURSE S_files FOLLOW_SYMLINKS ${SOURCE_PATH}*.S) - SET(new_list ${c_files} ${s_files} ${S_files}) + # Make a list of the .cpp source files that need to be linked + FILE(GLOB_RECURSE cpp_files FOLLOW_SYMLINKS ${SOURCE_PATH}*.cpp) + SET(new_list ${c_files} ${s_files} ${S_files} ${cpp_files}) SET(c_dir_list "") FOREACH(file_path IN LISTS new_list) SET(add 0) # This variable is set to 1 if the file_pth needs to be added to the list if(${file_path} MATCHES "${ROOT_PROJECT}device/") - if(NOT ${file_path} MATCHES "\\.S$") + if(NOT ${file_path} MATCHES ".*/crt/.*") SET(add 1) endif() - elseif( ( ${file_path} MATCHES "${ROOT_PROJECT}/applications/${PROJECT}/" ) AND ( NOT ${file_path} MATCHES "${ROOT_PROJECT}applications/${PROJECT}/.*${MAINFILE}\." ) AND ( NOT ${file_path} MATCHES "exclude" ) ) + elseif( ( ${file_path} MATCHES "${ROOT_PROJECT}applications/${PROJECT}/" ) AND ( NOT ${file_path} MATCHES "${ROOT_PROJECT}applications/${PROJECT}/.*${MAINFILE}\." ) AND ( NOT ${file_path} MATCHES "exclude" ) ) SET(add 1) endif() @@ -182,10 +186,11 @@ if( app_found EQUAL 0 ) endif() ENDFOREACH() -endif() - + endif() + LIST(REMOVE_DUPLICATES c_dir_list) + ####################################################################### # DETERMINE IF APP IS EXTERNAL ####################################################################### @@ -282,7 +287,7 @@ message( "${Magenta}Target: ${TARGET}${ColourReset}") SET(LINKED_FILES "${LIB_CRT_P} \ ${LIB_VCTR_P} \ ${LIB_CRT_EXT_P} \ - ${c_dir_list}") + ${c_dir_list}") message( "${Magenta}Linked files: ${LINKED_FILES}${ColourReset}") @@ -341,6 +346,7 @@ else() ") endif() set(CMAKE_C_FLAGS ${COMPILER_LINKER_FLAGS}) +set(CMAKE_CXX_FLAGS ${COMPILER_LINKER_FLAGS}) if (${COMPILER} MATCHES "clang") set( CMAKE_C_FLAGS "${CMAKE_C_FLAGS} --target=riscv32 \ @@ -362,8 +368,18 @@ endif() ####################################################################### # SET TARGETS ####################################################################### - -set(SOURCES ${SOURCE_PATH}applications/${PROJECT}/${MAINFILE}.c) +if (${COMPILER} MATCHES "gcc") + set(SOURCES ${SOURCE_PATH}applications/${PROJECT}/${MAINFILE}.c) + if(NOT EXISTS ${SOURCES}) + # If main.c does not exist, set the source file to main.cpp + set(SOURCES ${SOURCE_PATH}applications/${PROJECT}/${MAINFILE}.cpp) + message("${Red}cpp SOURCES set") + endif() +elseif(${COMPILER} MATCHES "clang") + set(SOURCES ${SOURCE_PATH}applications/${PROJECT}/${MAINFILE}.c) +elseif(${COMPILER} MATCHES "g\\+\\+.*$") + set(SOURCES ${SOURCE_PATH}applications/${PROJECT}/${MAINFILE}.cpp) +endif() # add the executable add_executable(${MAINFILE}.elf ${SOURCES}) @@ -393,6 +409,7 @@ SET(CMAKE_EXE_LINKER_FLAGS "-T ${LINKER_SCRIPT} \ -Wl,-Map=${MAINFILE}.map \ -L ${RISCV}/${COMPILER_PREFIX}elf/lib \ -lc -lm -lgcc -flto \ + -fpermissive -fno-rtti -fno-exceptions -fno-threadsafe-statics \ -ffunction-sections -fdata-sections -specs=nano.specs") message( "${Magenta}Lib Folder RISCV-GCC: ${RISCV}/${COMPILER_PREFIX}elf/lib${ColourReset}") @@ -448,12 +465,31 @@ add_custom_command(TARGET ${MAINFILE}.elf POST_BUILD COMMENT "Invoking: Hexdump") # Pre-processing command to create disassembly for each source file -foreach (SRC_MODULE ${MAINFILE} ) +if ((${COMPILER} MATCHES "gcc") OR (${COMPILER} MATCHES "clang")) + if(EXISTS ${SOURCE_PATH}applications/${PROJECT}/${MAINFILE}.c) + foreach (SRC_MODULE ${MAINFILE} ) + add_custom_command(TARGET ${MAINFILE}.elf + PRE_LINK + COMMAND ${CMAKE_OBJDUMP} -S ${ROOT_PROJECT}build/CMakeFiles/${MAINFILE}.elf.dir/${OBJ_PATH}applications/${PROJECT}/${SRC_MODULE}.c.obj > ${SRC_MODULE}.s + COMMENT "Invoking: GCC C Disassemble ( CMakeFiles/${MAINFILE}.dir/${SRC_MODULE}.c.obj)") + endforeach() + else() #main.cpp targets + foreach (SRC_MODULE ${MAINFILE} ) + add_custom_command(TARGET ${MAINFILE}.elf + PRE_LINK + COMMAND ${CMAKE_OBJDUMP} -S ${ROOT_PROJECT}build/CMakeFiles/${MAINFILE}.elf.dir/${OBJ_PATH}applications/${PROJECT}/${SRC_MODULE}.cpp.obj > ${SRC_MODULE}.s + COMMENT "Invoking: GCC CPP Disassemble ( CMakeFiles/${MAINFILE}.dir/${SRC_MODULE}.cpp.obj)") + endforeach() + endif() +elseif(${COMPILER} MATCHES "g\\+\\+.*$") + foreach (SRC_MODULE ${MAINFILE} ) add_custom_command(TARGET ${MAINFILE}.elf PRE_LINK - COMMAND ${CMAKE_OBJDUMP} -S ${ROOT_PROJECT}build/CMakeFiles/${MAINFILE}.elf.dir/${OBJ_PATH}applications/${PROJECT}/${SRC_MODULE}.c.obj > ${SRC_MODULE}.s - COMMENT "Invoking: Disassemble ( CMakeFiles/${MAINFILE}.dir/${SRC_MODULE}.c.obj)") -endforeach() + COMMAND ${CMAKE_OBJDUMP} -S ${ROOT_PROJECT}build/CMakeFiles/${MAINFILE}.elf.dir/${OBJ_PATH}applications/${PROJECT}/${SRC_MODULE}.cpp.obj > ${SRC_MODULE}.s + COMMENT "Invoking: G++ Disassemble ( CMakeFiles/${MAINFILE}.dir/${SRC_MODULE}.cpp.obj)") + endforeach() +endif() + # Adding gdb command - TBD #add_custom_target(gdb DEPENDS ${MAINFILE}.elf) @@ -462,4 +498,4 @@ endforeach() SET(DCMAKE_EXPORT_COMPILE_COMMANDS ON) -#message( FATAL_ERROR "You can not do this at all, CMake will exit." ) +#message( FATAL_ERROR "You can not do this at all, CMake will exit." ) \ No newline at end of file diff --git a/sw/Makefile b/sw/Makefile index a36f5ead2..e327138b5 100644 --- a/sw/Makefile +++ b/sw/Makefile @@ -59,7 +59,7 @@ $(info $$You are fetching sources from $(source_path) ) SOURCE_PATH = $(source_path)/ ROOT_PROJECT = $(mkfile_path)/ INC_FOLDERS = $(mkfile_path)/device/target/$(TARGET)/ -LINK_FOLDER = $(mkfile_path)/linker +LINK_FOLDER ?= $(mkfile_path)/linker # CMake keyword CMAKE_DIR=cmake diff --git a/sw/applications/example_ams_peripheral/main.c b/sw/applications/example_ams_peripheral/main.c index accf4c4db..817cbff61 100644 --- a/sw/applications/example_ams_peripheral/main.c +++ b/sw/applications/example_ams_peripheral/main.c @@ -10,7 +10,7 @@ #include "ams_regs.h" #include "mmio.h" -#ifdef TARGET_PYNQ_Z2 +#ifdef TARGET_IS_FPGA #error ( "This app does NOT work on the FPGA as it relies on the simulator testbench" ) #endif diff --git a/sw/applications/example_cpp/MyClass.cpp b/sw/applications/example_cpp/MyClass.cpp new file mode 100644 index 000000000..00d1b5767 --- /dev/null +++ b/sw/applications/example_cpp/MyClass.cpp @@ -0,0 +1,21 @@ + +extern "C" { + #include + #include +} + +#include "MyClass.hpp" + +MyClass::MyClass(int initialValue) : value(initialValue) {} + +void MyClass::setValue(int newValue) { + value = newValue; +} + +int MyClass::getValue() { + return value*5; +} + +void MyClass::printValue() { + printf("Value: %d\n\r", value); +} diff --git a/sw/applications/example_cpp/MyClass.hpp b/sw/applications/example_cpp/MyClass.hpp new file mode 100644 index 000000000..829228782 --- /dev/null +++ b/sw/applications/example_cpp/MyClass.hpp @@ -0,0 +1,15 @@ +#ifndef MYCLASS_HPP +#define MYCLASS_HPP + +class MyClass { +public: + MyClass(int initialValue); // Constructor + void setValue(int newValue); // Setter + int getValue(); // Getter + void printValue(); // Method to print the value + +private: + int value; +}; + +#endif // MYCLASS_HPP diff --git a/sw/applications/example_cpp/main.c b/sw/applications/example_cpp/main.c deleted file mode 100644 index bbe2f0717..000000000 --- a/sw/applications/example_cpp/main.c +++ /dev/null @@ -1,27 +0,0 @@ -/* - * Copyright 2020 ETH Zurich - * - * 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. - * - * Author: Robert Balas - */ - -#include -#include - -int main(int argc, char *argv[]) -{ - /* write something to stdout */ - printf("hello world!\n"); - return EXIT_SUCCESS; -} diff --git a/sw/applications/example_cpp/main.cpp b/sw/applications/example_cpp/main.cpp new file mode 100644 index 000000000..b314a2b89 --- /dev/null +++ b/sw/applications/example_cpp/main.cpp @@ -0,0 +1,50 @@ +/* + * Copyright 2024 EPFL + * + * 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. + * + * Author: Juan Sapriza + */ + + + +extern "C" { + #include + #include +} + +#include "MyClass.hpp" +#include "core_v_mini_mcu.h" +#include "x-heep.h" + +#if TARGET_SIM && PRINTF_IN_SIM +#define PRINTF(fmt, ...) printf(fmt, ##__VA_ARGS__) +#elif PRINTF_IN_FPGA && !TARGET_SIM +#define PRINTF(fmt, ...) printf(fmt, ##__VA_ARGS__) +#else +#define PRINTF(...) +#endif + +int main() +{ + MyClass myObject(10); // Create an object with initial value 10 + myObject.printValue(); // Print the initial value + + myObject.setValue(20); // Change the value to 20 + myObject.printValue(); // Print the updated value + + int value = myObject.getValue(); // Get the value + PRINTF("Retrieved Value: %d\n\r" ,value); // Print the retrieved value + + return value == 20*5 ? EXIT_SUCCESS : EXIT_FAILURE; +} \ No newline at end of file diff --git a/sw/applications/example_cpp/test_cpp.cpp b/sw/applications/example_cpp/test_cpp.cpp deleted file mode 100644 index 53d08e36f..000000000 --- a/sw/applications/example_cpp/test_cpp.cpp +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Copyright 2022 ESL EPFL - * - * 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. - * - * Author: Jose Miranda - */ - -extern "C" { - #include - #include - #include "test_cpp.h" -} - -#define LOOPS 5 -template -T mySum(T x, T y) -{ - return x+y; -} - -int test_numbers(void) -{ - int i = 0, a = 3, b = 7; - - while (i < LOOPS) - { - printf("Integer sum %d\n", mySum(a, b)); - i = i + 1; - } - - return 0; -} diff --git a/sw/applications/example_cpp/test_cpp.h b/sw/applications/example_cpp/test_cpp.h deleted file mode 100644 index 311214353..000000000 --- a/sw/applications/example_cpp/test_cpp.h +++ /dev/null @@ -1,4 +0,0 @@ -/// @brief -/// @param -/// @return -int test_numbers(void); \ No newline at end of file diff --git a/sw/applications/example_data_processing_from_flash/gen_stimuly.py b/sw/applications/example_data_processing_from_flash/gen_stimuly.py new file mode 100644 index 000000000..5a4a6cd23 --- /dev/null +++ b/sw/applications/example_data_processing_from_flash/gen_stimuly.py @@ -0,0 +1,65 @@ +#!/usr/bin/env python + +## Copyright 2024 EPFL +## Solderpad Hardware License, Version 2.1, see LICENSE.md for details. +## SPDX-License-Identifier: Apache-2.0 WITH SHL-2.1 + +# type " python gen_stimuly.py " in the terminal to generate the matrices.h file + +import sys +import random +import numpy as np + +def write_arr(f, name, arr, ctype, size): + f.write("const " + ctype + " " + name + "[] = {\n") + + for row in arr: + for elem in row[:-1]: + f.write('%d,' % (elem)) + f.write('%d,\n' % (row[-1])) + + f.write('};\n\n') + return + +def write_arr_flash_only(f, name, arr, ctype, size): + f.write( ctype + " __attribute__((section(\".xheep_data_flash_only\"))) __attribute__ ((aligned (16)))" + name + "[] = {\n") + + for row in arr: + for elem in row[:-1]: + f.write('%d,' % (elem)) + f.write('%d,\n' % (row[-1])) + + f.write('};\n\n') + return + + +################################################################################ +f = open('matrices.h', 'w') +f.write('#ifndef MATRICES_H_\n') +f.write('#define MATRICES_H_\n') +f.write('// This file is automatically generated\n') + + +SIZE = 64 +RANGE = 10 + +m_a = [] +m_b = [] +m_exp = [] + +# Generate random 8 bit integers from -RANGE to RANGE for A and B +A = np.random.randint(0, RANGE, size=(SIZE, SIZE), dtype=np.int32) +B = np.random.randint(0, RANGE, size=(SIZE, SIZE), dtype=np.int32) +C = np.zeros((SIZE, SIZE), dtype=np.int32) + +# Test the function with A and B +C = np.matmul(A,B) + +write_arr_flash_only(f, 'A', A, 'int32_t', SIZE) +write_arr(f, 'B', B, 'int32_t', SIZE) +write_arr(f, 'C', C, 'int32_t', SIZE) + +f.write('#define MATRIX_SIZE %d\n' % SIZE) + + +f.write('#endif') diff --git a/sw/applications/example_data_processing_from_flash/main.c b/sw/applications/example_data_processing_from_flash/main.c new file mode 100644 index 000000000..01f7dab7d --- /dev/null +++ b/sw/applications/example_data_processing_from_flash/main.c @@ -0,0 +1,87 @@ +// Copyright 2024 EPFL +// Solderpad Hardware License, Version 2.1, see LICENSE.md for details. +// SPDX-License-Identifier: Apache-2.0 WITH SHL-2.1 +// +// File: sw/applications/example_data_processing_from_flash/main.c +// Author: Francesco Poluzzi +// Date: 29/07/2024 + +/** + * @file main.c + * @brief Example of data processing (matrix multiplication) reading data from flash memory + * + * Simple example that read a matrix from flash memory in many step and performs + * matrix multiplication. This is useful for applications where the + * data size does not fit in the available SRAM memory, so some data needs to be + * stored as "flash_only" and read trough the spi interface. This usually requires + * filling a buffer and tiling the data processing. +*/ + +#include +#include +#include + +#include "x-heep.h" +#include "w25q128jw.h" +#include "main.h" +#include "dma_sdk.h" + +#define TILING_ROWS 2 + + /* By default, printfs are activated for FPGA and disabled for simulation. */ +#define PRINTF_IN_FPGA 1 +#define PRINTF_IN_SIM 0 +#if TARGET_SIM && PRINTF_IN_SIM + #define PRINTF(fmt, ...) printf(fmt, ## __VA_ARGS__) +#elif PRINTF_IN_FPGA && !TARGET_SIM + #define PRINTF(fmt, ...) printf(fmt, ## __VA_ARGS__) +#else + #define PRINTF(...) +#endif + +int32_t buffer_data[MATRIX_SIZE*TILING_ROWS] = {0}; +int32_t output_matrix[MATRIX_SIZE*MATRIX_SIZE] = {0}; + +int main(int argc, char *argv[]) { + + soc_ctrl_t soc_ctrl; + soc_ctrl.base_addr = mmio_region_from_addr((uintptr_t)SOC_CTRL_START_ADDRESS); + + #ifdef TARGET_SIM + PRINTF("This application is meant to run on FPGA only\n"); + return EXIT_SUCCESS; + #endif + + if ( get_spi_flash_mode(&soc_ctrl) == SOC_CTRL_SPI_FLASH_MODE_SPIMEMIO ) { + PRINTF("This application cannot work with the memory mapped SPI FLASH" + "module - do not use the FLASH_EXEC linker script for this application\n"); + return EXIT_SUCCESS; + } + + // Pick the correct spi device based on simulation type + spi_host_t* spi = spi_flash; + + // Init SPI host and SPI<->Flash bridge parameters + if (w25q128jw_init(spi) != FLASH_OK){ + PRINTF("Error initializing SPI flash\n"); + return EXIT_FAILURE; + } + + for (int i = 0; i < MATRIX_SIZE; i+=TILING_ROWS) { + // read first half matrix A from flash and perform matmul + if(fill_buffer(&A[i*MATRIX_SIZE], buffer_data, MATRIX_SIZE*TILING_ROWS)!=FLASH_OK){ + PRINTF("Error reading from flash\n"); + return EXIT_FAILURE; + } + matmul(buffer_data, B, &output_matrix[i*MATRIX_SIZE], TILING_ROWS, MATRIX_SIZE, MATRIX_SIZE); + } + + for(int i = 0; i < MATRIX_SIZE*MATRIX_SIZE; i++){ + if (output_matrix[i] != C[i]){ + PRINTF("Result[%d][%d]:golden model %d : %d\n", (i/MATRIX_SIZE), (i % MATRIX_SIZE), output_matrix[i], C[i]); + return EXIT_FAILURE; + } + } + PRINTF("All tests passed!\n"); + return EXIT_SUCCESS; +} diff --git a/sw/applications/example_data_processing_from_flash/main.h b/sw/applications/example_data_processing_from_flash/main.h new file mode 100644 index 000000000..0e588e78b --- /dev/null +++ b/sw/applications/example_data_processing_from_flash/main.h @@ -0,0 +1,42 @@ +// Copyright 2024 EPFL +// Solderpad Hardware License, Version 2.1, see LICENSE.md for details. +// SPDX-License-Identifier: Apache-2.0 WITH SHL-2.1 +// +// File: sw/applications/example_data_processing_from_flash/main.h +// Author: Francesco Poluzzi +// Date: 29/07/2024 + +#ifndef MAIN_H_ +#define MAIN_H_ + +#include +#include +#include + +#include "x-heep.h" +#include "w25q128jw.h" +#include "matrices.h" + +w25q_error_codes_t fill_buffer(uint32_t *source, uint32_t *buffer, uint32_t len); +void matmul(int32_t *A, int32_t *B, int32_t *res, int rowsA, int colsA, int colsB); + +w25q_error_codes_t fill_buffer(uint32_t *source, uint32_t *buffer, uint32_t len){ + uint32_t *source_flash = heep_get_flash_address_offset(source); + w25q_error_codes_t status = w25q128jw_read_standard(source_flash, buffer, len*4); + return status; +} + +void matmul(int32_t *A, int32_t *B, int32_t *res, int rowsA, int colsA, int colsB) { + for (int i = 0; i < rowsA; i++) { + for (int j = 0; j < colsB; j++) { + int32_t sum = 0; + for (int k = 0; k < colsA; k++) { + sum += A[i*colsA + k] * B[k*colsB + j]; + } + res[i*colsB + j] = sum; + } + } +} + + +#endif // DATA_H_ diff --git a/sw/applications/example_data_processing_from_flash/matrices.h b/sw/applications/example_data_processing_from_flash/matrices.h new file mode 100644 index 000000000..323d1a06b --- /dev/null +++ b/sw/applications/example_data_processing_from_flash/matrices.h @@ -0,0 +1,206 @@ +#ifndef MATRICES_H_ +#define MATRICES_H_ +// This file is automatically generated +int32_t __attribute__((section(".xheep_data_flash_only"))) __attribute__ ((aligned (16)))A[] = { +8,0,9,3,0,0,7,3,4,4,4,0,5,1,9,3,9,1,0,4,1,1,9,4,6,9,8,1,3,3,2,6,5,6,8,7,4,3,3,1,1,9,8,9,6,3,1,6,4,3,6,1,9,0,6,7,5,0,6,5,1,5,6,2, +3,1,8,2,9,8,1,3,0,2,4,6,6,5,7,1,5,5,5,8,6,5,4,9,2,6,7,1,7,2,5,0,9,9,7,6,2,1,6,5,4,9,5,3,1,5,6,9,3,4,4,1,1,7,2,5,8,0,6,4,3,1,8,2, +3,8,8,0,7,5,1,9,1,2,2,2,0,6,4,6,2,4,7,6,0,9,9,8,5,1,9,5,9,8,2,2,7,7,7,5,9,0,3,4,5,3,9,0,2,2,0,7,2,3,1,1,5,5,3,6,4,4,3,7,5,6,0,4, +9,4,4,2,0,4,3,5,7,5,7,5,3,6,5,4,5,7,4,2,4,0,2,8,6,8,4,0,1,3,8,5,4,8,5,1,6,0,7,6,3,1,1,6,2,5,4,9,4,9,5,1,3,7,0,1,0,2,6,3,8,0,1,6, +3,7,8,9,5,1,7,5,5,8,9,7,2,1,2,1,0,3,5,5,3,0,3,0,8,3,3,5,6,3,7,2,2,6,6,1,7,1,7,6,7,1,7,2,0,6,5,1,7,6,6,4,6,3,7,2,1,0,5,9,1,1,1,1, +4,5,6,7,5,3,5,0,2,9,1,7,1,5,6,6,8,8,1,4,5,8,5,2,5,8,8,5,9,5,2,5,6,4,1,2,9,7,1,1,0,6,9,1,2,2,6,3,7,6,6,1,2,0,1,1,8,9,7,0,4,9,0,3, +5,9,0,0,6,3,8,2,4,5,2,9,1,4,4,8,9,3,5,7,1,6,8,8,7,9,3,7,7,4,8,4,9,6,3,1,5,0,6,7,9,6,9,6,4,9,0,6,0,2,1,4,9,4,7,6,0,1,8,2,8,4,2,9, +5,0,0,9,3,7,3,3,8,4,2,0,1,9,5,5,0,0,4,9,8,7,1,3,3,4,3,0,0,3,3,0,2,5,1,8,6,0,2,0,4,2,3,2,9,5,5,2,8,9,4,2,9,7,7,8,8,3,8,9,3,9,1,9, +6,8,4,2,5,3,3,5,1,0,0,8,1,3,9,9,5,5,9,0,5,9,8,2,4,1,4,3,0,6,7,9,9,2,1,8,1,5,1,9,6,3,9,5,0,1,3,6,4,7,5,0,4,6,3,1,4,9,2,1,8,2,8,5, +9,8,5,1,8,0,0,0,1,5,2,6,2,3,2,6,6,7,3,6,6,3,5,9,8,8,1,7,4,4,1,2,0,2,4,3,0,0,5,5,3,8,6,1,4,6,4,8,5,9,7,9,8,1,0,9,8,1,9,6,4,4,0,2, +1,6,0,4,7,8,1,6,8,8,9,4,7,8,1,3,5,4,1,2,8,3,6,4,2,0,1,9,3,9,6,9,2,2,3,3,2,7,1,0,2,8,9,9,9,8,7,4,2,9,4,8,2,0,9,0,3,2,5,6,4,9,3,1, +0,9,9,8,5,4,5,4,1,9,3,3,0,1,6,3,8,6,6,6,3,8,5,7,8,9,4,1,8,2,6,0,5,3,6,7,1,9,8,7,9,0,5,5,3,7,0,1,2,9,4,3,8,3,2,6,1,0,5,5,6,0,4,8, +8,6,2,6,1,4,4,2,0,3,9,8,9,3,1,8,2,9,5,9,9,4,6,0,9,9,9,4,8,7,7,4,4,1,7,9,9,7,3,2,1,6,5,6,5,4,7,9,0,1,0,5,6,9,1,5,1,1,7,5,8,6,7,6, +7,6,5,8,6,2,3,1,2,8,3,9,5,9,3,9,8,8,2,4,7,0,7,4,1,1,9,8,9,7,4,9,3,4,8,3,6,1,1,7,4,9,7,6,8,3,9,1,2,8,2,1,1,6,0,1,2,1,2,2,2,1,0,1, +3,2,7,7,9,2,1,4,0,0,4,4,4,1,4,3,2,1,1,6,4,0,4,6,2,4,9,1,1,3,6,9,0,0,0,0,4,4,6,7,5,9,0,4,1,7,7,0,5,9,4,5,4,1,9,1,3,0,8,4,9,1,8,3, +9,9,3,9,1,9,4,5,3,6,0,5,8,4,3,9,0,2,7,6,1,9,7,4,0,0,9,4,1,6,3,6,7,9,3,9,4,6,6,1,1,7,0,8,0,3,1,0,5,9,3,9,4,7,7,1,1,7,7,1,1,0,2,1, +0,8,7,7,8,0,9,0,4,3,9,1,3,8,3,8,4,2,1,2,2,9,0,2,4,6,1,3,1,8,9,0,1,7,9,0,1,7,2,3,7,7,7,0,0,1,4,4,8,4,8,7,8,9,9,8,9,3,1,6,5,4,5,2, +6,1,9,3,1,9,0,6,0,0,1,5,3,0,0,4,7,7,2,4,8,8,9,3,8,8,9,4,5,3,8,4,1,2,7,5,4,4,9,3,7,5,2,9,4,5,0,3,2,3,7,2,2,8,7,1,4,4,9,2,1,7,7,6, +6,3,0,2,7,7,2,8,8,7,1,6,8,1,5,6,7,7,7,4,9,8,4,8,7,6,0,8,0,9,8,0,8,8,1,4,7,8,7,3,3,9,8,6,5,7,8,9,0,8,6,6,7,4,8,3,6,0,2,8,3,9,3,1, +5,9,0,5,3,9,5,4,6,8,4,9,9,6,8,2,5,8,0,1,3,8,5,9,8,3,1,4,9,0,9,8,1,6,6,9,9,5,0,4,9,4,2,5,3,0,2,9,8,1,6,3,7,3,8,2,9,9,9,6,0,7,4,5, +7,0,7,7,1,7,7,3,2,5,4,5,7,5,7,2,8,7,2,0,2,8,3,9,7,5,1,0,5,8,7,2,1,9,2,3,3,7,7,1,4,3,2,2,8,3,0,7,4,6,1,5,0,3,6,5,0,9,4,2,3,1,5,0, +9,4,2,7,3,2,5,8,5,1,7,2,1,9,1,6,7,3,7,0,2,7,9,4,3,1,9,6,1,3,2,5,9,9,1,9,0,5,4,2,3,3,3,3,1,7,2,2,9,9,8,4,1,1,1,1,6,6,5,4,0,3,1,2, +1,9,3,6,6,3,4,9,3,4,2,2,4,3,5,4,0,6,5,1,1,1,2,7,9,4,0,0,3,7,1,8,9,0,7,7,1,3,7,7,5,7,9,7,0,0,8,1,9,0,3,9,3,9,0,8,4,5,4,5,6,0,9,4, +5,6,4,1,1,8,9,4,3,2,5,9,5,4,5,6,9,4,1,7,1,3,0,0,5,9,5,2,3,2,5,5,7,2,2,9,3,1,2,8,5,0,1,0,8,9,2,9,3,4,6,9,5,4,7,4,7,9,6,1,0,8,2,8, +5,3,4,4,8,1,5,5,2,4,2,1,1,5,6,3,2,4,9,1,1,5,3,1,3,3,8,0,4,8,8,4,7,0,1,1,6,2,1,0,8,2,0,4,4,4,5,4,0,7,7,8,5,5,0,0,0,1,8,8,7,4,7,9, +1,7,2,6,1,6,7,6,2,9,6,2,5,7,7,8,1,5,7,3,3,4,9,8,7,6,5,5,2,8,7,8,7,1,6,0,5,2,2,4,2,3,3,2,0,0,3,4,5,2,2,9,2,9,7,7,9,5,9,0,1,5,3,0, +7,4,1,9,4,5,0,6,7,4,1,2,7,4,0,8,8,3,7,0,5,3,2,2,4,2,7,0,0,5,0,4,6,9,6,2,2,4,5,3,4,4,2,8,7,3,9,4,5,8,5,2,9,4,1,3,2,9,2,2,4,3,9,8, +6,7,8,8,8,8,9,1,1,5,0,5,5,4,2,0,3,0,4,2,1,0,2,6,2,8,1,4,1,6,8,0,0,5,1,3,8,0,2,3,6,6,3,3,4,8,9,2,8,3,1,3,7,2,1,4,9,9,4,4,3,1,2,1, +0,9,9,6,9,3,9,7,5,3,9,3,0,2,9,6,2,2,5,6,8,2,9,6,8,3,6,7,4,4,3,8,6,1,2,6,6,0,9,0,0,9,3,9,9,8,4,8,6,8,0,6,1,3,0,8,4,3,5,3,0,6,2,2, +1,7,1,4,3,5,6,3,7,2,3,1,6,3,5,2,9,3,7,0,6,9,6,5,3,9,6,0,2,3,1,0,6,2,5,8,2,3,0,0,0,4,3,9,4,6,3,5,2,0,3,8,8,2,8,2,5,6,9,7,3,2,9,9, +5,5,4,0,2,4,0,4,5,2,7,6,6,8,5,3,5,5,2,7,3,8,7,5,5,3,6,0,8,6,3,1,7,4,7,1,0,4,3,0,7,9,5,0,6,0,7,3,3,8,2,5,1,4,4,1,6,4,2,5,5,6,8,0, +8,5,9,6,7,9,8,2,9,1,5,1,0,5,9,6,1,9,8,7,6,9,1,0,1,2,8,4,7,4,5,6,9,1,5,8,7,2,4,3,8,3,8,9,0,6,3,1,6,5,7,0,3,1,3,8,5,7,0,8,8,2,7,3, +7,2,8,3,9,8,9,1,6,5,0,4,5,7,3,9,9,0,9,9,2,6,4,3,3,3,3,9,5,5,7,4,2,4,7,2,3,5,5,9,8,6,3,1,5,7,6,8,9,4,1,8,8,5,4,9,0,7,5,4,5,3,6,9, +0,4,8,3,0,9,2,7,1,2,8,5,6,2,8,5,2,0,4,8,2,5,8,2,0,5,7,8,8,0,8,9,3,7,9,5,4,9,4,2,6,9,7,1,5,4,6,3,2,6,3,9,8,4,1,0,1,6,2,0,5,3,7,6, +6,4,6,1,0,7,3,4,6,2,7,1,3,9,0,5,5,4,7,0,0,3,4,8,2,6,6,9,9,0,2,6,5,9,9,7,7,0,6,2,2,7,8,3,8,6,0,4,7,7,0,9,2,2,1,5,1,8,2,3,7,7,1,9, +0,4,2,8,9,7,3,4,1,5,3,3,0,6,4,1,1,8,1,4,9,6,4,0,8,0,2,7,2,2,1,2,8,3,2,3,7,5,9,1,7,8,5,5,6,2,0,0,1,4,3,5,2,8,4,2,7,7,0,5,7,3,4,0, +1,7,4,5,1,3,9,1,8,2,3,7,0,2,9,3,5,3,3,9,2,6,0,5,7,4,9,7,9,6,9,6,4,1,5,3,7,3,0,2,3,9,7,7,8,2,5,8,4,0,8,3,3,0,4,3,0,5,7,3,7,3,9,7, +4,5,5,0,8,1,5,7,8,1,2,5,8,3,0,6,2,9,6,4,5,5,6,6,0,3,8,0,2,0,9,9,1,7,9,4,8,8,7,5,8,1,5,0,0,4,7,1,2,0,2,0,6,7,8,0,1,3,4,0,8,6,9,5, +6,6,9,6,5,1,2,7,1,6,5,5,5,7,2,8,3,5,6,0,8,7,6,9,3,8,8,8,5,3,4,8,5,6,9,2,7,5,7,3,1,7,2,2,9,6,1,9,2,4,4,3,9,2,5,1,6,7,7,2,9,8,6,0, +9,4,4,3,0,3,4,7,1,0,8,4,8,3,1,4,2,5,9,0,0,1,8,5,1,9,3,0,8,9,9,7,4,2,0,3,7,5,8,8,9,1,6,8,3,2,1,6,7,1,6,9,5,9,9,3,5,2,3,4,7,2,1,1, +4,8,0,1,3,6,6,2,5,2,7,2,5,5,7,0,6,4,6,4,8,0,6,4,0,3,6,7,9,7,4,6,8,9,8,7,8,5,0,5,4,5,4,2,8,6,7,8,2,2,0,8,6,4,3,9,7,1,2,3,5,2,5,2, +8,7,0,9,4,4,0,7,5,9,1,8,7,6,9,3,5,6,8,7,8,8,6,9,4,8,6,1,2,8,5,1,1,3,3,3,6,2,8,0,7,3,1,3,2,4,1,1,3,5,6,1,0,9,7,7,7,9,3,4,3,3,6,7, +3,2,8,4,1,0,4,9,4,2,9,5,7,0,9,0,7,8,1,1,8,9,9,3,4,0,7,9,1,4,7,7,9,6,1,8,1,4,1,2,1,6,8,5,8,9,5,1,2,4,3,2,4,6,9,0,6,7,0,8,5,5,2,4, +2,8,7,9,4,3,4,7,0,1,6,9,2,4,9,0,4,2,4,7,6,2,7,7,6,1,5,7,0,2,4,8,4,9,0,1,4,0,1,5,4,9,1,6,8,6,7,9,6,7,6,5,2,0,1,7,1,0,8,0,1,2,5,2, +0,6,8,8,3,3,5,7,1,1,7,5,2,6,2,0,4,2,7,0,7,9,6,9,1,8,4,3,5,7,1,9,3,3,0,5,6,2,0,5,1,6,8,0,1,2,0,6,7,7,9,6,5,6,2,6,9,4,2,7,4,9,6,8, +3,3,9,1,5,2,9,1,4,4,8,9,3,8,1,4,7,9,0,2,4,8,4,5,6,0,7,4,8,9,6,9,9,3,4,9,6,2,2,6,4,0,5,2,5,3,2,3,4,1,3,4,1,9,5,4,9,3,0,3,4,1,2,8, +4,2,0,9,7,5,7,2,9,0,8,6,0,1,5,6,8,3,4,7,5,9,2,0,8,7,6,6,7,1,3,5,3,7,6,2,1,8,2,0,4,1,6,9,3,0,5,7,1,1,6,8,3,4,1,2,7,6,5,6,6,1,1,2, +6,3,5,8,4,9,2,0,9,7,5,4,3,5,7,0,5,7,7,4,4,0,5,3,4,6,0,0,2,2,7,7,0,5,7,2,6,5,3,5,9,9,4,1,6,7,7,3,4,8,6,6,1,1,7,1,3,4,4,4,4,2,1,1, +7,1,2,0,5,9,2,1,3,9,3,7,4,9,5,1,6,5,7,2,0,5,2,4,8,0,5,2,6,6,7,9,8,7,7,8,5,2,4,6,9,3,4,8,8,1,3,4,5,2,2,7,3,9,2,8,8,8,8,8,1,4,1,4, +8,6,3,2,5,1,1,0,9,9,9,8,3,9,5,4,6,2,0,0,4,6,7,1,0,0,0,2,5,2,5,4,9,1,1,5,6,7,6,0,6,4,3,6,5,2,8,7,3,4,0,7,5,4,6,8,8,9,9,6,2,3,2,9, +0,4,9,6,2,5,0,6,0,6,2,0,5,9,7,9,9,7,9,7,3,0,9,7,9,2,5,0,7,5,7,6,7,7,9,9,1,7,1,6,1,3,9,3,6,4,4,9,8,9,6,4,8,5,8,1,5,9,2,5,4,7,2,7, +1,1,3,6,0,1,9,3,5,0,5,0,0,3,0,3,7,7,5,7,5,8,0,4,9,8,8,2,3,7,1,9,8,3,2,1,4,7,8,4,3,7,6,0,2,7,6,2,4,6,2,4,9,2,5,0,0,0,7,5,3,5,7,1, +7,5,9,1,5,8,8,6,5,1,8,4,3,2,1,9,7,8,1,2,3,5,6,0,6,1,0,3,7,9,1,3,0,2,0,5,4,7,3,9,3,4,6,6,6,8,2,0,4,8,3,1,1,2,0,6,9,9,9,8,3,4,7,7, +4,9,4,5,0,9,6,5,5,2,5,5,5,5,1,9,4,9,8,1,5,2,9,8,1,9,9,0,0,7,4,8,7,1,8,8,7,3,3,2,2,4,5,0,1,2,3,6,9,0,0,2,1,5,5,7,7,0,7,6,5,5,7,6, +9,1,9,3,8,2,2,9,7,9,4,9,5,2,6,5,5,3,7,2,8,8,7,7,9,2,1,4,0,2,2,8,8,5,9,9,0,5,2,1,5,5,3,3,7,0,7,3,5,9,8,2,6,0,3,7,8,8,4,6,6,3,3,3, +0,8,6,6,5,9,4,8,7,8,4,8,3,8,7,9,4,4,5,5,6,0,9,4,9,2,8,7,0,1,2,5,3,6,1,9,2,7,4,6,4,7,9,5,6,0,4,1,1,2,6,5,2,1,1,9,6,2,0,8,0,4,7,5, +8,5,6,2,3,3,9,4,4,1,9,2,7,0,6,8,1,5,6,7,3,9,9,4,5,6,9,6,7,9,0,2,8,5,1,3,9,2,8,3,5,3,9,2,5,6,3,7,0,2,4,8,2,8,4,2,2,1,8,9,2,1,7,4, +3,8,3,6,2,1,1,6,1,1,7,9,3,4,5,4,8,7,2,3,9,8,8,1,8,0,8,5,8,1,1,2,2,3,9,5,6,1,0,3,1,5,2,1,6,4,4,7,0,4,9,8,7,6,6,9,6,3,0,6,7,6,3,5, +2,1,9,4,2,4,3,9,1,9,0,9,9,2,2,1,5,2,2,9,2,4,6,2,3,3,4,0,0,8,8,4,6,2,2,5,6,7,5,6,1,3,9,7,0,1,2,2,5,9,2,1,3,1,8,8,1,3,6,9,6,0,9,2, +5,4,8,8,6,8,9,1,7,6,2,0,7,8,6,1,0,6,9,2,7,6,8,1,3,6,2,1,5,1,0,4,9,1,5,2,4,4,4,5,6,4,4,6,8,5,6,7,8,5,3,1,3,3,6,9,6,0,0,8,1,8,8,6, +9,0,5,0,9,6,4,8,3,9,9,8,2,8,0,0,9,5,2,1,2,0,1,5,2,1,9,6,3,8,7,0,1,0,9,1,0,1,6,0,8,7,4,5,6,4,5,6,5,9,5,8,9,4,2,0,8,2,6,5,4,5,4,5, +8,7,5,6,8,4,8,2,4,6,5,0,2,1,1,0,8,3,0,0,6,7,6,5,5,7,0,9,4,2,1,0,8,1,5,0,4,5,5,1,4,6,6,5,5,2,2,7,9,8,6,6,4,3,9,5,7,6,5,8,5,7,7,3, +2,4,8,3,7,6,5,0,3,8,0,4,8,7,6,3,6,0,3,2,8,7,4,6,2,1,6,3,5,7,5,1,5,3,8,9,9,3,0,7,2,6,7,5,5,7,3,3,9,7,6,3,6,4,3,2,5,3,5,0,1,2,7,3, +6,0,1,2,3,9,7,8,4,6,3,8,3,0,5,2,9,1,9,6,9,6,2,1,0,6,0,1,2,5,6,4,9,8,9,4,1,2,6,6,4,7,1,2,7,3,7,9,9,2,6,0,4,4,2,5,0,0,4,6,2,4,8,0, +}; + +const int32_t B[] = { +1,1,9,1,9,6,7,6,2,8,2,8,5,6,3,0,2,4,0,1,7,5,9,1,7,4,7,3,2,2,8,0,3,1,3,7,1,9,2,5,6,5,2,3,7,1,6,3,3,2,1,4,2,4,8,9,4,0,8,3,6,4,0,9, +6,1,6,9,1,4,3,4,2,3,4,8,0,0,1,2,7,7,9,6,1,5,8,7,6,7,1,8,0,2,7,1,4,5,7,4,2,7,6,0,0,1,7,9,0,4,3,8,8,9,4,3,4,7,6,3,4,0,8,9,5,1,1,7, +3,3,4,7,0,4,1,5,2,1,2,5,6,6,1,4,4,2,8,7,5,4,0,5,8,1,7,2,3,4,7,7,6,3,1,5,6,2,8,5,7,8,7,7,9,3,5,9,9,1,1,1,6,8,5,5,9,7,9,5,8,9,0,9, +2,9,2,2,4,4,6,7,4,7,4,2,4,2,7,2,2,2,9,6,7,2,1,5,0,9,5,2,8,6,8,6,2,7,8,2,0,1,0,2,9,0,3,9,9,6,9,2,1,7,1,1,4,6,7,9,8,8,5,3,1,1,8,5, +2,2,8,2,0,6,7,3,0,1,2,0,5,2,6,9,3,5,2,0,9,7,6,7,2,5,2,3,0,6,0,4,5,1,6,4,7,8,0,1,0,7,1,6,6,5,4,7,2,3,0,3,8,0,7,4,5,0,3,8,6,3,5,0, +9,1,3,8,5,9,4,9,0,2,9,7,1,1,0,8,4,7,8,5,3,2,3,0,9,8,3,7,5,8,8,5,9,5,6,5,2,7,3,3,7,2,2,7,0,0,4,9,8,8,0,6,0,9,3,9,6,5,7,0,8,2,2,3, +4,3,2,5,2,0,4,6,7,1,5,1,6,4,1,0,4,6,1,1,3,8,8,9,1,1,9,9,7,5,0,8,5,5,9,0,4,1,7,5,2,2,7,8,5,1,1,0,0,2,0,1,7,9,2,0,3,7,7,8,6,9,4,0, +6,0,4,1,4,2,0,2,5,4,0,6,9,9,1,1,8,7,5,0,3,3,8,6,1,1,3,1,9,2,4,6,2,5,6,6,8,4,6,1,3,0,7,1,3,3,0,3,3,2,7,4,6,0,6,7,7,7,0,4,9,4,9,4, +1,9,5,5,4,5,7,7,2,4,9,4,6,0,3,5,2,6,8,0,3,0,5,5,1,6,9,2,6,9,7,3,8,0,6,1,0,0,4,4,2,3,3,7,8,1,7,4,7,0,9,2,1,4,2,7,0,3,0,7,7,7,4,0, +5,0,9,9,7,4,0,5,7,3,7,5,2,4,4,9,5,4,5,7,5,1,2,1,3,8,8,5,1,6,6,3,9,8,1,1,9,4,7,9,8,8,4,5,0,9,5,3,0,3,5,7,4,2,5,5,2,9,9,7,7,9,2,3, +4,9,9,2,5,3,1,7,6,2,5,2,5,9,3,1,1,9,0,3,1,1,2,5,0,7,6,0,4,0,0,5,5,9,8,4,1,0,9,8,9,7,1,5,8,5,7,1,1,3,7,1,3,4,7,8,7,8,8,5,2,7,5,5, +0,9,7,2,6,8,5,7,0,3,6,9,1,2,5,7,7,2,9,6,6,9,7,1,0,6,6,4,3,3,7,1,8,0,7,2,8,9,2,1,0,1,7,9,5,7,6,8,5,4,0,1,7,9,8,9,0,5,4,9,8,5,5,9, +9,9,3,4,5,3,7,5,7,3,3,2,1,5,7,5,0,8,4,5,8,9,4,3,9,8,2,6,3,9,3,1,3,6,5,3,9,0,8,4,5,5,5,9,4,0,9,6,0,4,4,6,2,5,8,2,0,9,0,0,0,0,7,9, +4,9,3,6,2,7,0,5,9,6,9,4,8,6,2,1,9,0,2,2,6,9,5,0,5,8,7,6,3,8,6,9,8,9,8,2,8,2,9,4,2,7,2,6,6,9,0,1,9,8,9,0,0,5,3,5,8,7,9,1,4,9,8,1, +3,5,1,8,4,8,7,4,0,4,9,8,0,8,6,7,0,7,9,1,6,6,4,8,2,0,8,9,7,8,9,0,1,4,2,0,9,4,9,5,3,0,4,0,6,8,2,0,3,9,7,4,8,3,4,9,4,6,9,6,2,9,9,6, +5,2,1,9,2,2,6,4,4,3,4,4,6,0,2,9,0,8,4,6,3,4,1,6,6,5,4,4,5,0,0,3,4,4,1,1,4,9,8,0,1,6,2,6,1,3,7,3,4,1,5,9,0,1,9,2,7,3,6,7,1,4,7,7, +7,7,9,0,8,0,2,4,2,3,7,5,7,4,2,0,8,8,3,7,1,4,7,2,7,9,4,3,0,8,1,6,9,8,6,0,3,4,7,8,8,6,7,1,7,9,9,9,6,0,3,9,4,8,8,3,8,9,5,0,4,4,4,7, +7,8,3,4,5,8,1,6,7,5,2,0,9,5,9,7,1,8,6,4,4,9,3,8,6,2,8,3,0,7,6,5,9,6,5,8,0,9,8,7,4,4,1,1,8,7,8,7,2,2,8,8,3,1,9,2,2,5,8,6,3,3,7,8, +1,3,6,6,2,8,8,1,2,5,2,4,8,3,8,5,0,8,4,9,9,3,4,4,3,6,6,7,3,3,8,5,0,9,6,9,0,2,5,8,9,3,1,0,8,8,4,4,3,5,9,8,6,1,2,0,9,4,2,9,4,0,0,1, +8,1,7,6,0,2,2,7,6,0,1,7,6,9,0,9,7,8,4,9,6,2,4,3,3,8,8,8,5,6,3,2,2,7,1,7,8,6,6,1,4,0,8,3,5,0,0,3,5,2,3,4,2,4,9,2,1,0,4,3,3,2,0,5, +3,8,0,3,8,6,8,3,8,8,7,2,5,4,6,5,5,8,2,8,8,1,4,0,8,9,3,2,0,9,7,2,9,4,6,2,5,0,2,5,9,1,7,7,9,9,4,5,3,7,6,3,3,9,4,8,5,4,6,4,0,8,8,2, +0,0,8,9,8,3,3,9,7,2,6,9,2,9,4,6,8,3,8,7,7,6,4,7,8,5,8,6,1,3,0,2,2,9,4,8,0,1,3,6,4,2,9,2,1,4,7,0,3,8,5,6,6,1,6,2,0,0,2,4,1,4,4,4, +6,8,7,2,1,9,3,3,8,5,8,2,7,5,9,8,5,6,5,9,8,6,3,2,7,1,1,2,7,6,7,1,1,6,8,5,0,8,4,4,3,6,8,1,7,6,0,9,2,6,0,2,2,8,6,2,6,9,7,3,3,3,5,4, +6,0,5,0,9,4,5,1,9,8,1,4,5,7,2,2,9,0,3,7,3,2,9,2,6,5,3,7,2,6,1,3,1,5,7,1,4,6,0,6,4,9,1,5,0,1,8,0,5,9,8,5,2,1,0,7,3,2,5,8,1,7,5,3, +0,5,8,3,4,8,3,3,6,1,2,9,6,8,3,6,6,9,8,1,5,2,0,9,6,7,1,7,6,3,0,9,0,3,5,6,2,4,2,8,2,7,5,7,6,5,2,2,0,2,8,9,3,6,7,6,1,5,2,9,0,1,7,5, +9,0,0,5,5,8,3,0,9,7,6,8,5,7,2,4,9,8,2,2,8,3,1,6,5,1,8,6,2,0,8,5,5,8,0,3,1,8,0,0,6,8,4,1,4,1,5,0,0,9,0,0,2,2,3,3,1,8,6,6,3,7,1,3, +6,6,0,9,4,8,7,5,0,3,1,4,0,1,1,2,8,7,4,4,8,3,1,1,6,3,7,9,2,0,8,9,6,5,8,7,0,8,8,6,3,4,9,1,5,0,9,8,8,2,0,4,2,5,7,0,7,5,1,6,5,8,6,9, +9,6,7,5,2,9,9,1,7,1,0,4,4,9,5,8,9,3,2,8,4,0,7,3,5,8,2,6,2,1,7,0,5,0,1,9,3,2,7,7,4,1,6,4,9,6,3,5,6,8,2,5,3,9,1,5,3,2,7,1,9,1,2,8, +1,0,7,9,9,1,7,7,0,5,6,8,6,8,7,4,9,6,3,7,9,3,2,5,2,4,6,7,8,9,7,5,6,9,7,4,3,7,0,1,7,1,1,7,8,4,9,8,7,1,0,2,6,8,4,9,1,9,1,3,9,7,4,5, +1,9,7,9,0,0,4,3,2,4,4,8,0,6,1,8,8,2,0,9,0,7,0,5,4,8,9,8,7,7,8,5,9,2,6,2,2,9,6,7,1,1,1,7,7,4,3,0,8,5,5,0,1,1,0,9,3,8,3,0,9,3,8,7, +4,7,5,5,2,4,5,1,3,4,6,7,7,4,3,0,7,7,8,4,0,3,4,9,0,4,6,6,4,3,9,5,5,5,2,6,2,1,8,0,8,5,0,8,2,5,7,2,6,4,3,4,7,4,3,5,0,0,1,3,0,6,7,4, +1,7,8,7,4,6,1,9,2,1,3,1,4,5,8,6,8,0,2,8,8,7,6,5,5,4,3,7,0,0,3,6,7,8,2,4,0,0,7,0,8,0,8,7,1,2,1,5,4,0,6,9,4,6,0,4,0,6,6,4,6,2,3,8, +3,2,5,5,6,6,0,8,2,5,8,5,8,9,8,6,6,2,5,0,4,4,5,9,6,3,9,5,7,6,1,6,2,6,5,8,2,2,4,1,0,6,2,1,9,7,9,5,9,7,8,3,1,2,1,0,7,5,4,4,6,4,5,8, +1,4,1,3,8,8,1,0,5,0,2,4,8,3,4,0,7,0,0,9,6,6,0,3,8,3,2,8,2,3,5,5,4,9,5,3,7,9,8,6,6,3,4,6,7,7,3,1,1,5,4,2,0,9,5,1,4,5,4,0,8,2,4,8, +5,4,4,9,9,1,0,6,9,8,4,6,0,3,8,9,4,9,5,6,2,7,0,5,7,8,0,3,7,0,1,3,1,4,7,4,7,6,3,4,7,2,4,8,9,2,4,1,7,1,9,9,5,5,4,0,5,4,5,8,6,8,1,2, +9,0,0,2,7,4,2,1,3,1,7,3,6,2,5,9,3,8,5,5,8,8,2,9,3,5,2,6,9,3,0,4,3,7,8,3,7,2,6,5,8,2,9,7,1,1,9,0,8,5,8,0,1,3,1,5,9,8,1,9,1,9,2,9, +6,7,5,6,2,5,7,6,0,9,5,7,4,3,0,6,1,8,7,9,5,7,2,8,1,5,4,3,4,7,4,7,5,0,0,4,2,8,0,2,9,9,5,1,7,3,0,7,3,3,3,0,9,7,6,8,5,6,0,2,1,5,3,5, +7,7,8,2,6,6,6,8,2,9,8,3,7,5,7,9,2,1,4,8,0,4,7,6,1,7,1,0,8,3,5,7,9,1,0,9,5,9,6,6,7,1,8,1,5,6,9,6,0,1,2,9,9,7,8,2,5,8,6,1,5,2,9,3, +1,7,5,1,6,1,4,2,8,5,1,7,0,3,0,4,1,3,2,8,0,3,6,1,8,9,4,1,7,1,2,0,3,8,7,8,1,6,9,6,5,7,6,3,3,3,9,8,2,1,0,6,6,2,4,2,5,0,8,2,6,4,6,3, +2,6,1,2,4,1,3,3,9,5,8,6,2,1,4,7,7,3,1,6,4,3,2,5,8,9,9,6,5,0,0,3,1,1,4,2,7,6,4,8,9,8,1,5,6,7,0,3,3,6,4,8,8,8,8,9,3,8,3,5,5,4,4,5, +2,3,0,1,6,0,0,1,6,3,0,9,6,8,9,5,1,4,0,7,0,5,5,2,2,8,5,1,8,2,9,9,1,9,4,5,9,6,6,5,9,2,7,6,4,4,5,2,9,5,4,6,8,4,0,7,6,1,5,3,0,9,5,1, +2,5,4,4,4,8,2,8,8,6,6,9,6,4,1,7,7,8,1,6,8,7,9,4,2,4,1,4,2,0,5,8,8,9,6,9,7,6,1,0,9,7,1,5,2,0,7,7,3,4,5,1,0,7,3,1,5,4,4,9,6,1,0,8, +2,6,8,2,6,9,8,4,3,6,5,9,8,0,0,6,7,1,8,4,0,1,8,1,2,8,2,2,2,8,1,6,1,5,9,8,9,5,6,3,2,1,4,2,9,3,2,0,9,6,8,8,3,3,7,6,7,0,0,4,8,4,0,6, +9,8,0,2,7,2,8,4,0,6,1,8,3,9,8,4,0,0,6,3,0,5,9,8,0,6,8,5,1,9,7,5,4,8,5,5,1,4,3,2,4,5,5,0,5,5,3,8,0,2,5,2,1,3,1,6,1,6,8,2,8,4,1,3, +1,0,1,5,5,2,9,9,4,1,0,0,3,6,4,0,8,5,0,6,2,9,4,5,6,2,8,8,5,5,6,0,7,4,6,3,6,9,6,1,4,3,5,4,0,3,2,6,6,5,8,1,5,7,3,0,4,5,4,8,0,0,5,4, +5,7,1,4,4,4,2,7,3,2,7,7,8,6,5,1,1,0,3,1,4,6,7,3,7,1,6,3,7,5,6,6,8,4,9,1,2,7,3,0,2,8,8,3,9,3,7,2,8,5,0,9,4,5,1,3,4,6,6,8,6,9,7,7, +1,4,8,6,7,9,7,5,6,7,4,2,0,5,4,8,2,2,4,3,4,2,3,7,2,0,4,9,6,9,1,2,6,3,2,0,4,9,9,4,9,7,7,8,4,3,4,7,8,4,9,1,9,3,1,3,4,2,5,6,2,2,8,6, +3,5,9,4,5,9,7,1,6,7,7,3,1,9,3,2,8,5,7,7,8,1,7,7,0,0,2,3,7,3,6,8,6,3,7,6,6,3,6,0,1,9,9,3,0,9,2,2,3,0,2,6,3,3,2,8,5,6,2,6,9,6,7,0, +6,5,6,4,9,5,8,1,4,2,1,7,0,7,2,9,7,7,3,8,3,2,3,7,0,0,2,7,6,7,0,6,6,8,4,9,8,5,6,3,4,4,7,0,6,6,8,2,0,4,8,2,4,2,6,0,9,7,0,9,5,2,4,1, +4,5,1,2,4,3,7,7,9,5,0,7,3,2,0,8,4,1,6,5,9,8,8,1,2,3,4,0,2,4,0,3,2,6,1,0,9,2,2,3,4,0,7,3,3,0,4,7,7,1,2,9,7,8,5,5,6,3,4,1,8,9,3,5, +1,2,9,2,1,6,5,1,5,7,5,0,2,6,5,7,6,6,8,9,1,8,3,3,6,8,2,3,0,7,8,0,9,9,8,9,7,6,1,8,8,3,7,5,5,7,6,5,5,9,0,8,6,3,1,9,5,6,2,3,4,9,2,7, +5,1,9,8,5,2,6,7,8,7,1,2,1,1,6,8,0,0,1,0,4,3,1,8,3,4,1,8,4,7,3,3,0,3,6,7,7,5,6,3,0,7,6,7,9,3,9,4,1,9,1,2,1,8,8,9,6,6,1,0,8,9,8,6, +9,4,9,5,4,3,6,1,2,9,8,6,9,1,4,4,6,0,3,6,4,6,5,0,4,2,0,1,6,8,2,6,4,6,5,5,6,8,6,8,8,2,9,9,2,9,7,4,9,3,1,3,2,1,2,2,5,8,2,2,4,2,7,0, +5,1,3,4,8,7,2,7,3,2,1,4,0,0,4,8,5,6,6,7,9,0,7,6,0,8,1,2,3,0,5,9,1,3,6,1,8,2,7,1,7,5,9,5,1,9,5,9,1,8,5,0,8,2,9,0,8,1,6,9,1,5,1,8, +4,6,2,3,5,2,7,1,6,9,9,5,2,6,1,3,6,3,3,3,7,6,5,1,4,6,2,3,0,6,0,7,4,6,0,1,9,1,5,3,3,7,3,9,0,2,8,8,8,1,1,9,0,9,7,5,3,8,8,6,7,4,0,9, +7,8,8,4,4,9,3,1,1,5,2,7,5,6,2,2,9,3,0,8,1,7,1,8,5,5,3,0,9,9,6,4,6,0,4,2,3,6,2,8,4,8,4,9,9,6,1,0,0,0,6,1,9,7,7,2,8,4,7,8,4,4,6,2, +2,4,9,4,4,3,2,9,1,4,5,2,2,4,1,9,0,0,8,0,1,5,8,5,9,0,8,3,7,8,3,3,2,1,9,5,6,0,7,6,2,1,8,4,4,4,3,2,9,3,2,8,4,1,6,3,6,2,1,4,3,0,0,5, +9,4,8,3,1,9,6,4,0,9,7,7,6,3,9,4,0,3,2,2,1,9,0,6,5,2,0,7,8,3,3,4,6,0,6,0,0,5,7,8,3,4,6,5,8,2,7,8,9,7,9,0,8,1,9,8,5,7,4,4,9,8,5,7, +3,3,3,1,4,2,9,1,7,3,7,7,9,2,7,3,1,8,6,3,6,2,0,2,0,7,6,9,6,1,9,6,2,3,7,0,1,6,7,0,0,5,8,1,0,2,2,5,8,3,3,4,1,6,8,9,3,8,0,9,2,1,6,6, +0,7,1,3,6,0,7,0,3,9,4,1,5,2,6,3,7,6,1,3,8,4,6,3,3,4,6,6,0,6,5,6,3,6,9,4,4,0,0,8,0,5,3,7,4,0,9,4,2,2,9,1,8,7,5,3,8,6,1,9,9,8,9,0, +4,8,3,0,7,6,1,6,3,7,6,5,0,1,3,4,7,3,7,0,7,8,2,8,4,0,3,3,3,2,6,3,9,1,2,6,5,6,1,7,2,8,8,5,8,0,1,4,3,8,4,4,5,6,3,4,2,9,1,7,4,0,4,5, +8,4,8,6,5,5,1,2,2,9,9,2,4,7,0,1,1,8,6,2,9,1,7,0,8,9,3,2,6,9,1,4,6,6,2,8,1,6,7,3,0,7,7,4,3,9,3,5,6,5,6,5,8,9,5,3,1,0,9,5,4,6,7,6, +7,8,3,7,7,2,5,3,4,1,7,3,8,0,0,5,2,3,6,4,5,2,0,3,3,3,1,2,6,1,1,1,4,7,6,0,4,9,1,2,8,5,0,3,3,2,5,0,1,4,5,9,0,3,6,8,2,2,4,1,2,3,7,3, +9,5,2,1,0,7,2,1,3,5,2,3,0,5,6,2,2,9,0,4,1,6,5,3,0,4,8,2,2,1,2,1,2,2,1,7,7,2,0,3,4,7,3,6,8,0,9,0,0,8,3,2,0,4,6,7,9,6,0,1,7,1,7,6, +}; + +const int32_t C[] = { +1223,1282,1286,1163,1319,1270,1192,1100,1142,1261,1291,1367,1260,1311,985,1232,1313,1316,1148,1320,1277,1275,1145,1199,1157,1203,1225,1211,1177,1281,1164,1239,1209,1454,1377,1145,1189,1335,1306,1108,1302,1233,1421,1194,1339,1065,1322,1063,1169,979,1109,1155,976,1341,1258,1173,1250,1486,1204,1322,1273,1303,1130,1377, +1184,1267,1312,1237,1443,1455,1188,1232,1260,1222,1312,1451,1244,1331,1078,1464,1396,1375,1279,1437,1466,1272,1208,1262,1248,1352,1314,1335,1209,1309,1263,1277,1264,1509,1546,1222,1369,1428,1334,1090,1392,1323,1378,1303,1411,1190,1443,1195,1305,1244,1177,1218,1132,1317,1370,1300,1356,1338,1210,1421,1310,1349,1236,1435, +1162,1146,1395,1317,1216,1378,1119,1124,1091,1251,1202,1479,1236,1287,1005,1364,1472,1348,1223,1445,1362,1273,1148,1252,1226,1323,1226,1294,1205,1235,1192,1299,1135,1351,1441,1303,1192,1422,1313,1142,1138,1213,1390,1262,1400,1148,1242,1149,1390,1197,1230,1127,1164,1269,1347,1231,1377,1294,1109,1386,1400,1285,1202,1375, +1085,1269,1161,1032,1288,1306,1093,1075,1208,1217,1133,1354,1087,1180,994,1148,1230,1282,1151,1255,1213,1176,1122,1190,1027,1230,1284,1184,1022,1073,1237,1162,1222,1300,1273,1038,1101,1271,1263,1053,1266,1276,1277,1185,1288,1092,1256,1066,1070,1100,1139,1110,1041,1201,1214,1307,1153,1334,1146,1282,1220,1292,1202,1281, +953,1262,1262,1113,1165,1134,1165,1034,1155,1150,1092,1340,1127,1129,991,1264,1190,1241,1146,1305,1178,1086,1038,1146,925,1348,1187,1146,1084,1170,1147,1201,1109,1331,1327,1101,1191,1195,1240,1088,1269,1097,1275,1367,1394,1097,1289,1092,1121,1008,1014,1078,1185,1328,1277,1274,1192,1304,1097,1331,1288,1287,1139,1227, +1201,1282,1518,1325,1281,1510,1265,1336,1105,1327,1407,1443,1225,1295,1066,1483,1340,1349,1354,1403,1371,1315,1151,1265,1189,1335,1343,1363,1137,1399,1248,1254,1465,1378,1333,1240,1131,1468,1371,1139,1253,1175,1496,1213,1473,1246,1402,1272,1330,1226,1126,1186,1212,1376,1462,1329,1229,1447,1227,1339,1347,1301,1240,1524, +1377,1419,1524,1328,1441,1503,1332,1263,1372,1402,1486,1771,1459,1449,1219,1424,1629,1493,1295,1539,1427,1451,1453,1399,1276,1583,1497,1480,1279,1354,1389,1403,1376,1571,1552,1346,1348,1654,1440,1173,1324,1504,1554,1499,1542,1332,1499,1277,1431,1402,1203,1382,1206,1560,1479,1448,1313,1516,1372,1582,1508,1404,1374,1553, +1180,1190,1170,1150,1253,1280,1261,1120,1132,1291,1259,1295,1148,1178,936,1279,1199,1307,1136,1266,1352,1219,1137,1104,1051,1323,1299,1210,1202,1416,1149,1190,1192,1345,1331,1059,1264,1186,1238,1078,1212,1136,1433,1338,1238,1081,1314,980,1253,1209,1241,996,1074,1264,1301,1235,1360,1275,1076,1355,1130,1254,1272,1195, +1159,1328,1348,1187,1256,1466,1220,1187,1064,1249,1357,1457,1216,1173,1179,1494,1289,1263,1350,1375,1310,1373,1248,1356,1142,1296,1226,1265,1158,1135,1179,1185,1210,1338,1376,1222,1213,1348,1378,1129,1272,1097,1444,1236,1363,1248,1324,1170,1316,1302,1266,1319,1195,1216,1356,1352,1313,1320,1173,1359,1298,1234,1253,1485, +1134,1192,1510,1136,1287,1364,1271,1096,1336,1308,1148,1414,1194,1245,1032,1431,1381,1276,1130,1384,1336,1264,1240,1175,1239,1336,1183,1249,1064,1289,1208,1102,1247,1302,1428,1224,1265,1470,1235,1161,1156,1305,1444,1343,1369,1177,1340,1132,1237,1177,1092,1239,1110,1324,1379,1318,1259,1278,1188,1466,1312,1262,1205,1355, +1266,1520,1489,1292,1362,1379,1379,1383,1309,1375,1416,1341,1262,1346,1139,1468,1367,1283,1245,1376,1341,1328,1405,1194,1162,1568,1256,1316,1109,1523,1281,1251,1550,1493,1500,1225,1303,1342,1463,1098,1279,1224,1440,1471,1384,1199,1433,1338,1395,1243,1313,1311,1093,1536,1311,1466,1223,1480,1327,1313,1491,1332,1396,1461, +1324,1267,1330,1251,1403,1288,1188,1214,1312,1302,1313,1591,1341,1331,1203,1456,1421,1408,1342,1529,1301,1337,1204,1372,1206,1499,1416,1278,1280,1291,1305,1313,1258,1588,1452,1262,1314,1473,1316,1285,1467,1271,1521,1386,1488,1250,1570,1182,1258,1285,1140,1369,1305,1405,1438,1359,1400,1532,1314,1458,1338,1451,1351,1364, +1508,1591,1551,1501,1560,1630,1454,1467,1378,1516,1488,1608,1383,1444,1297,1613,1483,1759,1421,1635,1655,1449,1270,1550,1260,1637,1451,1499,1405,1380,1486,1452,1522,1566,1612,1445,1283,1683,1534,1209,1565,1462,1665,1582,1626,1309,1625,1376,1302,1344,1358,1280,1260,1595,1679,1500,1399,1654,1363,1613,1365,1399,1545,1702, +1128,1402,1315,1329,1340,1389,1269,1399,1237,1236,1196,1382,1177,1206,1166,1486,1421,1321,1176,1538,1402,1373,1244,1194,1180,1494,1352,1365,1053,1302,1324,1224,1425,1421,1437,1106,1273,1479,1411,1069,1432,1156,1353,1414,1474,1224,1352,1368,1351,1155,1216,1164,1160,1468,1376,1301,1283,1407,1340,1365,1333,1314,1222,1562, +990,1242,1037,945,1056,1066,1122,1041,1072,1106,1025,1176,971,1006,867,1205,1076,1045,1000,1134,1225,1119,1020,1013,914,1099,1048,1067,931,982,1033,1069,1108,1191,1107,972,1094,1217,1063,856,1183,1130,1222,1157,1123,796,1215,1122,1048,969,815,1089,996,1210,1176,1192,1055,1217,1003,1157,1077,1052,1133,1251, +1284,1200,1269,1326,1294,1339,1278,1318,1181,1223,1209,1465,1143,1136,1127,1486,1193,1233,1248,1465,1402,1356,1180,1231,1176,1432,1200,1408,1160,1184,1267,1200,1191,1474,1389,1208,1165,1362,1425,1053,1281,1076,1472,1331,1252,1040,1517,1310,1238,1248,1084,1152,967,1341,1425,1250,1309,1399,1241,1257,1382,1239,1175,1577, +1157,1371,1479,1324,1290,1274,1173,1187,1301,1333,1326,1392,1165,1215,985,1438,1376,1284,1211,1396,1195,1319,1162,1383,1117,1438,1185,1208,1183,1294,1112,1369,1307,1449,1440,1203,1413,1294,1443,1230,1321,1264,1402,1575,1427,1266,1471,1020,1234,1245,1201,1196,1241,1327,1444,1219,1381,1343,1251,1407,1293,1369,1284,1303, +1316,1298,1260,1190,1373,1338,1219,1183,1240,1283,1256,1493,1236,1335,1122,1382,1292,1488,1302,1486,1338,1254,1162,1173,1297,1483,1244,1220,1166,1161,1344,1272,1290,1480,1411,1317,1103,1441,1318,1080,1426,1259,1527,1254,1328,1124,1486,1371,1274,1181,1027,1319,1105,1419,1442,1367,1249,1373,1263,1297,1258,1317,1227,1527, +1421,1598,1719,1432,1649,1684,1592,1420,1513,1671,1607,1690,1512,1581,1246,1737,1578,1567,1514,1660,1608,1495,1644,1490,1460,1731,1507,1492,1354,1732,1465,1436,1612,1657,1650,1527,1527,1676,1606,1398,1527,1522,1674,1585,1600,1498,1731,1449,1478,1458,1463,1573,1379,1600,1567,1585,1439,1563,1439,1575,1620,1505,1612,1670, +1491,1497,1680,1447,1639,1595,1469,1385,1371,1551,1666,1664,1403,1545,1403,1621,1512,1657,1560,1631,1536,1573,1433,1514,1314,1649,1409,1603,1415,1605,1462,1469,1523,1675,1690,1356,1478,1533,1601,1331,1565,1380,1688,1687,1424,1429,1693,1375,1481,1428,1447,1426,1364,1620,1623,1724,1355,1714,1367,1620,1474,1547,1523,1614, +1090,1249,1258,1115,1331,1239,1173,1167,1179,1179,1203,1371,1159,1269,1009,1150,1245,1181,1104,1338,1191,1301,1036,1221,1125,1263,1207,1256,1169,1232,1197,1194,1278,1346,1321,997,1116,1309,1316,1143,1253,1186,1246,1267,1269,1089,1401,1067,1059,1099,1098,1109,1089,1309,1330,1325,1136,1422,1227,1181,1224,1284,1296,1325, +1083,1217,1287,1048,1226,1301,1124,1133,1146,1134,1156,1229,1271,1223,1073,1232,1203,1169,1079,1252,1233,1235,1179,1147,1137,1240,1198,1207,1135,1142,1101,1184,1170,1434,1480,1202,1022,1193,1354,1134,1161,1055,1393,1101,1390,1119,1408,1067,1225,1142,1141,1134,982,1215,1252,1163,1391,1336,1110,1216,1293,1274,1213,1374, +1176,1248,1367,1209,1350,1327,1126,1169,1186,1257,1147,1409,1134,1158,1140,1539,1249,1262,1208,1267,1171,1228,1130,1483,1033,1346,1135,1305,1283,1225,1075,1250,1112,1364,1485,1199,1257,1356,1297,1102,1262,1223,1307,1367,1397,1109,1360,1064,1102,1259,1421,1132,1150,1122,1323,1293,1342,1352,1120,1478,1299,1183,1290,1328, +1358,1196,1378,1279,1255,1332,1182,1268,1170,1229,1412,1412,1192,1322,1065,1309,1291,1437,1206,1262,1288,1387,1202,1326,1237,1347,1386,1400,1304,1272,1176,1260,1337,1353,1438,1130,1291,1371,1493,1071,1218,1283,1566,1423,1335,1169,1460,1183,1396,1247,1090,1229,1152,1443,1453,1404,1296,1502,1227,1393,1356,1435,1306,1545, +970,1102,1135,1132,1034,1115,1110,1029,968,1150,1057,1154,1067,1083,1050,1148,1048,1194,1057,1091,1223,1172,1002,1175,842,1109,1236,1167,1003,1080,1207,1133,1084,1245,1215,1080,1015,1208,1115,949,1090,1113,1203,1106,1225,993,1243,1012,1082,1110,1004,989,1072,1076,1100,1213,1163,1220,937,1160,1132,1206,1271,1157, +1280,1299,1484,1423,1264,1417,1198,1259,1318,1303,1342,1413,1185,1310,1112,1504,1318,1388,1303,1392,1330,1217,1124,1375,1184,1439,1286,1431,1277,1300,1269,1324,1198,1410,1436,1153,1171,1297,1529,1139,1252,1276,1397,1376,1339,1280,1301,1146,1227,1303,1277,1239,1122,1313,1444,1336,1289,1461,1322,1437,1257,1302,1310,1431, +1213,1324,1265,1154,1335,1331,1249,1177,1102,1299,1174,1296,1159,1095,1117,1293,1148,1228,1118,1345,1221,1292,1112,1155,1161,1335,1152,1185,1161,1244,1133,1151,1287,1381,1380,1096,1145,1420,1294,1142,1364,1169,1359,1304,1370,1133,1458,1201,1186,1127,1257,1200,1028,1197,1323,1221,1332,1368,1115,1191,1290,1125,1327,1335, +1100,1107,1206,1076,1070,1217,1152,1037,976,1124,1102,1303,1049,1040,882,1175,1105,1061,1042,1170,1089,1200,1062,1149,1001,1083,1126,1249,1103,1233,1170,1129,1187,1130,1274,933,1092,1276,1166,982,1200,1136,1238,1307,1227,953,1190,1037,1182,1145,936,850,1108,1180,1121,1178,1162,1225,1059,1234,1170,1117,1057,1164, +1304,1421,1468,1398,1297,1510,1405,1379,1285,1250,1266,1486,1317,1492,1127,1487,1396,1510,1326,1485,1468,1341,1380,1525,1189,1391,1401,1442,1377,1416,1318,1342,1377,1510,1619,1288,1212,1407,1511,1086,1297,1355,1607,1403,1533,1261,1387,1344,1291,1234,1317,1152,1280,1513,1394,1379,1461,1464,1440,1650,1456,1457,1417,1467, +1311,1229,1205,1189,1294,1243,1263,1098,1123,1304,1394,1277,1192,1149,1133,1228,1157,1342,1180,1163,1268,1249,1113,1244,1099,1237,1215,1302,1102,1304,1141,1175,1185,1392,1483,1056,1062,1223,1203,1080,1165,1221,1384,1235,1295,1041,1492,1081,1176,1246,1169,1099,897,1234,1279,1242,1253,1482,1052,1325,1231,1235,1262,1267, +1040,1219,1342,1241,1334,1251,1081,1293,1204,1209,1230,1364,1124,1250,975,1353,1284,1270,1159,1251,1291,1236,1125,1121,1154,1273,1190,1220,1133,1240,1114,1134,1208,1417,1376,1149,1219,1342,1313,1052,1175,1109,1288,1249,1294,1029,1356,1157,1294,1130,1236,1131,1045,1275,1329,1190,1158,1222,1145,1239,1212,1243,1217,1354, +1330,1514,1426,1478,1427,1534,1396,1449,1113,1412,1455,1612,1408,1417,1252,1604,1356,1498,1443,1472,1434,1509,1309,1542,1292,1482,1586,1417,1344,1534,1440,1396,1500,1590,1601,1427,1239,1447,1447,1338,1503,1249,1509,1459,1763,1201,1585,1278,1501,1317,1409,1322,1357,1424,1436,1494,1504,1462,1386,1541,1526,1580,1334,1504, +1483,1510,1662,1516,1472,1567,1511,1383,1427,1441,1486,1693,1423,1445,1277,1653,1566,1564,1252,1712,1527,1504,1368,1474,1343,1638,1479,1562,1473,1475,1400,1462,1536,1550,1565,1394,1529,1659,1619,1351,1574,1540,1647,1672,1703,1358,1640,1358,1459,1364,1329,1423,1398,1645,1623,1495,1576,1619,1410,1576,1638,1504,1511,1513, +1378,1291,1411,1401,1335,1462,1228,1371,1250,1265,1322,1481,1251,1293,1165,1519,1344,1376,1263,1528,1354,1316,1168,1275,1161,1352,1151,1340,1317,1165,1286,1229,1292,1526,1369,1343,1357,1465,1506,1065,1534,1125,1546,1396,1442,1091,1466,1272,1352,1308,1147,1242,1180,1487,1382,1336,1331,1464,1227,1231,1398,1345,1305,1532, +1346,1268,1352,1280,1401,1465,1207,1261,1254,1325,1211,1445,1266,1284,1084,1275,1388,1362,1081,1393,1339,1344,1202,1257,1253,1346,1235,1335,1205,1225,1189,1267,1311,1467,1462,1328,1183,1386,1372,1146,1313,1354,1377,1324,1560,1051,1474,1183,1329,1275,1320,1132,1053,1471,1323,1273,1426,1453,1205,1349,1508,1382,1197,1463, +1022,1187,1170,1044,1151,1246,1027,1219,1050,1128,1098,1206,1071,1097,1007,1357,1094,1118,1119,1173,1158,1171,1093,1157,1052,1307,1048,1041,1028,1172,1076,1129,1159,1224,1297,1159,1100,1159,1144,1040,1137,1017,1259,1165,1290,1100,1200,1175,1063,1165,1072,1022,1080,1204,1242,1138,1170,1136,1153,1163,1132,1100,1126,1205, +1228,1387,1427,1403,1300,1425,1408,1294,1135,1292,1385,1536,1215,1375,1090,1372,1493,1488,1375,1443,1291,1323,1196,1449,1011,1387,1405,1524,1237,1315,1375,1284,1439,1430,1453,1275,1203,1455,1334,1082,1336,1212,1446,1398,1447,1109,1415,1167,1347,1258,1260,1182,1166,1460,1312,1474,1164,1448,1142,1484,1380,1336,1312,1462, +1229,1355,1264,1183,1314,1315,1133,1150,1185,1272,1267,1278,1256,1081,1103,1341,1182,1357,1212,1377,1320,1300,1159,1211,1155,1350,1139,1159,1123,1117,1120,1224,1267,1341,1296,1181,1190,1433,1408,1056,1392,1231,1416,1354,1320,1066,1387,1219,1231,1083,1171,1302,1194,1261,1386,1159,1144,1254,1142,1345,1164,1230,1272,1376, +1402,1490,1593,1472,1540,1586,1420,1415,1467,1578,1512,1550,1450,1479,1264,1497,1579,1482,1403,1634,1640,1530,1355,1386,1494,1515,1378,1446,1330,1336,1452,1408,1570,1617,1621,1422,1320,1645,1575,1282,1514,1496,1689,1515,1551,1392,1592,1417,1496,1390,1331,1436,1293,1555,1537,1461,1428,1662,1461,1603,1505,1478,1485,1641, +1190,1351,1391,1151,1359,1261,1272,1151,1193,1410,1220,1442,1176,1321,1145,1331,1298,1288,1138,1432,1265,1303,1234,1344,1097,1368,1299,1257,1225,1254,1342,1328,1216,1474,1431,1321,1194,1382,1412,1126,1384,1312,1382,1311,1391,1184,1442,1234,1173,1217,1092,1163,1187,1294,1353,1395,1298,1525,1167,1290,1331,1287,1237,1428, +1304,1416,1446,1406,1444,1463,1304,1344,1205,1344,1408,1391,1263,1330,1190,1426,1445,1377,1141,1490,1381,1376,1240,1404,1244,1378,1310,1436,1337,1454,1351,1288,1410,1408,1562,1208,1295,1477,1469,1192,1434,1246,1467,1463,1576,1277,1378,1198,1375,1277,1300,1163,1179,1544,1309,1334,1374,1525,1258,1383,1395,1343,1357,1443, +1337,1425,1424,1300,1416,1506,1314,1263,1283,1442,1345,1599,1210,1381,1185,1491,1360,1514,1366,1549,1449,1436,1297,1274,1278,1592,1432,1358,1241,1418,1492,1221,1363,1523,1521,1200,1309,1466,1375,1285,1432,1284,1492,1419,1474,1272,1567,1251,1258,1427,1298,1269,1276,1309,1573,1506,1367,1432,1366,1467,1307,1432,1391,1526, +1245,1479,1359,1192,1393,1455,1247,1338,1192,1281,1363,1339,1314,1433,1229,1360,1377,1332,1284,1331,1385,1395,1346,1348,1263,1295,1340,1301,1159,1368,1222,1257,1361,1454,1507,1271,1256,1206,1537,1251,1334,1178,1573,1343,1532,1225,1547,1291,1358,1286,1343,1219,1201,1442,1395,1281,1329,1515,1276,1401,1372,1362,1373,1679, +1088,1321,1295,1136,1190,1421,1235,1158,1223,1093,1118,1345,1176,1361,1049,1237,1373,1226,1196,1435,1316,1245,1186,1244,1044,1246,1207,1342,1137,1222,1302,1160,1238,1386,1398,1125,1219,1352,1315,976,1279,1157,1435,1272,1340,1212,1151,1148,1147,1207,1109,1078,1104,1456,1245,1357,1238,1354,1232,1349,1244,1180,1291,1399, +1233,1346,1483,1244,1296,1440,1206,1207,1228,1387,1335,1338,1222,1302,1034,1380,1488,1306,1266,1492,1447,1316,1269,1265,1181,1368,1261,1244,1149,1343,1204,1310,1313,1503,1547,1292,1255,1242,1249,1197,1355,1234,1505,1377,1462,1245,1408,1104,1243,1398,1216,1132,1221,1395,1361,1429,1450,1494,1145,1375,1395,1375,1295,1414, +1150,1376,1431,1299,1328,1327,1091,1375,1134,1150,1289,1330,1179,1324,1130,1429,1436,1311,1203,1401,1274,1414,1187,1409,1205,1395,1449,1332,1164,1307,1128,1333,1378,1352,1505,1192,1250,1254,1451,1217,1289,1257,1433,1468,1507,1226,1471,1181,1337,1189,1240,1139,1172,1376,1394,1321,1324,1470,1213,1393,1332,1401,1269,1547, +1086,1274,1427,1184,1336,1347,1301,1288,1076,1234,1239,1313,1176,1249,1188,1370,1234,1309,1243,1253,1244,1162,1137,1358,1024,1365,1262,1301,1150,1258,1228,1221,1250,1336,1448,1214,1060,1271,1290,1135,1225,1045,1454,1293,1480,1147,1399,1131,1121,1131,1116,1150,1134,1318,1381,1327,1183,1343,1102,1294,1311,1241,1247,1276, +1055,1338,1289,1170,1280,1362,1196,1278,1246,1310,1316,1358,1177,1180,1127,1391,1160,1306,1197,1344,1291,1294,1137,1177,1097,1365,1251,1240,1111,1305,1330,1218,1345,1410,1325,1109,1232,1329,1290,1099,1453,1200,1285,1355,1391,1092,1356,1205,1247,1158,1168,1232,1166,1421,1269,1371,1216,1398,1251,1336,1261,1328,1173,1321, +1241,1362,1541,1375,1528,1481,1285,1364,1179,1334,1358,1569,1284,1388,1337,1545,1414,1401,1245,1493,1384,1470,1260,1417,1267,1581,1456,1544,1325,1443,1378,1432,1360,1517,1612,1293,1353,1448,1561,1323,1417,1354,1493,1485,1468,1348,1504,1294,1413,1278,1469,1253,1298,1438,1467,1442,1423,1520,1282,1510,1463,1438,1355,1512, +1191,1405,1548,1192,1355,1385,1281,1306,1116,1400,1443,1404,1205,1296,1223,1375,1174,1249,1177,1294,1233,1342,1279,1290,1032,1370,1345,1204,1249,1404,1243,1196,1357,1300,1416,1119,1227,1290,1448,1139,1231,1347,1457,1401,1401,1252,1517,1270,1319,1150,1263,1046,1187,1352,1436,1425,1350,1436,1271,1350,1371,1359,1370,1384, +1533,1515,1630,1488,1528,1655,1407,1376,1435,1595,1587,1596,1514,1517,1354,1704,1603,1606,1510,1715,1590,1563,1342,1497,1435,1593,1392,1554,1467,1607,1335,1566,1479,1775,1667,1446,1608,1589,1735,1394,1572,1454,1692,1573,1659,1501,1695,1421,1608,1406,1575,1572,1313,1565,1659,1484,1697,1825,1410,1592,1599,1564,1547,1710, +1030,1264,1235,1155,1239,1140,1107,1169,1228,1181,1199,1289,1264,1173,908,1223,1267,1189,1040,1262,1244,1091,1075,1137,1063,1274,1222,1202,1090,1111,969,1306,1205,1427,1291,1148,953,1258,1254,1058,1239,1108,1343,1147,1290,1008,1357,1026,1152,999,1101,1209,994,1239,1195,1079,1115,1298,1073,1232,1165,1155,1254,1254, +1243,1402,1384,1183,1246,1257,1266,1330,1115,1206,1321,1437,1353,1240,1078,1420,1196,1383,1183,1344,1188,1398,1211,1321,1227,1379,1400,1246,1291,1312,1190,1233,1403,1285,1511,1135,1150,1454,1366,1224,1258,1221,1410,1342,1498,1066,1384,1252,1288,1102,1202,1247,1224,1436,1536,1461,1352,1494,1232,1360,1440,1282,1332,1466, +1376,1432,1337,1363,1390,1476,1213,1241,1248,1363,1401,1387,1244,1207,1079,1454,1381,1566,1314,1472,1399,1322,1191,1356,1247,1391,1336,1360,1269,1270,1294,1360,1366,1462,1550,1256,1130,1431,1423,1110,1321,1340,1438,1383,1399,1126,1480,1170,1290,1251,1342,1232,1060,1371,1438,1266,1461,1509,1254,1567,1297,1286,1327,1459, +1238,1391,1654,1282,1535,1583,1349,1371,1349,1438,1396,1503,1400,1483,1352,1670,1451,1452,1393,1511,1507,1553,1343,1431,1373,1456,1367,1364,1333,1462,1290,1251,1432,1511,1557,1338,1462,1445,1472,1429,1484,1300,1658,1546,1579,1316,1561,1295,1411,1270,1516,1399,1360,1430,1484,1458,1469,1569,1336,1635,1471,1468,1405,1548, +1328,1454,1420,1339,1301,1584,1286,1280,1242,1267,1371,1399,1361,1255,1125,1574,1424,1452,1350,1505,1338,1342,1278,1317,1284,1571,1323,1374,1275,1421,1342,1274,1397,1487,1589,1303,1386,1468,1493,1298,1385,1243,1476,1502,1498,1285,1414,1261,1274,1307,1370,1280,1183,1533,1502,1422,1490,1479,1370,1480,1419,1377,1417,1481, +1222,1364,1443,1416,1382,1408,1431,1338,1335,1312,1306,1510,1331,1404,1159,1485,1364,1526,1255,1513,1490,1320,1246,1408,1261,1466,1481,1469,1303,1305,1353,1331,1239,1519,1619,1349,1219,1522,1492,1206,1306,1366,1531,1346,1561,1139,1528,1252,1285,1321,1207,1237,1181,1438,1533,1434,1404,1478,1258,1462,1425,1431,1400,1589, +1207,1377,1473,1278,1359,1422,1204,1284,1197,1383,1316,1329,1130,1287,1145,1371,1422,1446,1296,1427,1422,1393,1162,1300,1248,1385,1149,1205,1164,1347,1224,1188,1328,1362,1530,1244,1269,1378,1281,1228,1304,1222,1561,1470,1516,1302,1417,1272,1254,1233,1184,1200,1226,1510,1500,1344,1353,1468,1197,1424,1285,1343,1358,1503, +1191,1379,1246,1083,1210,1174,1160,1093,1102,1200,1219,1429,1167,1147,923,1368,1318,1166,1231,1362,1215,1256,1084,1156,1002,1320,1218,1141,1110,1254,1116,1175,1212,1297,1225,1082,1283,1247,1229,1081,1218,1143,1336,1235,1273,986,1269,1137,1107,975,1089,1161,1112,1296,1385,1268,1172,1429,1053,1191,1338,1190,1197,1337, +1259,1436,1359,1390,1411,1479,1330,1340,1282,1402,1434,1360,1272,1425,1205,1464,1312,1423,1284,1440,1498,1374,1303,1352,1285,1402,1470,1302,1314,1571,1313,1321,1376,1566,1517,1295,1295,1339,1354,1200,1421,1357,1432,1424,1544,1293,1445,1228,1226,1258,1330,1254,1275,1477,1365,1285,1438,1490,1414,1492,1384,1388,1358,1277, +1203,1315,1492,1177,1365,1245,1219,1234,1241,1305,1174,1385,1160,1270,1017,1348,1315,1297,1118,1263,1216,1289,1330,1083,1083,1391,1298,1197,1140,1290,1254,1276,1399,1398,1534,1247,1368,1413,1429,1207,1287,1267,1412,1361,1376,1170,1480,1240,1385,1103,1117,1211,1131,1291,1320,1385,1357,1369,1198,1306,1478,1417,1300,1326, +1277,1318,1539,1185,1440,1354,1365,1235,1312,1446,1350,1412,1227,1379,1083,1407,1381,1347,1233,1348,1337,1269,1350,1304,1212,1405,1226,1215,1164,1413,1163,1254,1348,1431,1470,1297,1250,1341,1255,1221,1268,1348,1484,1347,1474,1210,1514,1269,1228,1253,1159,1259,1213,1468,1432,1361,1359,1421,1314,1402,1464,1348,1241,1340, +1276,1309,1326,1354,1357,1323,1325,1275,1202,1251,1403,1463,1174,1212,1064,1536,1341,1326,1305,1491,1351,1375,1168,1253,1217,1436,1278,1363,1195,1441,1184,1246,1358,1420,1478,1120,1412,1381,1337,1141,1416,1238,1449,1368,1412,1217,1425,1182,1378,1282,1199,1218,1140,1381,1329,1357,1345,1478,1152,1357,1294,1417,1230,1416, +1071,1204,1308,1204,1446,1269,1175,1132,1209,1188,1315,1368,1169,1283,1042,1331,1380,1409,1113,1452,1307,1186,1166,1205,1120,1335,1265,1279,1207,1236,1194,1144,1336,1483,1401,1154,1251,1352,1293,1073,1423,1138,1343,1257,1280,1237,1323,1077,1118,1105,1228,1177,1158,1301,1205,1194,1234,1343,1162,1376,1295,1242,1234,1213, +}; + +#define MATRIX_SIZE 64 +#endif \ No newline at end of file diff --git a/sw/applications/example_dma/main.c b/sw/applications/example_dma/main.c index d92ad700e..6ed19c0c9 100644 --- a/sw/applications/example_dma/main.c +++ b/sw/applications/example_dma/main.c @@ -63,10 +63,10 @@ dma_data_type_t C_type_2_dma_type(int C_type) } #define WAIT_DMA \ - while (!dma_is_ready()) \ + while (!dma_is_ready(0)) \ { \ CSR_CLEAR_BITS(CSR_REG_MSTATUS, 0x8); \ - if (dma_is_ready() == 0) \ + if (dma_is_ready(0) == 0) \ { \ wait_for_interrupt(); \ } \ @@ -106,7 +106,7 @@ dma_data_type_t C_type_2_dma_type(int C_type) { \ if (src[i] != dst[i]) \ { \ - PRINTF("[%d] Expected: %x Got : %x\n", i, src[i], dst[i]); \ + PRINTF("[%d] Expected: %x Got : %x\n\r", i, src[i], dst[i]); \ errors++; \ } \ } \ @@ -134,13 +134,13 @@ dma_data_type_t C_type_2_dma_type(int C_type) trans.src = &tgt_src; \ trans.dst = &tgt_dst; \ trans.src_addr = &tgt_addr; \ - trans.src_type = dma_dst_type; \ + trans.src_type = dma_src_type; \ trans.dst_type = dma_dst_type; \ trans.mode = DMA_TRANS_MODE_SINGLE; \ trans.win_du = 0; \ trans.sign_ext = signed; \ trans.end = DMA_TRANS_END_INTR; \ - trans.dim = DMA_DIM_CONF_1D; + trans.dim = DMA_DIM_CONF_1D; \ #define TEST(C_src_type, C_dst_type, test_size, sign_extend) \ PRINT_TEST(sign_extend, test_size, C_type_2_dma_type(sizeof(C_src_type)), C_type_2_dma_type(sizeof(C_dst_type))) \ @@ -207,7 +207,7 @@ int32_t errors = 0; int8_t cycles = 0; // INTERRUPT HANDLERS -void dma_intr_handler_trans_done(void) +void dma_intr_handler_trans_done(uint8_t channel) { cycles++; } @@ -216,9 +216,8 @@ void dma_intr_handler_trans_done(void) int32_t window_intr_flag; -void dma_intr_handler_window_done(void) -{ - window_intr_flag++; +void dma_intr_handler_window_done(uint8_t channel) { + window_intr_flag ++; } uint8_t dma_window_ratio_warning_threshold() @@ -228,6 +227,8 @@ uint8_t dma_window_ratio_warning_threshold() #endif // TEST_WINDOW +dma_trans_t trans; + int main(int argc, char *argv[]) { @@ -251,7 +252,6 @@ int main(int argc, char *argv[]) .size_du = TEST_DATA_SIZE, .trig = DMA_TRIG_MEMORY, }; - dma_trans_t trans; #ifdef TEST_SINGLE_MODE @@ -424,8 +424,7 @@ int main(int argc, char *argv[]) { while (cycles < consecutive_trans) { - while (!dma_is_ready()) - ; + while (!dma_is_ready(0)); cycles++; } } @@ -490,16 +489,15 @@ int main(int argc, char *argv[]) if (trans.end == DMA_TRANS_END_POLLING) { // There will be no interrupts whatsoever! - while (!dma_is_ready()) - ; + while (!dma_is_ready(0)); PRINTF("?\n\r"); } else { - while (!dma_is_ready()) + while (!dma_is_ready(0)) { wait_for_interrupt(); - PRINTF("i\n\r"); + PRINTF("i\n\r");//@ToDo: is this a debugging? } } diff --git a/sw/applications/example_dma_2d/main.c b/sw/applications/example_dma_2d/main.c index de7521efd..0a5651c12 100644 --- a/sw/applications/example_dma_2d/main.c +++ b/sw/applications/example_dma_2d/main.c @@ -99,7 +99,7 @@ dma_input_data_type copied_data_1D_CPU[OUT_DIM_2D]; dma_config_flags_t res_valid, res_load, res_launch; -dma *peri = dma_peri; +dma *peri = dma_peri(0); dma_target_t tgt_src; dma_target_t tgt_dst; @@ -122,32 +122,6 @@ uint8_t stride_1d_cnt = 0; uint8_t stride_2d_cnt = 0; char passed = 1; -#ifdef TEST_ID_3 - -/* Function used to simplify register operations */ -static inline volatile void write_register( uint32_t p_val, - uint32_t p_offset, - uint32_t p_mask, - uint8_t p_sel, - dma* peri ) -{ - /* - * The index is computed to avoid needing to access the structure - * as a structure. - */ - uint8_t index = p_offset / sizeof(int); - - /* - * An intermediate variable "value" is used to prevent writing twice into - * the register. - */ - uint32_t value = (( uint32_t * ) peri ) [ index ]; - value &= ~( p_mask << p_sel ); - value |= (p_val & p_mask) << p_sel; - (( uint32_t * ) peri ) [ index ] = value; -}; -#endif - int main() { #ifdef TEST_ID_0 @@ -161,7 +135,7 @@ int main() CSR_WRITE(CSR_REG_MCYCLE, 0); #endif - tgt_src.ptr = test_data; + tgt_src.ptr = (uint8_t *) test_data; tgt_src.inc_du = SRC_INC_D1; tgt_src.inc_d2_du = SRC_INC_D2; tgt_src.size_du = SIZE_EXTR_D1; @@ -169,7 +143,7 @@ int main() tgt_src.trig = DMA_TRIG_MEMORY; tgt_src.type = DMA_DATA_TYPE; - tgt_dst.ptr = copied_data_2D_DMA; + tgt_dst.ptr = (uint8_t *) copied_data_2D_DMA; tgt_dst.inc_du = DST_INC_D1; tgt_dst.inc_d2_du = DST_INC_D2; tgt_dst.size_du = OUT_D1_PAD_STRIDE; @@ -206,13 +180,13 @@ int main() PRINTF("laun: %u \t%s\n\r", res_launch, res_launch == DMA_CONFIG_OK ? "Ok!" : "Error!"); #endif - while( ! dma_is_ready()) { + while( ! dma_is_ready(0)) { #if !EN_PERF /* Disable_interrupts */ /* This does not prevent waking up the core as this is controlled by the MIP register */ CSR_CLEAR_BITS(CSR_REG_MSTATUS, 0x8); - if ( dma_is_ready() == 0 ) { + if ( dma_is_ready(0) == 0 ) { wait_for_interrupt(); /* From here the core wakes up even if we did not jump to the ISR */ } @@ -347,7 +321,7 @@ int main() CSR_WRITE(CSR_REG_MCYCLE, 0); #endif - tgt_src.ptr = &test_data[0]; + tgt_src.ptr = (uint8_t *) test_data; tgt_src.inc_du = SRC_INC_TRSP_D1; tgt_src.inc_d2_du = SRC_INC_TRSP_D2; tgt_src.size_du = SIZE_EXTR_D1; @@ -355,7 +329,7 @@ int main() tgt_src.trig = DMA_TRIG_MEMORY; tgt_src.type = DMA_DATA_TYPE; - tgt_dst.ptr = &copied_data_2D_DMA[0]; + tgt_dst.ptr = (uint8_t *) copied_data_2D_DMA; tgt_dst.inc_du = DST_INC_D1; tgt_dst.inc_d2_du = DST_INC_D2; tgt_dst.trig = DMA_TRIG_MEMORY; @@ -390,13 +364,13 @@ int main() PRINTF("laun: %u \t%s\n\r", res_launch, res_launch == DMA_CONFIG_OK ? "Ok!" : "Error!"); #endif - while( ! dma_is_ready()) { + while( ! dma_is_ready(0)) { #if !EN_PERF /* Disable_interrupts */ /* This does not prevent waking up the core as this is controlled by the MIP register */ CSR_CLEAR_BITS(CSR_REG_MSTATUS, 0x8); - if ( dma_is_ready() == 0 ) { + if ( dma_is_ready(0) == 0 ) { wait_for_interrupt(); /* From here the core wakes up even if we did not jump to the ISR */ } @@ -526,7 +500,7 @@ int main() CSR_WRITE(CSR_REG_MCYCLE, 0); #endif - tgt_src.ptr = &test_data[0]; + tgt_src.ptr = (uint8_t *) test_data; tgt_src.inc_du = SRC_INC_D1; tgt_src.size_du = SIZE_EXTR_D1; tgt_src.inc_d2_du = 0; @@ -534,7 +508,7 @@ int main() tgt_src.trig = DMA_TRIG_MEMORY; tgt_src.type = DMA_DATA_TYPE; - tgt_dst.ptr = copied_data_1D_DMA; + tgt_dst.ptr = (uint8_t *) copied_data_1D_DMA; tgt_dst.inc_du = DST_INC_D1; tgt_dst.inc_d2_du = 0; tgt_dst.trig = DMA_TRIG_MEMORY; @@ -569,12 +543,12 @@ int main() PRINTF("laun: %u \t%s\n\r", res_launch, res_launch == DMA_CONFIG_OK ? "Ok!" : "Error!"); #endif - while( ! dma_is_ready()) { + while( ! dma_is_ready(0)) { #if !EN_PERF /* Disable_interrupts */ CSR_CLEAR_BITS(CSR_REG_MSTATUS, 0x8); - if ( dma_is_ready() == 0 ) { + if ( dma_is_ready(0) == 0 ) { wait_for_interrupt(); } CSR_SET_BITS(CSR_REG_MSTATUS, 0x8); @@ -702,8 +676,8 @@ int main() CSR_SET_BITS(CSR_REG_MIE, DMA_CSR_REG_MIE_MASK); /* Pointer set up */ - peri->SRC_PTR = &test_data[0]; - peri->DST_PTR = copied_data_2D_DMA; + peri->SRC_PTR = (uint32_t) (test_data); + peri->DST_PTR = (uint32_t) (copied_data_2D_DMA); /* Dimensionality configuration */ write_register( 0x1, @@ -795,13 +769,13 @@ int main() DMA_SIZE_D1_SIZE_OFFSET, peri ); - while( ! dma_is_ready()) { + while( ! dma_is_ready(0)) { #if !EN_PERF /* Disable_interrupts */ /* This does not prevent waking up the core as this is controlled by the MIP register */ CSR_CLEAR_BITS(CSR_REG_MSTATUS, 0x8); - if ( dma_is_ready() == 0 ) { + if ( dma_is_ready(0) == 0 ) { wait_for_interrupt(); /* From here the core wakes up even if we did not jump to the ISR */ } diff --git a/sw/applications/example_dma_external/main.c b/sw/applications/example_dma_external/main.c index 6afa8ab6f..e73f84af0 100644 --- a/sw/applications/example_dma_external/main.c +++ b/sw/applications/example_dma_external/main.c @@ -29,7 +29,7 @@ int32_t errors = 0; -void dma_intr_handler_trans_done() +void dma_intr_handler_trans_done(uint8_t channel) { PRINTF("D"); } @@ -82,11 +82,11 @@ int main(int argc, char *argv[]) res = dma_launch(&trans); PRINTF("laun: %u \t%s\n\r", res, res == DMA_CONFIG_OK ? "Ok!" : "Error!"); - while( ! dma_is_ready() ){ + while( ! dma_is_ready(0) ){ // disable_interrupts // this does not prevent waking up the core as this is controlled by the MIP register CSR_CLEAR_BITS(CSR_REG_MSTATUS, 0x8); - if ( dma_is_ready() == 0 ) { + if ( dma_is_ready(0) == 0 ) { wait_for_interrupt(); //from here we wake up even if we did not jump to the ISR } diff --git a/sw/applications/example_dma_multichannel/main.c b/sw/applications/example_dma_multichannel/main.c new file mode 100644 index 000000000..2e7f2bc8e --- /dev/null +++ b/sw/applications/example_dma_multichannel/main.c @@ -0,0 +1,1261 @@ +/* + * Copyright EPFL contributors. + * Licensed under the Apache License, Version 2.0, see LICENSE for details. + * SPDX-License-Identifier: Apache-2.0 + * + * Author: Tommaso Terzano + * + * Info: Example application of matrix manipulation by exploiting the multichannel feature of the DMA subsystem. + * This code is capable of testing the following features: + * - Verification of matrix operations carried out by the DMA subsystem + * - Performance comparison between the DMA multichannel and the CPU, obtained by performing similar matrix operations + * and monitoring the performance counter. + * The performance of the DMA is compared against sequential CPU loops for each operation performed by a single channel. + * A typical case in which the DMA could be used to improve the performance is a series of matrix operations, + * like extracting 3 matrices from a larger one. + * By exploiting the DMA, these three separate calls can be performed in parallel. + */ + +#include +#include +#include "dma.h" +#include "core_v_mini_mcu.h" +#include "x-heep.h" +#include "csr.h" +#include "rv_plic.h" +#include "test_data.h" +#include "w25q128jw.h" + +/* + * The following code is designed to test the DMA subsystem multichannel feature. In order to do so, several + * tests are available, which are run using some or all of the DMA channels available. + * + * DISCLAIMER: + * When using the default memory configuration (64kB), pay attention to the dimensions of the output matrices. + * When executing TEST_ID_4 on QuestaSim, make sure to enable the SPI FLASH. + * + * Enable one or more of the following tests by defining the correct TEST_ID_* macro: + * + * 0: Extract a NxM matrix, perform optional padding and copy the result to two separate + * AxB matrices using N channels at the same time and using direct register writes. + * Additionally, each DMA channel is set with a window of M size. For each window interrupt, + * the correct window flag is set. + * + * 1: Extract a NxM matrix, perform optional padding and copy the result to two separate + * AxB matrices using N channels at the same time and using HALs. For each transaction interrupt, + * the correct transaction flag is set. + * + * 2: Each DMA channel performs, alternatively, one of the following operations: + * - Extract a NxM matrix, perform optional padding and copy the result + * - Extract a NxM matrix, perform optional padding, transpose it and copy the result + * + * 3: Extract a NxM matrix, perform optional padding and copy the result to a location using one channel (with HALs), + * while at the same time read a buffer from SPI and copy it to another location using another channel (with HALs). + * This test can only be performed on FPGA boards or using QuestaSim, by setting the correct macro (SIM_QUESTASIM). + * When executing on QuestaSim, make sure to compile in the correct way: + * - Include LINKER=flash_load in "make app ..." + * - Add boot_sel and execute_from_flash: + * 'make run PLUSARGS="c firmware=../../../sw/build/main.hex boot_sel=1 execute_from_flash=0" ' + * + */ + +#define TEST_ID_0 +#define TEST_ID_1 +#define TEST_ID_2 +#define TEST_ID_3 + +/* Enable performance analysis */ +#define EN_PERF 1 + +/* Enable verification */ +#define EN_VERIF 1 + +/* Parameters */ + +/* Size of the extracted matrix (including strides on the input, excluding strides on the outputs) */ +#define SIZE_EXTR_D1 10 +#define SIZE_EXTR_D2 10 + +/* Set strides of the input ad output matrix */ +#define STRIDE_IN_D1 1 +#define STRIDE_IN_D2 1 +#define STRIDE_OUT_D1 1 +#define STRIDE_OUT_D2 1 + +/* Set the padding parameters */ +#define TOP_PAD 0 +#define BOTTOM_PAD 0 +#define LEFT_PAD 0 +#define RIGHT_PAD 0 + +/* Macros for dimensions computation */ +#define OUT_D1_PAD ( SIZE_EXTR_D1 + LEFT_PAD + RIGHT_PAD ) +#define OUT_D2_PAD ( SIZE_EXTR_D2 + TOP_PAD + BOTTOM_PAD ) +#define OUT_D1_PAD_STRIDE ( (OUT_D1_PAD * STRIDE_OUT_D1) - (STRIDE_OUT_D1 - 1) ) +#define OUT_D2_PAD_STRIDE ( (OUT_D2_PAD * STRIDE_OUT_D2) - (STRIDE_OUT_D2 - 1) ) +#define OUT_DIM_1D ( OUT_D1_PAD_STRIDE ) +#define OUT_DIM_2D ( OUT_D1_PAD_STRIDE * OUT_D2_PAD_STRIDE ) + +/* + * Window size for the DMA. Since it has to be > 71 for the ISR to have the time to react, + * the OUT_DIM_2D has to be big enough. DMA_WINDOW_SIZE is by default set to 80 for good measure. + */ + +#define DMA_WINDOW_SIZE 80 + +#if defined(TEST_ID_0) && ((OUT_DIM_2D < DMA_WINDOW_SIZE) || (DMA_WINDOW_SIZE < 72)) + #error "In order to correctly execute TEST_ID_0, the matrix output dimension has to be bigger than 72 and bigger than the window dimension.\nCheck these parameters and recompile.\n" +#endif + +/* Defines for DMA channels */ +#define DMA_CH0_IDX 0 +#define DMA_CH1_IDX 1 +#define DMA_CH2_IDX 2 +#define DMA_CH3_IDX 3 + +/* Assigning a pointer to a define writes the pointed array in the flash */ +#define TEST_DATA_FLASH_PTR test_data_flash + +/* Mask for direct register operations example */ +#define DMA_CSR_REG_MIE_MASK (( 1 << 19 ) | (1 << 11 )) + +/* Transposition example def */ +#define TRANSPOSITION_EN 1 + +/* Pointer increments computation */ +#define SRC_INC_D1 STRIDE_IN_D1 +#define DST_INC_D1 STRIDE_OUT_D1 +#define SRC_INC_D2 (STRIDE_IN_D2 * SIZE_IN_D1 - (SIZE_EXTR_D1 - 1 + (STRIDE_IN_D1 - 1) * (SIZE_EXTR_D1 - 1))) +#define DST_INC_D2 ((STRIDE_OUT_D2 - 1) * OUT_DIM_1D + 1) +#define SRC_INC_TRSP_D1 SRC_INC_D1 +#define SRC_INC_TRSP_D2 (STRIDE_IN_D2 * SIZE_IN_D1) + +/* By default, printfs are activated for FPGA and disabled for simulation. */ +#define PRINTF_IN_FPGA 1 +#define PRINTF_IN_SIM 0 + +#if TARGET_SIM && PRINTF_IN_SIM + #define PRINTF(fmt, ...) printf(fmt, ## __VA_ARGS__) +#elif TARGET_IS_FPGA && PRINTF_IN_FPGA + #define PRINTF(fmt, ...) printf(fmt, ## __VA_ARGS__) +#else + #define PRINTF(...) +#endif + +/* FPGA SPI board selection */ +#if !TARGET_SIM + #define USE_SPI_FLASH +#endif + +/* QuestaSim macro to enable test 4, by default is disabled */ +//#define SIM_QUESTASIM + +/* Size of FLASH buffer */ +#define TEST_DATA_FLASH_SIZE 32 + +/* Memory allocation for examples */ +uint32_t copied_test_data_flash[TEST_DATA_FLASH_SIZE]; +dma_input_data_type copied_data_2D_DMA[DMA_CH_NUM][OUT_DIM_2D]; +dma_input_data_type copied_data_2D_CPU[DMA_CH_NUM][OUT_DIM_2D]; + +#if (TARGET_SIM == 0 || defined(TARGET_QUESTASIM)) +/* Data for TEST_ID_3 to be stored in the FLASH */ +uint32_t __attribute__((section(".xheep_data_flash_only"))) __attribute__ ((aligned (16))) test_data_flash[TEST_DATA_FLASH_SIZE] = { + 105 ,82 ,221 ,172 ,77 ,62, + 81 ,185 ,33 ,213 ,249 ,117, + 69 ,212 ,99 ,137 ,9 ,233, + 107 ,105 ,166 ,141 ,53 ,207, + 53 ,21 ,221 ,102 ,84 ,108, + 43 ,99 ,123 ,71 ,30 ,179 + }; + +/* Data for TEST_ID_3 to compare the copied data from the FLASH */ +uint32_t test_data_flash_golden[TEST_DATA_FLASH_SIZE] = { + 105 ,82 ,221 ,172 ,77 ,62, + 81 ,185 ,33 ,213 ,249 ,117, + 69 ,212 ,99 ,137 ,9 ,233, + 107 ,105 ,166 ,141 ,53 ,207, + 53 ,21 ,221 ,102 ,84 ,108, + 43 ,99 ,123 ,71 ,30 ,179 + }; +#endif + +/* DMA source, destination and transaction */ +dma_target_t tgt_src; +dma_target_t tgt_src_trsp; +dma_target_t tgt_dst[DMA_CH_NUM]; +dma_trans_t trans[DMA_CH_NUM]; + +dma_config_flags_t res_valid, res_load, res_launch; + +/* CPU computation variables */ +uint32_t dst_ptr = 0, src_ptr = 0; +uint32_t cycles_dma, cycles_cpu; +uint32_t size_dst_trans_d1; +uint32_t dst_stride_d1; +uint32_t dst_stride_d2; +uint32_t size_src_trans_d1; +uint32_t src_stride_d1; +uint32_t src_stride_d2; +uint32_t i_in; +uint32_t j_in; +uint32_t i_in_last; +uint32_t j_in_last; +uint16_t left_pad_cnt = 0; +uint16_t top_pad_cnt = 0; +uint8_t stride_1d_cnt = 0; +uint8_t stride_2d_cnt = 0; +uint8_t transaction_flag[DMA_CH_NUM]; +uint8_t window_flag[DMA_CH_NUM]; +char passed = 1; +char flag = 0; + +/* Strong transaction ISR implementation */ +void dma_intr_handler_trans_done(uint8_t channel) +{ + transaction_flag[channel] = 1; + return; +} + +/* Strong window ISR implementation */ +void dma_intr_handler_window_done(uint8_t channel) +{ + window_flag[channel] = 1; + return; +} + +int main() +{ + + #ifdef TEST_ID_0 + + /* + * Testing copy and padding of a NxM matrix using direct register operations. + * This strategy allows for maximum performance but doesn't perform any checks on the data integrity. + * The data is copied using N channels to N different memory locations. + */ + + #if EN_PERF + + /* Reset the counter to evaluate the performance of the DMA */ + CSR_CLEAR_BITS(CSR_REG_MCOUNTINHIBIT, 0x1); + CSR_WRITE(CSR_REG_MCYCLE, 0); + #endif + + /* The DMA channels are initialized (i.e. Any current transaction is cleaned.) */ + dma_init(NULL); + + plic_Init(); + plic_irq_set_priority( DMA_WINDOW_INTR, 1); + plic_irq_set_enabled( DMA_WINDOW_INTR, kPlicToggleEnabled); + + /* Enable global interrupts */ + CSR_SET_BITS(CSR_REG_MSTATUS, 0x8); + + /* Enable fast interrupts */ + CSR_SET_BITS(CSR_REG_MIE, DMA_CSR_REG_MIE_MASK); + + + for (int i=0; iSRC_PTR = &test_data[0]; + dma_peri(i)->DST_PTR = copied_data_2D_DMA[i]; + + /* Dimensionality configuration */ + write_register( 0x1, + DMA_DIM_CONFIG_REG_OFFSET, + 0x1, + DMA_DIM_CONFIG_DMA_DIM_BIT, + dma_peri(i) ); + + /* Operation mode configuration */ + write_register( DMA_TRANS_MODE_SINGLE, + DMA_MODE_REG_OFFSET, + DMA_MODE_MODE_MASK, + DMA_MODE_MODE_OFFSET, + dma_peri(i) ); + + /* Data type configuration */ + write_register( DMA_DATA_TYPE, + DMA_DST_DATA_TYPE_REG_OFFSET, + DMA_DST_DATA_TYPE_DATA_TYPE_MASK, + DMA_DST_DATA_TYPE_DATA_TYPE_OFFSET, + dma_peri(i) ); + + write_register( DMA_DATA_TYPE, + DMA_SRC_DATA_TYPE_REG_OFFSET, + DMA_SRC_DATA_TYPE_DATA_TYPE_MASK, + DMA_SRC_DATA_TYPE_DATA_TYPE_OFFSET, + dma_peri(i) ); + + /* Set the source strides */ + write_register( SRC_INC_D1 * DMA_DATA_TYPE_2_SIZE(DMA_DATA_TYPE), + DMA_SRC_PTR_INC_D1_REG_OFFSET, + DMA_SRC_PTR_INC_D1_INC_MASK, + DMA_SRC_PTR_INC_D1_INC_OFFSET, + dma_peri(i) ); + + write_register( SRC_INC_D2 * DMA_DATA_TYPE_2_SIZE(DMA_DATA_TYPE), + DMA_SRC_PTR_INC_D2_REG_OFFSET, + DMA_SRC_PTR_INC_D2_INC_MASK, + DMA_SRC_PTR_INC_D2_INC_OFFSET, + dma_peri(i) ); + + write_register( DST_INC_D1 * DMA_DATA_TYPE_2_SIZE( DMA_DATA_TYPE), + DMA_DST_PTR_INC_D1_REG_OFFSET, + DMA_DST_PTR_INC_D1_INC_MASK, + DMA_DST_PTR_INC_D1_INC_OFFSET, + dma_peri(i) ); + + write_register( DST_INC_D2 * DMA_DATA_TYPE_2_SIZE( DMA_DATA_TYPE), + DMA_DST_PTR_INC_D2_REG_OFFSET, + DMA_DST_PTR_INC_D2_INC_MASK, + DMA_DST_PTR_INC_D2_INC_OFFSET, + dma_peri(i) ); + + /* Padding configuration */ + write_register( TOP_PAD * DMA_DATA_TYPE_2_SIZE( DMA_DATA_TYPE), + DMA_PAD_TOP_REG_OFFSET, + DMA_PAD_TOP_PAD_MASK, + DMA_PAD_TOP_PAD_OFFSET, + dma_peri(i) ); + + write_register( RIGHT_PAD * DMA_DATA_TYPE_2_SIZE( DMA_DATA_TYPE), + DMA_PAD_RIGHT_REG_OFFSET, + DMA_PAD_RIGHT_PAD_MASK, + DMA_PAD_RIGHT_PAD_OFFSET, + dma_peri(i) ); + + write_register( LEFT_PAD * DMA_DATA_TYPE_2_SIZE( DMA_DATA_TYPE), + DMA_PAD_LEFT_REG_OFFSET, + DMA_PAD_LEFT_PAD_MASK, + DMA_PAD_LEFT_PAD_OFFSET, + dma_peri(i) ); + + write_register( BOTTOM_PAD * DMA_DATA_TYPE_2_SIZE( DMA_DATA_TYPE), + DMA_PAD_BOTTOM_REG_OFFSET, + DMA_PAD_BOTTOM_PAD_MASK, + DMA_PAD_BOTTOM_PAD_OFFSET, + dma_peri(i) ); + + /* Set the window size */ + write_register( DMA_WINDOW_SIZE, + DMA_WINDOW_SIZE_REG_OFFSET, + DMA_WINDOW_SIZE_WINDOW_SIZE_MASK, + DMA_WINDOW_SIZE_WINDOW_SIZE_OFFSET, + dma_peri(i) ); + } + + for (int i=0; i= SIZE_EXTR_D2 + TOP_PAD || j_in < LEFT_PAD || j_in >= SIZE_EXTR_D1 + LEFT_PAD || + stride_1d_cnt != 0 || stride_2d_cnt != 0) + { + copied_data_2D_CPU[c][dst_ptr] = 0; + } + else + { + copied_data_2D_CPU[c][dst_ptr] = test_data[src_ptr]; + } + + if (j_in < LEFT_PAD && i_in >= TOP_PAD && stride_1d_cnt == 0 && stride_2d_cnt == 0) + { + left_pad_cnt++; + } + + if (stride_1d_cnt == STRIDE_OUT_D1 - 1) + { + stride_1d_cnt = 0; + j_in++; + } + else + { + stride_1d_cnt++; + } + + } + + if (i_in < TOP_PAD && stride_2d_cnt == 0) + { + top_pad_cnt++; + } + + if (stride_2d_cnt == STRIDE_OUT_D2 - 1) + { + stride_2d_cnt = 0; + i_in++; + } + else + { + stride_2d_cnt++; + } + + left_pad_cnt = 0; + } + } + + #endif + + #if EN_PERF + + /* Read the cycles count after the CPU run */ + CSR_READ(CSR_REG_MCYCLE, &cycles_cpu); + + PRINTF("DMA cycles: %d\n\r", cycles_dma); + PRINTF("CPU cycles: %d \n\r", cycles_cpu); + #endif + + #if EN_VERIF + + /* Verify that the DMA and the CPU outputs are the same */ + for (int c=0; c= SIZE_EXTR_D2 + TOP_PAD || j_in < LEFT_PAD || j_in >= SIZE_EXTR_D1 + LEFT_PAD || + stride_1d_cnt != 0 || stride_2d_cnt != 0) + { + copied_data_2D_CPU[c][dst_ptr] = 0; + } + else + { + copied_data_2D_CPU[c][dst_ptr] = test_data[src_ptr]; + } + + if (j_in < LEFT_PAD && i_in >= TOP_PAD && stride_1d_cnt == 0 && stride_2d_cnt == 0) + { + left_pad_cnt++; + } + + if (stride_1d_cnt == STRIDE_OUT_D1 - 1) + { + stride_1d_cnt = 0; + j_in++; + } + else + { + stride_1d_cnt++; + } + + } + + if (i_in < TOP_PAD && stride_2d_cnt == 0) + { + top_pad_cnt++; + } + + if (stride_2d_cnt == STRIDE_OUT_D2 - 1) + { + stride_2d_cnt = 0; + i_in++; + } + else + { + stride_2d_cnt++; + } + + left_pad_cnt = 0; + } + } + #endif + + #if EN_PERF + + /* Read the cycles count after the CPU run */ + CSR_READ(CSR_REG_MCYCLE, &cycles_cpu); + PRINTF("DMA cycles: %d\n\r", cycles_dma); + PRINTF("CPU cycles: %d \n\r", cycles_cpu); + + #endif + + #if EN_VERIF + + /* Verify that the DMA and the CPU outputs are the same */ + for (int c=0; c= SIZE_EXTR_D2 + TOP_PAD || j_in < LEFT_PAD || j_in >= SIZE_EXTR_D1 + LEFT_PAD || + stride_1d_cnt != 0 || stride_2d_cnt != 0) + { + copied_data_2D_CPU[c][dst_ptr] = 0; + } + else + { + copied_data_2D_CPU[c][dst_ptr] = test_data[src_ptr]; + } + + if (j_in < LEFT_PAD && i_in >= TOP_PAD && stride_1d_cnt == 0 && stride_2d_cnt == 0) + { + left_pad_cnt++; + } + + if (stride_1d_cnt == STRIDE_OUT_D1 - 1) + { + stride_1d_cnt = 0; + j_in++; + } + else + { + stride_1d_cnt++; + } + + } + + if (i_in < TOP_PAD && stride_2d_cnt == 0) + { + top_pad_cnt++; + } + + if (stride_2d_cnt == STRIDE_OUT_D2 - 1) + { + stride_2d_cnt = 0; + i_in++; + } + else + { + stride_2d_cnt++; + } + + left_pad_cnt = 0; + } + flag = 1; + } + else + { + for (int i=0; i < OUT_D2_PAD_STRIDE; i++) + { + stride_1d_cnt = 0; + j_in = 0; + + for (int j=0; j < OUT_D1_PAD_STRIDE; j++) + { + dst_ptr = i * OUT_D1_PAD_STRIDE + j; + src_ptr = (j_in - left_pad_cnt) * STRIDE_IN_D2 * SIZE_IN_D1 + (i_in - top_pad_cnt ) * STRIDE_IN_D1; + if (i_in < TOP_PAD || i_in >= SIZE_EXTR_D2 + TOP_PAD || j_in < LEFT_PAD || j_in >= SIZE_EXTR_D1 + LEFT_PAD || + stride_1d_cnt != 0 || stride_2d_cnt != 0) + { + copied_data_2D_CPU[c][dst_ptr] = 0; + } + else + { + copied_data_2D_CPU[c][dst_ptr] = test_data[src_ptr]; + } + + if (j_in < LEFT_PAD && i_in >= TOP_PAD && stride_1d_cnt == 0 && stride_2d_cnt == 0) + { + left_pad_cnt++; + } + + if (stride_1d_cnt == STRIDE_OUT_D1 - 1) + { + stride_1d_cnt = 0; + j_in++; + } + else + { + stride_1d_cnt++; + } + + } + + if (i_in < TOP_PAD && stride_2d_cnt == 0) + { + top_pad_cnt++; + } + + if (stride_2d_cnt == STRIDE_OUT_D2 - 1) + { + stride_2d_cnt = 0; + i_in++; + } + else + { + stride_2d_cnt++; + } + + left_pad_cnt = 0; + } + flag = 0; + } + } + + #endif + + #if EN_PERF + + /* Read the cycles count after the CPU run */ + CSR_READ(CSR_REG_MCYCLE, &cycles_cpu); + PRINTF("DMA cycles: %d\n\r", cycles_dma); + PRINTF("CPU cycles: %d \n\r", cycles_cpu); + + #endif + + #if EN_VERIF + + /* Verify that the DMA and the CPU outputs are the same */ + for (int c=0; c 1 + + /* Testing SPI2RAM & RAM2RAM operations on 2 channels */ + + /* Reset for fourth test */ + passed = 1; + i_in = 0; + j_in = 0; + left_pad_cnt = 0; + top_pad_cnt = 0; + stride_1d_cnt = 0; + stride_2d_cnt = 0; + for (int i = 0; i < OUT_DIM_2D; i++) { + copied_data_2D_DMA[1][i] = 0; + copied_data_2D_CPU[1][i] = 0; + } + + #if EN_PERF + + /* Reset the counter to evaluate the performance of the DMA */ + CSR_CLEAR_BITS(CSR_REG_MCOUNTINHIBIT, 0x1); + CSR_WRITE(CSR_REG_MCYCLE, 0); + #endif + + dma_init(NULL); + + tgt_src.ptr = (uint8_t *) test_data; + tgt_src.inc_du = SRC_INC_D1; + tgt_src.inc_d2_du = SRC_INC_D2; + tgt_src.size_du = SIZE_EXTR_D1; + tgt_src.size_d2_du = SIZE_EXTR_D2; + tgt_src.trig = DMA_TRIG_MEMORY; + tgt_src.type = DMA_DATA_TYPE; + + tgt_dst[1].ptr = (uint8_t *) copied_data_2D_DMA[1]; + tgt_dst[1].inc_du = DST_INC_D1; + tgt_dst[1].inc_d2_du = DST_INC_D2; + tgt_dst[1].trig = DMA_TRIG_MEMORY; + + trans[1].src = &tgt_src; + trans[1].dst = &tgt_dst[1]; + trans[1].mode = DMA_TRANS_MODE_SINGLE; + trans[1].dim = DMA_DIM_CONF_2D; + trans[1].pad_top_du = TOP_PAD; + trans[1].pad_bottom_du = BOTTOM_PAD; + trans[1].pad_left_du = LEFT_PAD; + trans[1].pad_right_du = RIGHT_PAD; + trans[1].win_du = 0; + trans[1].end = DMA_TRANS_END_INTR; + trans[1].channel = DMA_CH1_IDX; + + /* Initialize the SPI */ + soc_ctrl_t soc_ctrl; + soc_ctrl.base_addr = mmio_region_from_addr((uintptr_t)SOC_CTRL_START_ADDRESS); + + if ( get_spi_flash_mode(&soc_ctrl) == SOC_CTRL_SPI_FLASH_MODE_SPIMEMIO ) { + PRINTF("This application cannot work with the memory mapped SPI FLASH" + "module - do not use the FLASH_EXEC linker script for this application\n"); + return EXIT_SUCCESS; + } + + /* Pick the correct spi device based on simulation type */ + spi_host_t *spi; + #ifndef USE_SPI_FLASH + spi = spi_host1; + #else + spi = spi_flash; + #endif + + /* Init SPI host and SPI<->Flash bridge parameters */ + if (w25q128jw_init(spi) != FLASH_OK) + { + PRINTF("Error initializing the flash SPI\n\r"); + return EXIT_FAILURE; + } + + /* Start the reading process from the SPI, avoiding both sanity checks and waiting for the DMA to finish */ + w25q_error_codes_t status = w25q128jw_read_standard_dma(TEST_DATA_FLASH_PTR, copied_test_data_flash, TEST_DATA_FLASH_SIZE*4, 1, 1); + if (status != FLASH_OK) + { + PRINTF("Error reading from flash\n\r"); + return EXIT_FAILURE; + } + + #if EN_PERF + + dma_validate_transaction(&trans[1], DMA_ENABLE_REALIGN, DMA_PERFORM_CHECKS_INTEGRITY); + dma_load_transaction(&trans[1]); + dma_launch(&trans[1]); + + #else + + res_valid = dma_validate_transaction(&trans_ch1, DMA_ENABLE_REALIGN, DMA_PERFORM_CHECKS_INTEGRITY); + PRINTF("tran: %u \t%s\n\r", res_valid, res_valid == DMA_CONFIG_OK ? "Ok!" : "Error!"); + res_load = dma_load_transaction(&trans_ch1); + PRINTF("load: %u \t%s\n\r", res_load, res_load == DMA_CONFIG_OK ? "Ok!" : "Error!"); + res_launch = dma_launch(&trans_ch1); + PRINTF("laun: %u \t%s\n\r", res_launch, res_launch == DMA_CONFIG_OK ? "Ok!" : "Error!"); + #endif + + /* Wait for CH1 to end */ + while(!dma_is_ready(1)) { + #if !EN_PERF + /* Disable_interrupts */ + /* This does not prevent waking up the core as this is controlled by the MIP register */ + + CSR_CLEAR_BITS(CSR_REG_MSTATUS, 0x8); + if ( dma_is_ready(1) == 0 ) { + wait_for_interrupt(); + /* From here the core wakes up even if we did not jump to the ISR */ + } + CSR_SET_BITS(CSR_REG_MSTATUS, 0x8); + #endif + } + + #if EN_PERF + + /* Read the cycles count after the DMA run */ + CSR_READ(CSR_REG_MCYCLE, &cycles_dma); + + /* Reset the performance counter to evaluate the CPU performance */ + CSR_SET_BITS(CSR_REG_MCOUNTINHIBIT, 0x1); + CSR_WRITE(CSR_REG_MCYCLE, 0); + CSR_CLEAR_BITS(CSR_REG_MCOUNTINHIBIT, 0x1); + #endif + + #if EN_VERIF + + /* Run the same computation on the CPU */ + for (int i=0; i < OUT_D2_PAD_STRIDE; i++) + { + stride_1d_cnt = 0; + j_in = 0; + + for (int j=0; j < OUT_D1_PAD_STRIDE; j++) + { + dst_ptr = i * OUT_D1_PAD_STRIDE + j; + src_ptr = (i_in - top_pad_cnt ) * STRIDE_IN_D2 * SIZE_IN_D1 + (j_in - left_pad_cnt) * STRIDE_IN_D1; + if (i_in < TOP_PAD || i_in >= SIZE_EXTR_D2 + TOP_PAD || j_in < LEFT_PAD || j_in >= SIZE_EXTR_D1 + LEFT_PAD || + stride_1d_cnt != 0 || stride_2d_cnt != 0) + { + copied_data_2D_CPU[1][dst_ptr] = 0; + } + else + { + copied_data_2D_CPU[1][dst_ptr] = test_data[src_ptr]; + } + + if (j_in < LEFT_PAD && i_in >= TOP_PAD && stride_1d_cnt == 0 && stride_2d_cnt == 0) + { + left_pad_cnt++; + } + + if (stride_1d_cnt == STRIDE_OUT_D1 - 1) + { + stride_1d_cnt = 0; + j_in++; + } + else + { + stride_1d_cnt++; + } + + } + + if (i_in < TOP_PAD && stride_2d_cnt == 0) + { + top_pad_cnt++; + } + + if (stride_2d_cnt == STRIDE_OUT_D2 - 1) + { + stride_2d_cnt = 0; + i_in++; + } + else + { + stride_2d_cnt++; + } + + left_pad_cnt = 0; + } + #endif + + #if EN_PERF + + /* Read the cycles count after the CPU run */ + CSR_READ(CSR_REG_MCYCLE, &cycles_cpu); + PRINTF("DMA cycles: %d\n\r", cycles_dma); + PRINTF("CPU cycles: %d \n\r", cycles_cpu); + + #endif + + #if EN_VERIF + + /* Verify that the DMA and the CPU outputs are the same */ + for (int i = 0; i < OUT_D2_PAD_STRIDE; i++) { + for (int j = 0; j < OUT_D1_PAD_STRIDE; j++) { + if ((copied_data_2D_DMA[1][i * OUT_D1_PAD_STRIDE + j] != copied_data_2D_CPU[1][i * OUT_D1_PAD_STRIDE + j])) + { + passed = 0; + } + } + } + + /* Verify that the SPI copy was successful */ + for (int i=0; i < TEST_DATA_FLASH_SIZE; i++) + { + if (copied_test_data_flash[i] != test_data_flash_golden[i]) + { + passed = 0; + } + } + + if (passed) { + PRINTF("Success test 3\n\r"); + } + else + { + PRINTF("Fail test 3\n\r"); + return EXIT_FAILURE; + } + #endif + + #endif + + return EXIT_SUCCESS; +} \ No newline at end of file diff --git a/sw/applications/example_dma_multichannel/test_data.h b/sw/applications/example_dma_multichannel/test_data.h new file mode 100644 index 000000000..c582490e7 --- /dev/null +++ b/sw/applications/example_dma_multichannel/test_data.h @@ -0,0 +1,34 @@ +#ifndef __TEST_DATA_H__ +#define __TEST_DATA_H__ + +#define SIZE_IN_D1 15 +#define SIZE_IN_D2 15 +#define DMA_DATA_TYPE DMA_DATA_TYPE_WORD + +/* + * Change the input datatype depending on the DMA_DATA_TYPE. + * The test data has been generated using byte as datatype, so it's possible to use both uint8_t, uint16_t and uint32_t. + * NOTE: To fully evaluate the performance gains of the DMA it's recommended to increase the number of memory banks and + * the size of the data to be copied up ot at least 50x50. + */ +typedef uint32_t dma_input_data_type; + +dma_input_data_type test_data[SIZE_IN_D1 * SIZE_IN_D2] = { + 236 ,19 ,35 ,109 ,59 ,58 ,216 ,210 ,72 ,169 ,123 ,74 ,132 ,27 ,208, + 43 ,93 ,216 ,145 ,94 ,235 ,80 ,25 ,146 ,127 ,115 ,9 ,45 ,61 ,60, + 78 ,218 ,28 ,230 ,140 ,236 ,152 ,242 ,98 ,173 ,20 ,206 ,176 ,157 ,219, + 225 ,182 ,118 ,22 ,153 ,196 ,7 ,9 ,87 ,99 ,173 ,11 ,103 ,173 ,49, + 122 ,185 ,128 ,93 ,243 ,29 ,172 ,32 ,207 ,64 ,184 ,40 ,165 ,87 ,157, + 182 ,196 ,160 ,93 ,182 ,91 ,20 ,82 ,156 ,45 ,109 ,132 ,168 ,234 ,85, + 121 ,39 ,188 ,109 ,190 ,127 ,192 ,124 ,149 ,90 ,247 ,68 ,26 ,82 ,57, + 189 ,249 ,91 ,193 ,23 ,210 ,167 ,79 ,175 ,31 ,212 ,131 ,102 ,43 ,4, + 192 ,184 ,234 ,9 ,81 ,4 ,221 ,12 ,181 ,76 ,64 ,131 ,59 ,26 ,83, + 86 ,140 ,176 ,18 ,120 ,115 ,153 ,50 ,41 ,83 ,110 ,157 ,173 ,145 ,81, + 9 ,120 ,250 ,3 ,27 ,119 ,64 ,195 ,48 ,245 ,160 ,78 ,177 ,246 ,59, + 156 ,114 ,97 ,7 ,11 ,224 ,75 ,154 ,223 ,221 ,90 ,77 ,254 ,181 ,198, + 193 ,254 ,161 ,177 ,201 ,3 ,182 ,18 ,36 ,23 ,67 ,46 ,116 ,229 ,166, + 90 ,158 ,248 ,194 ,224 ,218 ,245 ,34 ,60 ,126 ,68 ,248 ,143 ,232 ,226, + 58 ,202 ,172 ,209 ,229 ,89 ,246 ,184 ,215 ,7 ,163 ,204 ,43 ,116 ,33}; +; + +#endif \ No newline at end of file diff --git a/sw/applications/example_dma_sdk/main.c b/sw/applications/example_dma_sdk/main.c index 9f96c27c8..22b3ce65c 100644 --- a/sw/applications/example_dma_sdk/main.c +++ b/sw/applications/example_dma_sdk/main.c @@ -2,7 +2,7 @@ // Solderpad Hardware License, Version 2.1, see LICENSE.md for details. // SPDX-License-Identifier: Apache-2.0 WITH SHL-2.1 // -// File: dma_sdk.c +// File: example_dma_sdk.c // Author: Juan Sapriza // Date: 13/06/2024 // Description: Example application to test the DMA SDK. Will copy @@ -11,41 +11,102 @@ // are performed correctly. #include +#include // For compatibility with OH Group compiler #include #include "dma_sdk.h" +#include "dma.h" // For compatibility with OH Group compiler #include "core_v_mini_mcu.h" #include "x-heep.h" +#include "csr.h" // For compatibility with OH Group compiler /* By default, printfs are activated for FPGA and disabled for simulation. */ -#define PRINTF_IN_FPGA 1 -#define PRINTF_IN_SIM 0 +#define PRINTF_IN_FPGA 1 +#define PRINTF_IN_SIM 0 #if TARGET_SIM && PRINTF_IN_SIM - #define PRINTF(fmt, ...) printf(fmt, ## __VA_ARGS__) +#define PRINTF(fmt, ...) printf(fmt, ##__VA_ARGS__) #elif PRINTF_IN_FPGA && !TARGET_SIM - #define PRINTF(fmt, ...) printf(fmt, ## __VA_ARGS__) +#define PRINTF(fmt, ...) printf(fmt, ##__VA_ARGS__) #else - #define PRINTF(...) +#define PRINTF(...) #endif -#define SOURCE_BUFFER_SIZE_32b 5 -#define CONST_VALUE 123 +#define SOURCE_BUFFER_SIZE_32b 5 +#define SOURCE_BUFFER_SIZE_16b 5 +#define SOURCE_BUFFER_SIZE_8b 5 +#define CONST_VALUE_32B 123 +#define CONST_NEG_VALUE_32B -123 +#define CONST_VALUE_16B 123 +#define CONST_NEG_VALUE_16B -123 +#define CONST_VALUE_8B 123 +#define CONST_NEG_VALUE_8B -123 -int main(){ - static uint32_t source[SOURCE_BUFFER_SIZE_32b]; - static uint32_t destin[SOURCE_BUFFER_SIZE_32b]; +static uint32_t source_32b[SOURCE_BUFFER_SIZE_32b]; +static uint32_t destin_32b[SOURCE_BUFFER_SIZE_32b]; - static uint32_t value = CONST_VALUE; +static uint16_t destin_16b[SOURCE_BUFFER_SIZE_16b]; +static uint16_t source_16b[SOURCE_BUFFER_SIZE_16b]; - dma_fill( &source, &value, SOURCE_BUFFER_SIZE_32b ); - dma_copy_32b( &destin, &source, SOURCE_BUFFER_SIZE_32b ); +static uint8_t destin_8b[SOURCE_BUFFER_SIZE_8b]; +static uint8_t source_8b[SOURCE_BUFFER_SIZE_8b]; - uint32_t i; - uint32_t errors = 0; - for( i = 0; i < SOURCE_BUFFER_SIZE_32b; i++){ - errors += destin[i] != CONST_VALUE; +static int32_t neg_source_32b[SOURCE_BUFFER_SIZE_32b]; +static int32_t neg_destin_32b[SOURCE_BUFFER_SIZE_32b]; + +static int16_t neg_destin_16b[SOURCE_BUFFER_SIZE_16b]; +static int16_t neg_source_16b[SOURCE_BUFFER_SIZE_16b]; + +static int8_t neg_destin_8b[SOURCE_BUFFER_SIZE_8b]; +static int8_t neg_source_8b[SOURCE_BUFFER_SIZE_8b]; + +static uint32_t value_32b = CONST_VALUE_32B; +static uint16_t value_16b = CONST_VALUE_16B; +static uint8_t value_8b = CONST_VALUE_8B; + +static int32_t neg_value_32b = CONST_NEG_VALUE_32B; +static int16_t neg_value_16b = CONST_NEG_VALUE_16B; +static int8_t neg_value_8b = CONST_NEG_VALUE_8B; + +uint32_t i; +uint32_t errors = 0; + +int main() +{ + dma_sdk_init(); + + dma_fill((uint32_t)source_32b, (uint32_t)&value_32b, SOURCE_BUFFER_SIZE_32b, 0, DMA_DATA_TYPE_WORD, DMA_DATA_TYPE_WORD, 0); + dma_copy((uint32_t)destin_32b, (uint32_t)source_32b, SOURCE_BUFFER_SIZE_32b, 0, DMA_DATA_TYPE_WORD, DMA_DATA_TYPE_WORD, 0); + + for (i = 0; i < SOURCE_BUFFER_SIZE_32b; i++) + { + errors += destin_32b[i] != CONST_VALUE_32B; + } + + dma_fill((uint32_t)source_16b, (uint32_t)&value_16b, SOURCE_BUFFER_SIZE_16b, 0, DMA_DATA_TYPE_HALF_WORD, DMA_DATA_TYPE_HALF_WORD, 0); + dma_copy((uint32_t)destin_16b, (uint32_t)source_16b, SOURCE_BUFFER_SIZE_16b, 0, DMA_DATA_TYPE_HALF_WORD, DMA_DATA_TYPE_HALF_WORD, 0); + + for (i = 0; i < SOURCE_BUFFER_SIZE_16b; i++) + { + errors += destin_16b[i] != CONST_VALUE_16B; + } + + dma_fill((uint32_t)source_8b, (uint32_t)&value_8b, SOURCE_BUFFER_SIZE_8b, 0, DMA_DATA_TYPE_BYTE, DMA_DATA_TYPE_BYTE, 0); + dma_copy((uint32_t)destin_8b, (uint32_t)source_8b, SOURCE_BUFFER_SIZE_8b, 0, DMA_DATA_TYPE_BYTE, DMA_DATA_TYPE_BYTE, 0); + + for (i = 0; i < SOURCE_BUFFER_SIZE_8b; i++) + { + errors += destin_8b[i] != CONST_VALUE_8B; } - PRINTF("Errors:%d\n\r",errors ); + + dma_fill((uint32_t)neg_source_16b, (uint32_t)&neg_value_8b, SOURCE_BUFFER_SIZE_32b, 0, DMA_DATA_TYPE_BYTE, DMA_DATA_TYPE_HALF_WORD, 1); + dma_copy((uint32_t)neg_destin_32b, (uint32_t)neg_source_16b, SOURCE_BUFFER_SIZE_32b, 0, DMA_DATA_TYPE_HALF_WORD, DMA_DATA_TYPE_WORD, 1); + + for (i = 0; i < SOURCE_BUFFER_SIZE_32b; i++) + { + errors += destin_32b[i] != CONST_VALUE_32B; + } + + PRINTF("Errors:%d\n\r", errors); return errors ? EXIT_FAILURE : EXIT_SUCCESS; } \ No newline at end of file diff --git a/sw/applications/example_freertos_blinky/main.c b/sw/applications/example_freertos_blinky/main.c index fbb9941f4..77c0c461e 100644 --- a/sw/applications/example_freertos_blinky/main.c +++ b/sw/applications/example_freertos_blinky/main.c @@ -116,7 +116,7 @@ /* The rate at which data is sent to the queue. The 200ms value is converted to ticks using the pdMS_TO_TICKS() macro. */ -#ifdef TARGET_PYNQ_Z2 +#ifdef TARGET_IS_FPGA #define mainQUEUE_SEND_FREQUENCY_MS pdMS_TO_TICKS( 200 ) #else #define mainQUEUE_SEND_FREQUENCY_MS pdMS_TO_TICKS( 3 ) @@ -148,7 +148,7 @@ or 0 to run the more comprehensive test and demo application. */ #endif /* #if mainCREATE_SIMPLE_BLINKY_DEMO_ONLY == 1 */ -#ifdef TARGET_PYNQ_Z2 +#ifdef TARGET_IS_FPGA #define GPIO_LD5_R 15 #define GPIO_LD5_B 16 #define GPIO_LD5_G 17 diff --git a/sw/applications/example_gpio_intr/main.c b/sw/applications/example_gpio_intr/main.c index 1a9c895d2..bbdb9bc5f 100644 --- a/sw/applications/example_gpio_intr/main.c +++ b/sw/applications/example_gpio_intr/main.c @@ -41,7 +41,7 @@ #endif -#ifdef TARGET_PYNQ_Z2 +#ifdef TARGET_IS_FPGA #define GPIO_TB_OUT 8 #define GPIO_TB_IN 9 #define GPIO_INTR GPIO_INTR_9 diff --git a/sw/applications/example_i2s/main.c b/sw/applications/example_i2s/main.c index 04111da13..2c7d9a163 100644 --- a/sw/applications/example_i2s/main.c +++ b/sw/applications/example_i2s/main.c @@ -34,7 +34,7 @@ #include "i2s_structs.h" -#ifdef TARGET_PYNQ_Z2 +#ifdef TARGET_IS_FPGA #define I2S_TEST_BATCH_SIZE 128 #define I2S_TEST_BATCHES 16 #define I2S_CLK_DIV 8 @@ -104,7 +104,7 @@ void handler_irq_i2s(uint32_t id) { } #ifdef USE_DMA -void dma_intr_handler_trans_done(void) +void dma_intr_handler_trans_done(uint8_t channel) { dma_intr_flag = 1; } @@ -173,7 +173,7 @@ void setup() int main(int argc, char *argv[]) { bool success = true; -#ifdef TARGET_PYNQ_Z2 +#ifdef TARGET_IS_FPGA for (uint32_t i = 0; i < 0x10000; i++) asm volatile("nop"); #endif PRINTF("I2S DEMO\r\n\r"); @@ -182,7 +182,7 @@ int main(int argc, char *argv[]) { //PRINTF("Setup done!\r\n\r"); -#ifdef TARGET_PYNQ_Z2 +#ifdef TARGET_IS_FPGA for (uint32_t i = 0; i < I2S_WAIT_CYCLES; i++) asm volatile("nop"); diff --git a/sw/applications/example_iffifo/main.c b/sw/applications/example_iffifo/main.c index fc17e51a4..89cc242be 100644 --- a/sw/applications/example_iffifo/main.c +++ b/sw/applications/example_iffifo/main.c @@ -44,16 +44,16 @@ int32_t to_fifo [6] __attribute__ ((aligned (4))) = { 1, 2, 3, 4, 5, 6 }; int32_t from_fifo[4] __attribute__ ((aligned (4))) = { 0, 0, 0, 0 }; int8_t dma_intr_flag = 0; -void dma_intr_handler_trans_done() +void dma_intr_handler_trans_done(uint8_t channel) { dma_intr_flag = 1; } void protected_wait_for_dma_interrupt(void) { - while(!dma_is_ready()) { + while(!dma_is_ready(0)) { CSR_CLEAR_BITS(CSR_REG_MSTATUS, 0x8); - if (!dma_is_ready()) { + if (!dma_is_ready(0)) { wait_for_interrupt(); } CSR_SET_BITS(CSR_REG_MSTATUS, 0x8); diff --git a/sw/applications/example_im2col/im2col_lib.c b/sw/applications/example_im2col/im2col_lib.c index ae4d11604..75d38d747 100644 --- a/sw/applications/example_im2col/im2col_lib.c +++ b/sw/applications/example_im2col/im2col_lib.c @@ -196,11 +196,11 @@ void dma_run(dma_trans_t * trans) res = dma_load_transaction(trans); res = dma_launch(trans); - while( ! dma_is_ready()) { + while( ! dma_is_ready(0)) { // disable_interrupts // this does not prevent waking up the core as this is controlled by the MIP register CSR_CLEAR_BITS(CSR_REG_MSTATUS, 0x8); - if ( dma_is_ready() == 0 ) { + if ( dma_is_ready(0) == 0 ) { wait_for_interrupt(); //from here we wake up even if we did not jump to the ISR } diff --git a/sw/applications/example_power_manager/main.c b/sw/applications/example_power_manager/main.c index 9f083b444..4f025e1e7 100644 --- a/sw/applications/example_power_manager/main.c +++ b/sw/applications/example_power_manager/main.c @@ -41,7 +41,7 @@ static rv_timer_t timer_2_3; static const uint64_t kTickFreqHz = 1000 * 1000; // 1 MHz static power_manager_t power_manager; -#ifndef TARGET_PYNQ_Z2 +#ifndef TARGET_IS_FPGA #define GPIO_TB_OUT 30 #define GPIO_TB_IN 31 #define GPIO_INTR GPIO_INTR_31 @@ -73,7 +73,7 @@ int main(int argc, char *argv[]) uint32_t reset_off, reset_on, switch_off, switch_on, iso_off, iso_on; // Setup pads -#ifndef TARGET_PYNQ_Z2 +#ifndef TARGET_IS_FPGA pad_control_t pad_control; pad_control.base_addr = mmio_region_from_addr((uintptr_t)PAD_CONTROL_START_ADDRESS); pad_control_set_mux(&pad_control, (ptrdiff_t)(PAD_CONTROL_PAD_MUX_I2C_SCL_REG_OFFSET), 1); @@ -196,12 +196,16 @@ int main(int argc, char *argv[]) tgt_src.size_du = TEST_DATA_SIZE; tgt_src.trig = DMA_TRIG_MEMORY; tgt_src.type = DMA_DATA_TYPE_WORD; + tgt_src.env = NULL; + tgt_src.inc_d2_du = 0; tgt_dst.ptr = (uint8_t *)copied_data_4B; tgt_dst.inc_du = 1; tgt_dst.size_du = TEST_DATA_SIZE; tgt_dst.trig = DMA_TRIG_MEMORY; tgt_dst.type = DMA_DATA_TYPE_WORD; + tgt_dst.env = NULL; + tgt_dst.inc_d2_du = 0; trans.src = &tgt_src; trans.dst = &tgt_dst; @@ -212,17 +216,25 @@ int main(int argc, char *argv[]) trans.win_du = 0; trans.sign_ext = 0; trans.end = DMA_TRANS_END_INTR; + trans.dim = DMA_DIM_CONF_1D; + trans.dim_inv = 0; + trans.channel = 0; - trans.flags = 0x0; + trans.pad_top_du = 0; + trans.pad_bottom_du = 0; + trans.pad_left_du = 0; + trans.pad_right_du = 0; + + trans.flags = 0x0; res = dma_validate_transaction(&trans, DMA_ENABLE_REALIGN, DMA_PERFORM_CHECKS_INTEGRITY); res = dma_load_transaction(&trans); res = dma_launch(&trans); - while (!dma_is_ready()) - { + while (!dma_is_ready(0)) + { CSR_CLEAR_BITS(CSR_REG_MSTATUS, 0x8); - if (dma_is_ready() == 0) - { + if (dma_is_ready(0) == 0) + { if (power_gate_core(&power_manager, kDma_pm_e, &power_manager_counters) != kPowerManagerOk_e) { PRINTF("Error: power manager fail.\n\r"); @@ -239,11 +251,11 @@ int main(int argc, char *argv[]) } } -#ifndef TARGET_PYNQ_Z2 +#ifndef TARGET_IS_FPGA // Power-gate and wake-up due to plic GPIO gpio_assign_irq_handler( GPIO_INTR_31, &gpio_handler_in ); - + bool state = false; plic_irq_set_priority(GPIO_INTR_31, 1); plic_irq_set_enabled(GPIO_INTR_31, kPlicToggleEnabled); diff --git a/sw/applications/example_sdk_spi_flash/main.c b/sw/applications/example_sdk_spi_flash/main.c index 068b20e65..226a2a02b 100644 --- a/sw/applications/example_sdk_spi_flash/main.c +++ b/sw/applications/example_sdk_spi_flash/main.c @@ -30,13 +30,13 @@ #if TARGET_SIM && PRINTF_IN_SIM #define PRINTF(fmt, ...) printf(fmt, ## __VA_ARGS__) -#elif TARGET_PYNQ_Z2 && PRINTF_IN_FPGA +#elif TARGET_IS_FPGA && PRINTF_IN_FPGA #define PRINTF(fmt, ...) printf(fmt, ## __VA_ARGS__) #else #define PRINTF(...) #endif -#ifdef TARGET_PYNQ_Z2 +#ifdef TARGET_IS_FPGA #define USE_SPI_FLASH #endif diff --git a/sw/applications/example_spi_host_dma_power_gate/main.c b/sw/applications/example_spi_host_dma_power_gate/main.c deleted file mode 100644 index c164740b0..000000000 --- a/sw/applications/example_spi_host_dma_power_gate/main.c +++ /dev/null @@ -1,341 +0,0 @@ -// Copyright EPFL contributors. -// Licensed under the Apache License, Version 2.0, see LICENSE for details. -// SPDX-License-Identifier: Apache-2.0 - -#include -#include -#include - -#include "core_v_mini_mcu.h" -#include "csr.h" -#include "hart.h" -#include "handler.h" -#include "soc_ctrl.h" -#include "spi_host.h" -#include "dma.h" -#include "fast_intr_ctrl.h" -#include "power_manager.h" -#include "x-heep.h" - -#ifdef TARGET_PYNQ_Z2 - #define USE_SPI_FLASH -#endif - -/* By default, printfs are activated for FPGA and disabled for simulation. */ -#define PRINTF_IN_FPGA 1 -#define PRINTF_IN_SIM 0 - -#if TARGET_SIM && PRINTF_IN_SIM - #define PRINTF(fmt, ...) printf(fmt, ## __VA_ARGS__) -#elif PRINTF_IN_FPGA && !TARGET_SIM - #define PRINTF(fmt, ...) printf(fmt, ## __VA_ARGS__) -#else - #define PRINTF(...) -#endif - -// Type of data frome the SPI. For types different than words the SPI data is requested in separate transactions -// word(0), half-word(1), byte(2,3) -#define SPI_DATA_TYPE DMA_DATA_TYPE_WORD - -// Number of elements to copy -#define COPY_DATA_NUM 16 - -#define FLASH_CLK_MAX_HZ (133*1000*1000) // In Hz (133 MHz for the flash w25q128jvsim used in the EPFL Programmer) - -#define REVERT_24b_ADDR(addr) ((((uint32_t)(addr) & 0xff0000) >> 16) | ((uint32_t)(addr) & 0xff00) | (((uint32_t)(addr) & 0xff) << 16)) - -volatile int8_t dma_intr_flag; -int8_t core_sleep_flag; -spi_host_t* spi_peri; - -static power_manager_t power_manager; - -void dma_intr_handler_trans_done(void) -{ - PRINTF("Non-weak implementation of a DMA interrupt\n\r"); - dma_intr_flag = 1; -} - -// Reserve memory array -uint32_t flash_data[COPY_DATA_NUM] __attribute__ ((aligned (4))) = {0x76543210,0xfedcba98,0x579a6f90,0x657d5bee,0x758ee41f,0x01234567,0xfedbca98,0x89abcdef,0x679852fe,0xff8252bb,0x763b4521,0x6875adaa,0x09ac65bb,0x666ba334,0x44556677,0x0000ba98}; -uint32_t copy_data[COPY_DATA_NUM] __attribute__ ((aligned (4))) = { 0 }; - -#if SPI_DATA_TYPE == DMA_DATA_TYPE_DATA_TYPE_VALUE_DMA_32BIT_WORD - #define DATA_TYPE uint32_t -#elif SPI_DATA_TYPE == DMA_DATA_TYPE_DATA_TYPE_VALUE_DMA_16BIT_WORD - #define DATA_TYPE uint16_t -#else - #define DATA_TYPE uint8_t -#endif - -#define COPY_DATA_TYPE (COPY_DATA_NUM/(sizeof(uint32_t)/sizeof(DATA_TYPE))) - -int main(int argc, char *argv[]) -{ - - soc_ctrl_t soc_ctrl; - soc_ctrl.base_addr = mmio_region_from_addr((uintptr_t)SOC_CTRL_START_ADDRESS); - uint32_t read_byte_cmd; - uint32_t* flash_data_lma = flash_data; - //set MS 8 bits to 0 as the flash only uses 24b - flash_data_lma = (uint32_t*) ((uint32_t)(flash_data_lma) & 0x00FFFFFF); - - - if ( get_spi_flash_mode(&soc_ctrl) == SOC_CTRL_SPI_FLASH_MODE_SPIMEMIO ) - { -#ifdef USE_SPI_FLASH - PRINTF("This application cannot work with the memory mapped SPI FLASH module - do not use the FLASH_EXEC linker script for this application\n"); - return EXIT_SUCCESS; -#else - /* - if we are using in SIMULATION the SPIMMIO from Yosys, then the flash_original data is different - as the compilation is done differently, so we will store there the first WORDs of code mapped at the beginning of the FLASH - */ - uint32_t* ptr_flash = (uint32_t*)FLASH_MEM_START_ADDRESS; - for(int i =0; i < COPY_DATA_NUM ; i++){ - flash_data[i] = ptr_flash[i]; - } -#endif - } - - #ifndef USE_SPI_FLASH - spi_peri = spi_host1; - #else - spi_peri = spi_flash; - #endif - - // Setup power_manager - mmio_region_t power_manager_reg = mmio_region_from_addr(POWER_MANAGER_START_ADDRESS); - power_manager.base_addr = power_manager_reg; - power_manager_counters_t power_manager_cpu_counters; - // Init cpu_subsystem's counters - if (power_gate_counters_init(&power_manager_cpu_counters, 300, 300, 300, 300, 300, 300, 0, 0) != kPowerManagerOk_e) - { - PRINTF("Error: power manager fail. Check the reset and powergate counters value\n\r"); - return EXIT_FAILURE; - } - - uint32_t core_clk = soc_ctrl_get_frequency(&soc_ctrl); - - // Enable interrupt on processor side - // Enable global interrupt for machine-level interrupts - CSR_SET_BITS(CSR_REG_MSTATUS, 0x8); - - // Set mie.MEIE bit to one to enable machine-level fast dma interrupt - const uint32_t mask = 1 << 19; - CSR_SET_BITS(CSR_REG_MIE, mask); - - #ifdef USE_SPI_FLASH - // Select SPI host as SPI output - soc_ctrl_select_spi_host(&soc_ctrl); - #endif - - // Enable SPI host device - spi_set_enable(spi_peri, true); - // Enable SPI output - spi_output_enable(spi_peri, true); - - // SPI and SPI_FLASH are the same IP so same register map - uint32_t *fifo_ptr_rx = (uintptr_t)spi_peri + SPI_HOST_RXDATA_REG_OFFSET; - - core_sleep_flag = 0; - - // -- DMA CONFIGURATION -- - - dma_init(NULL); - - #ifndef USE_SPI_FLASH - uint8_t slot = DMA_TRIG_SLOT_SPI_RX ; // The DMA will wait for the SPI RX FIFO valid signal - #else - uint8_t slot = DMA_TRIG_SLOT_SPI_FLASH_RX ; // The DMA will wait for the SPI FLASH RX FIFO valid signal - #endif - - static dma_target_t tgt_src = { - .size_du = COPY_DATA_NUM, - .inc_du = 0, - .type = SPI_DATA_TYPE, - }; - tgt_src.ptr = fifo_ptr_rx; - tgt_src.trig = slot; - - static dma_target_t tgt_dst = { - .ptr = copy_data, - .inc_du = 1, - .type = SPI_DATA_TYPE, - .trig = DMA_TRIG_MEMORY, - }; - - static dma_trans_t trans = { - .src = &tgt_src, - .dst = &tgt_dst, - .end = DMA_TRANS_END_INTR, - }; - - dma_config_flags_t res; - - res = dma_validate_transaction(&trans ,DMA_ENABLE_REALIGN, DMA_PERFORM_CHECKS_INTEGRITY); - PRINTF("trans: %u \n\r", res ); - res = dma_load_transaction(&trans); - PRINTF(" load: %u \n\r", res ); - - - - // Configure SPI clock - // SPI clk freq = 1/2 core clk freq when clk_div = 0 - // SPI_CLK = CORE_CLK/(2 + 2 * CLK_DIV) <= CLK_MAX => CLK_DIV > (CORE_CLK/CLK_MAX - 2)/2 - uint16_t clk_div = 0; - if(FLASH_CLK_MAX_HZ < core_clk/2){ - clk_div = (core_clk/(FLASH_CLK_MAX_HZ) - 2)/2; // The value is truncated - if (core_clk/(2 + 2 * clk_div) > FLASH_CLK_MAX_HZ) clk_div += 1; // Adjust if the truncation was not 0 - } - // SPI Configuration - // Configure chip 0 (flash memory) - const uint32_t chip_cfg = spi_create_configopts((spi_configopts_t){ - .clkdiv = clk_div, - .csnidle = 0xF, - .csntrail = 0xF, - .csnlead = 0xF, - .fullcyc = false, - .cpha = 0, - .cpol = 0 - }); - spi_set_configopts(spi_peri, 0, chip_cfg); - spi_set_csid(spi_peri, 0); - - // Reset - const uint32_t reset_cmd = 0xFFFFFFFF; - spi_write_word(spi_peri, reset_cmd); - const uint32_t cmd_reset = spi_create_command((spi_command_t){ - .len = 3, - .csaat = false, - .speed = SPI_SPEED_STANDARD, - .direction = SPI_DIR_TX_ONLY - }); - spi_set_command(spi_peri, cmd_reset); - spi_wait_for_ready(spi_peri); - - // Power up flash - const uint32_t powerup_byte_cmd = 0xab; - spi_write_word(spi_peri, powerup_byte_cmd); - const uint32_t cmd_powerup = spi_create_command((spi_command_t){ - .len = 0, - .csaat = false, - .speed = SPI_SPEED_STANDARD, - .direction = SPI_DIR_TX_ONLY - }); - spi_set_command(spi_peri, cmd_powerup); - spi_wait_for_ready(spi_peri); - - // Load command FIFO with read command (1 Byte at single speed) - const uint32_t cmd_read = spi_create_command((spi_command_t){ - .len = 3, - .csaat = true, - .speed = SPI_SPEED_STANDARD, - .direction = SPI_DIR_TX_ONLY - }); - - dma_intr_flag = 0; - dma_launch(&trans); - PRINTF("Launched\n\r"); - - #if SPI_DATA_TYPE == DMA_DATA_TYPE_DATA_TYPE_VALUE_DMA_32BIT_WORD - if(get_spi_flash_mode(&soc_ctrl) != SOC_CTRL_SPI_FLASH_MODE_SPIMEMIO) - read_byte_cmd = ((REVERT_24b_ADDR(flash_data_lma) << 8) | 0x03); // The address bytes sent through the SPI to the Flash are in reverse order - else - // we read the data from the FLASH address 0x0, which corresponds to FLASH_MEM_START_ADDRESS - read_byte_cmd = ((REVERT_24b_ADDR(0x0) << 8) | 0x03); // The address bytes sent through the SPI to the Flash are in reverse order - const uint32_t cmd_read_rx = spi_create_command((spi_command_t){ // Single transaction - .len = COPY_DATA_NUM*sizeof(DATA_TYPE) - 1, // In bytes - 1 - .csaat = false, - .speed = SPI_SPEED_STANDARD, - .direction = SPI_DIR_RX_ONLY - }); - spi_write_word(spi_peri, read_byte_cmd); // Fill TX FIFO with TX data (read command + 3B address) - spi_wait_for_ready(spi_peri); // Wait for readiness to process commands - spi_set_command(spi_peri, cmd_read); // Send read command to the external device through SPI - spi_wait_for_ready(spi_peri); - spi_set_command(spi_peri, cmd_read_rx); // Receive data in RX - spi_wait_for_ready(spi_peri); - #else - const uint32_t cmd_read_rx = spi_create_command((spi_command_t){ // Multiple transactions of the data type - .len = (sizeof(DATA_TYPE) - 1), - .csaat = false, - .speed = SPI_SPEED_STANDARD, - .direction = SPI_DIR_RX_ONLY - }); - DATA_TYPE* flash_ptr = (DATA_TYPE *)flash_data_lma; - for (int i = 0; i +#include +#include + +#include "x-heep.h" +#include "w25q128jw.h" +#include "csr.h" +#include "core_v_mini_mcu.h" +#include "power_manager.h" + +/* By default, PRINTFs are activated for FPGA and disabled for simulation. */ +#define PRINTF_IN_FPGA 1 +#define PRINTF_IN_SIM 0 + +#if TARGET_SIM && PRINTF_IN_SIM + #define PRINTF(fmt, ...) printf(fmt, ## __VA_ARGS__) +#elif PRINTF_IN_FPGA && !TARGET_SIM + #define PRINTF(fmt, ...) printf(fmt, ## __VA_ARGS__) +#else + #define PRINTF(...) +#endif + +#if defined(TARGET_PYNQ_Z2) || defined(TARGET_ZCU104) || defined(TARGET_NEXYS_A7_100T) + #define USE_SPI_FLASH +#endif + +#define FLASH_ONLY_WORDS 32 +#define FLASH_ONLY_BYTES (FLASH_ONLY_WORDS*4) + +uint32_t on_chip_buffer[FLASH_ONLY_WORDS]; + +int32_t __attribute__((section(".xheep_data_flash_only"))) __attribute__ ((aligned (16))) flash_only_buffer[FLASH_ONLY_WORDS] = { + 0xABCDEF00, + 0xABCDEF01, + 0xABCDEF02, + 0xABCDEF03, + 0xABCDEF04, + 0xABCDEF05, + 0xABCDEF06, + 0xABCDEF07, + 0xABCDEF08, + 0xABCDEF09, + 0xABCDEF0A, + 0xABCDEF0B, + 0xABCDEF0C, + 0xABCDEF0D, + 0xABCDEF0E, + 0xABCDEF0F, + 0xABCDEF10, + 0xABCDEF11, + 0xABCDEF12, + 0xABCDEF13, + 0xABCDEF14, + 0xABCDEF15, + 0xABCDEF16, + 0xABCDEF17, + 0xABCDEF18, + 0xABCDEF19, + 0xABCDEF1A, + 0xABCDEF1B, + 0xABCDEF1C, + 0xABCDEF1D, + 0xABCDEF1E, + 0xABCDEF1F, +}; + +int32_t __attribute__ ((aligned (16))) flash_only_buffer_golden_value[FLASH_ONLY_WORDS] = { + 0xABCDEF00, + 0xABCDEF01, + 0xABCDEF02, + 0xABCDEF03, + 0xABCDEF04, + 0xABCDEF05, + 0xABCDEF06, + 0xABCDEF07, + 0xABCDEF08, + 0xABCDEF09, + 0xABCDEF0A, + 0xABCDEF0B, + 0xABCDEF0C, + 0xABCDEF0D, + 0xABCDEF0E, + 0xABCDEF0F, + 0xABCDEF10, + 0xABCDEF11, + 0xABCDEF12, + 0xABCDEF13, + 0xABCDEF14, + 0xABCDEF15, + 0xABCDEF16, + 0xABCDEF17, + 0xABCDEF18, + 0xABCDEF19, + 0xABCDEF1A, + 0xABCDEF1B, + 0xABCDEF1C, + 0xABCDEF1D, + 0xABCDEF1E, + 0xABCDEF1F, +}; + + +int main(int argc, char *argv[]) +{ + soc_ctrl_t soc_ctrl; + soc_ctrl.base_addr = mmio_region_from_addr((uintptr_t)SOC_CTRL_START_ADDRESS); + + if ( get_spi_flash_mode(&soc_ctrl) == SOC_CTRL_SPI_FLASH_MODE_SPIMEMIO ) { + PRINTF("This application cannot work with the memory mapped SPI FLASH" + "module - do not use the FLASH_EXEC linker script for this application\n"); + return EXIT_SUCCESS; + } + + // Pick the correct spi device based on simulation type + spi_host_t* spi; + #ifndef USE_SPI_FLASH + spi = spi_host1; + #else + spi = spi_flash; + #endif + + // Setup power_manager + power_manager_t power_manager; + mmio_region_t power_manager_reg = mmio_region_from_addr(POWER_MANAGER_START_ADDRESS); + power_manager.base_addr = power_manager_reg; + power_manager_counters_t power_manager_counters; + //counters + uint32_t reset_off, reset_on, switch_off, switch_on, iso_off, iso_on; + + //Turn off: first, isolate the CPU outputs, then I reset it, then I switch it off (reset and switch off order does not really matter) + iso_off = 10; + reset_off = iso_off + 5; + switch_off = reset_off + 5; + //Turn on: first, give back power by switching on, then deassert the reset, the unisolate the CPU outputs + switch_on = 10; + reset_on = switch_on + 20; //give 20 cycles to emulate the turn on time, this number depends on technology and here it is just a random number + iso_on = reset_on + 5; + + if (power_gate_counters_init(&power_manager_counters, reset_off, reset_on, switch_off, switch_on, iso_off, iso_on, 0, 0) != kPowerManagerOk_e) + { + PRINTF("Error: power manager fail. Check the reset and powergate counters value\n\r"); + return EXIT_FAILURE; + } + + // Define status variable + int32_t errors = 0; + + // Init SPI host and SPI<->Flash bridge parameters + if (w25q128jw_init(spi) != FLASH_OK) return EXIT_FAILURE; + + uint32_t *test_buffer_flash = heep_get_flash_address_offset(flash_only_buffer); + // Read from flash memory at the same address + w25q_error_codes_t status = w25q128jw_read_standard_dma_async(test_buffer_flash, on_chip_buffer, FLASH_ONLY_BYTES); + if (status != FLASH_OK) exit(EXIT_FAILURE); + + //wait for the DMA to finish in DEEP SLEEP mode + while (!dma_is_ready(0)) + { + CSR_CLEAR_BITS(CSR_REG_MSTATUS, 0x8); + if (dma_is_ready(0) == 0) + { + PRINTF("Going to sleep...\r\n"); + if (power_gate_core(&power_manager, kDma_pm_e, &power_manager_counters) != kPowerManagerOk_e) + { + PRINTF("Error: power manager fail.\n\r"); + return EXIT_FAILURE; + } + PRINTF("Woken up...\r\n"); + + } + CSR_SET_BITS(CSR_REG_MSTATUS, 0x8); + } + + + PRINTF("Check results...\r\n"); + + // Check if what we read is correct (i.e. on_chip_buffer == flash_only_buffer_golden_value) + for(int i = 0; i < FLASH_ONLY_WORDS; i++) { + if (on_chip_buffer[i] != flash_only_buffer_golden_value[i]) { + errors++; + PRINTF("Error: on_chip_buffer[%d] = 0x%08x, flash_only_buffer_golden_value[%d] = 0x%08x\n", i, on_chip_buffer[i], i, flash_only_buffer_golden_value[i]); + } + } + + if(errors==0) PRINTF("TEST RUN SUCCEFFULLY\r\n"); + + return errors; +} diff --git a/sw/applications/example_timer_sdk/main.c b/sw/applications/example_timer_sdk/main.c new file mode 100644 index 000000000..cb5bb5ae6 --- /dev/null +++ b/sw/applications/example_timer_sdk/main.c @@ -0,0 +1,126 @@ +// Copyright 2024 EPFL +// Solderpad Hardware License, Version 2.1, see LICENSE.md for details. +// SPDX-License-Identifier: Apache-2.0 WITH SHL-2.1 +// +// File: example_timer_sdk.c +// Author: Juan Sapriza, Francesco Poluzzi +// Date: 23/07/2024 +// Description: Example application to test the Timer SDK. Will count the time to execute a few short tasks. + +#include +#include +#include +#include "core_v_mini_mcu.h" +#include "timer_sdk.h" +#include "x-heep.h" +#include "soc_ctrl.h" + +/* By default, printfs are activated for FPGA and disabled for simulation. */ +#define PRINTF_IN_FPGA 1 +#define PRINTF_IN_SIM 0 + +/* Error tolerances for the tests. */ +#define CYCLE_TOLERANCE 2 // cycles tolerance for simple timer reads +#define INTERRUPT_TOLERANCE 70 // cycles tolerance for timer interrupt +#define TIMER_WAIT_TOLERANCE 20 // milliseconds tolerance for timer wait + +#if TARGET_SIM && PRINTF_IN_SIM + #define PRINTF(fmt, ...) printf(fmt, ## __VA_ARGS__) +#elif PRINTF_IN_FPGA && !TARGET_SIM + #define PRINTF(fmt, ...) printf(fmt, ## __VA_ARGS__) +#else + #define PRINTF(...) +#endif + +void __attribute__((aligned(4), interrupt)) handler_irq_timer(void) { + timer_arm_stop(); + timer_irq_clear(); + return; +} + +int main(){ + uint32_t i = 0; + uint32_t timer_cycles; + uint32_t nop_cycles[4]; + + // Get current Frequency + soc_ctrl_t soc_ctrl; + soc_ctrl.base_addr = mmio_region_from_addr((uintptr_t)SOC_CTRL_START_ADDRESS); + uint32_t freq_hz = soc_ctrl_get_frequency(&soc_ctrl); + + timer_cycles_init(); // Init the timer SDK for clock cycles + timer_start(); // Start counting the time + nop_cycles[0] = timer_stop(); // Stop counting the time + PRINTF("0 NOPs:\t%d cc\n\r", nop_cycles[0] ); + + timer_start(); + asm volatile ("nop"); + nop_cycles[1] = timer_stop(); + PRINTF("1 NOP:\t%d cc\n\r", nop_cycles[1] ); + + timer_start(); + asm volatile ("nop"); + asm volatile ("nop"); + nop_cycles[2] = timer_stop(); + PRINTF("2 NOPs:\t%d cc\n\r", nop_cycles[2] ); + + timer_start(); + asm volatile ("nop"); + asm volatile ("nop"); + asm volatile ("nop"); + nop_cycles[3] = timer_stop(); + PRINTF("3 NOPs:\t%d cc\n\r", nop_cycles[3] ); + + if( abs(nop_cycles[1] - nop_cycles[0])>CYCLE_TOLERANCE || abs(nop_cycles[2] - nop_cycles[1])>CYCLE_TOLERANCE || abs(nop_cycles[3] - nop_cycles[2])>CYCLE_TOLERANCE){ + PRINTF("Clock count failed\n\r"); + return EXIT_FAILURE; + } + + enable_timer_interrupt(); // Enable the timer machine-level interrupt + + timer_cycles_init(); + timer_irq_enable(); + timer_arm_start(1000); + asm volatile ("wfi"); // Wait for interrupt + timer_cycles = timer_stop(); + if(abs(timer_cycles-1000) < INTERRUPT_TOLERANCE){ + PRINTF("Timer threshold interrupt working\n" ); + } else { + PRINTF("Timer threshold interrupt failed\n\r"); + return EXIT_FAILURE; + } + + timer_cycles_init(); // Init the timer SDK for microseconds + timer_start(); + for(i = 0; i < 1000; i++){ + asm volatile ("nop"); + } + timer_cycles = timer_stop(); + PRINTF("Microseconds for 1000 NOPs:\t%d μs\n\r", (uint32_t)get_time_from_cycles(timer_cycles) ); + + #ifdef TARGET_IS_FPGA + PRINTF("Wait 5 second\n\r"); + timer_wait_us(5000000); // Wait for 5 seconds + timer_cycles = timer_stop(); + PRINTF("Done\n\r"); + + if(abs(timer_cycles-(5*freq_hz)) > TIMER_WAIT_TOLERANCE){ + PRINTF("Timer wait failed\n\r"); + return EXIT_FAILURE; + } + #endif + #ifdef TARGET_SIM // Reduced time for simulation for faster testing + PRINTF("Wait 0.001 second\n\r"); + timer_wait_us(1000); // Wait for 1 millisecond + timer_cycles = timer_stop(); + PRINTF("Done\n\r"); + + if(abs(timer_cycles-(0.001*freq_hz)) > TIMER_WAIT_TOLERANCE){ + PRINTF("Timer wait failed\n\r"); + return EXIT_FAILURE; + } + #endif + + PRINTF("All tests passed\n\r"); + return EXIT_SUCCESS; +} diff --git a/sw/applications/hello_world/main.c b/sw/applications/hello_world/main.c index e61339734..bbe2f0717 100644 --- a/sw/applications/hello_world/main.c +++ b/sw/applications/hello_world/main.c @@ -25,4 +25,3 @@ int main(int argc, char *argv[]) printf("hello world!\n"); return EXIT_SUCCESS; } - diff --git a/sw/device/bsp/w25q/w25q.c b/sw/device/bsp/w25q/w25q.c index 026d74d74..04908d8ea 100644 --- a/sw/device/bsp/w25q/w25q.c +++ b/sw/device/bsp/w25q/w25q.c @@ -24,6 +24,10 @@ * @brief Source file of the W25Q-family flash memory driver. */ +#ifdef __cplusplus +extern "C" { +#endif // __cplusplus + /****************************************************************************/ /** **/ /* MODULES USED */ @@ -289,7 +293,7 @@ w25q_error_codes_t w25q128jw_read(uint32_t addr, void *data, uint32_t length) { if (status != FLASH_OK) return status; } else { // Wait DMA to be free - while(!dma_is_ready()); + while(!dma_is_ready(0)); status = w25q128jw_read_quad_dma(addr, data, length); if (status != FLASH_OK) return status; } @@ -308,7 +312,7 @@ w25q_error_codes_t w25q128jw_write(uint32_t addr, void *data, uint32_t length, u status = erase_and_write(addr, data, length); } else { // Wait DMA to be free - while(!dma_is_ready()); + while(!dma_is_ready(0)); status = w25q128jw_write_quad_dma(addr, data, length); } @@ -437,19 +441,19 @@ w25q_error_codes_t w25q128jw_erase_and_write_standard(uint32_t addr, void* data, } +w25q_error_codes_t w25q128jw_read_standard_dma(uint32_t addr, void *data, uint32_t length, uint8_t no_wait_dma, uint8_t no_sanity_checks) { -w25q_error_codes_t w25q128jw_read_standard_dma(uint32_t addr, void *data, uint32_t length) { // Sanity checks - if (w25q128jw_sanity_checks(addr, data, length) != FLASH_OK) return FLASH_ERROR; + if (!no_sanity_checks) if (w25q128jw_sanity_checks(addr, data, length) != FLASH_OK) return FLASH_ERROR; /* * SET UP DMA */ // SPI and SPI_FLASH are the same IP so same register map - uint32_t *fifo_ptr_rx = (uintptr_t)spi + SPI_HOST_RXDATA_REG_OFFSET; + uint32_t *fifo_ptr_rx = (uint32_t *)((uintptr_t)spi + SPI_HOST_RXDATA_REG_OFFSET); // Init DMA, the integrated DMA is used (peri == NULL) - dma_init(NULL); + if(!no_wait_dma) dma_init(NULL); // The DMA will wait for the SPI HOST/FLASH RX FIFO valid signal #ifndef USE_SPI_FLASH @@ -486,6 +490,7 @@ w25q_error_codes_t w25q128jw_read_standard_dma(uint32_t addr, void *data, uint32 }; // Validate, load and launch DMA transaction + dma_config_flags_t res; res = dma_validate_transaction(&trans, DMA_ENABLE_REALIGN, DMA_PERFORM_CHECKS_INTEGRITY ); res = dma_load_transaction(&trans); @@ -519,17 +524,114 @@ w25q_error_codes_t w25q128jw_read_standard_dma(uint32_t addr, void *data, uint32 spi_wait_for_ready(spi); // Wait for DMA to finish transaction - while(!dma_is_ready()); + if(!no_wait_dma) while(!dma_is_ready(0)); // Take into account the extra bytes (if any) if (length % 4 != 0) { uint32_t last_word = 0; - spi_read_word(spi, &last_word); + + spi_read_word((spi_host_t *)spi, &last_word); + #ifdef __cplusplus + memcpy(static_cast(data) + length - (length % 4), &last_word, length % 4); + #else memcpy(&data[length - length%4], &last_word, length%4); + #endif } return FLASH_OK; } +w25q_error_codes_t w25q128jw_read_standard_dma_async(uint32_t addr, void *data, uint32_t length) { + + // Sanity checks + if (w25q128jw_sanity_checks(addr, data, length) != FLASH_OK) return FLASH_ERROR; + + // Take into account the extra bytes (if any) + if (length % 4 != 0) { + //only multiple of 4 bytes are supported in this function + return FLASH_ERROR; + } + + /* + * SET UP DMA + */ + // SPI and SPI_FLASH are the same IP so same register map + uint32_t *fifo_ptr_rx = (uint32_t *)((uintptr_t)spi + SPI_HOST_RXDATA_REG_OFFSET); + + // Init DMA, the integrated DMA is used (peri == NULL) + dma_init(NULL); + + // The DMA will wait for the SPI HOST/FLASH RX FIFO valid signal + #ifndef USE_SPI_FLASH + uint8_t slot = DMA_TRIG_SLOT_SPI_RX; + #else + uint8_t slot = DMA_TRIG_SLOT_SPI_FLASH_RX; + #endif + + // Set up DMA source target + static dma_target_t tgt_src = { + .inc_du = 0, // Target is peripheral, no increment + .type = DMA_DATA_TYPE_WORD, // Data type is word + }; + // Size is in data units (words in this case) + tgt_src.size_du = length>>2; + // Target is SPI RX FIFO + tgt_src.ptr = (uint8_t*)fifo_ptr_rx; + // Trigger to control the data flow + tgt_src.trig = slot; + + // Set up DMA destination target + static dma_target_t tgt_dst = { + .inc_du = 1, // Increment by 1 data unit (word) + .type = DMA_DATA_TYPE_WORD, // Data type is byte + .trig = DMA_TRIG_MEMORY, // Read-write operation to memory + }; + tgt_dst.ptr = (uint8_t*)data; // Target is the data buffer + + // Set up DMA transaction + static dma_trans_t trans = { + .src = &tgt_src, + .dst = &tgt_dst, + .end = DMA_TRANS_END_INTR, //so that you can wait for interrupt + }; + // Validate, load and launch DMA transaction + dma_config_flags_t res; + res = dma_validate_transaction(&trans, DMA_ENABLE_REALIGN, DMA_PERFORM_CHECKS_INTEGRITY ); + res = dma_load_transaction(&trans); + res = dma_launch(&trans); + + // Address + Read command + uint32_t read_byte_cmd = ((REVERT_24b_ADDR(addr & 0x00ffffff) << 8) | FC_RD); + // Load command to TX FIFO + spi_write_word(spi, read_byte_cmd); + spi_wait_for_ready(spi); + + // Set up segment parameters -> send command and address + const uint32_t cmd_read_1 = spi_create_command((spi_command_t){ + .len = 3, // 4 Bytes + .csaat = true, // Command not finished + .speed = SPI_SPEED_STANDARD, // Single speed + .direction = SPI_DIR_TX_ONLY // Write only + }); + // Load segment parameters to COMMAND register + spi_set_command(spi, cmd_read_1); + spi_wait_for_ready(spi); + + // Set up segment parameters -> read length bytes + const uint32_t cmd_read_2 = spi_create_command((spi_command_t){ + .len = length-1, // len bytes + .csaat = false, // End command + .speed = SPI_SPEED_STANDARD, // Single speed + .direction = SPI_DIR_RX_ONLY // Read only + }); + spi_set_command(spi, cmd_read_2); + spi_wait_for_ready(spi); + + // Wait for DMA to finish transaction outside this function, the DMA generates also an interrupt + // However, you need to enable the interrupt in the INT controllers, and CPU + + return FLASH_OK; +} + w25q_error_codes_t w25q128jw_write_standard_dma(uint32_t addr, void *data, uint32_t length) { // Call the wrapper with quad = 0, dma = 1 @@ -549,7 +651,7 @@ w25q_error_codes_t w25q128jw_erase_and_write_standard_dma(uint32_t addr, void* d uint32_t sector_start_addr = current_addr & 0xfffff000; // Read the full sector and save it into RAM - status = w25q128jw_read_standard_dma(sector_start_addr, sector_data, FLASH_SECTOR_SIZE); + status = w25q128jw_read_standard_dma(sector_start_addr, sector_data, FLASH_SECTOR_SIZE, 0, 0); if (status != FLASH_OK) return FLASH_ERROR; // Erase the sector (no need to do so in simulation) @@ -787,7 +889,7 @@ w25q_error_codes_t w25q128jw_read_quad_dma(uint32_t addr, void *data, uint32_t l * SET UP DMA */ // SPI and SPI_FLASH are the same IP so same register map - uint32_t *fifo_ptr_rx = (uintptr_t)spi + SPI_HOST_RXDATA_REG_OFFSET; + uint32_t *fifo_ptr_rx = (uint32_t *)((uintptr_t)spi + SPI_HOST_RXDATA_REG_OFFSET); // Init DMA, the integrated DMA is used (peri == NULL) dma_init(NULL); @@ -833,13 +935,20 @@ w25q_error_codes_t w25q128jw_read_quad_dma(uint32_t addr, void *data, uint32_t l res = dma_launch(&trans); // Wait for DMA to finish transaction - while(!dma_is_ready()); + while(!dma_is_ready(0)); // Take into account the extra bytes (if any) if (length % 4 != 0) { uint32_t last_word = 0; - spi_read_word(spi, &last_word); + + spi_read_word((spi_host_t *)spi, &last_word); + + #ifdef __cplusplus + memcpy(static_cast(data) + length - (length % 4), &last_word, length % 4); + #else memcpy(&data[length - length%4], &last_word, length%4); + #endif + } return FLASH_OK; @@ -847,14 +956,14 @@ w25q_error_codes_t w25q128jw_read_quad_dma(uint32_t addr, void *data, uint32_t l w25q_error_codes_t w25q128jw_write_quad_dma(uint32_t addr, void *data, uint32_t length) { // Call the wrapper with quad = 1, dma = 1 - return page_write_wrapper(addr, data, length, 1, 1); + return page_write_wrapper(addr, (uint8_t *)data, length, 1, 1); } w25q_error_codes_t w25q128jw_erase_and_write_quad_dma(uint32_t addr, void *data, uint32_t length) { uint32_t remaining_length = length; uint32_t current_addr = addr; - uint8_t *current_data = data; + uint8_t *current_data = (uint8_t *)data; w25q_error_codes_t status; @@ -1353,7 +1462,7 @@ static w25q_error_codes_t page_write(uint32_t addr, uint8_t *data, uint32_t leng static w25q_error_codes_t dma_send_toflash(uint8_t *data, uint32_t length) { // SPI and SPI_FLASH are the same IP so same register map - uint32_t *fifo_ptr_tx = (uintptr_t)spi + SPI_HOST_TXDATA_REG_OFFSET; + uint32_t *fifo_ptr_tx = (uint32_t *)((uintptr_t)spi + SPI_HOST_TXDATA_REG_OFFSET); // Init DMA, the integrated DMA is used (peri == NULL) dma_init(NULL); @@ -1404,7 +1513,7 @@ static w25q_error_codes_t dma_send_toflash(uint8_t *data, uint32_t length) { if (res != DMA_CONFIG_OK) return FLASH_ERROR_DMA; // Wait for DMA to finish transaction - while(!dma_is_ready()); + while(!dma_is_ready(0)); // Take into account the extra bytes (if any) if (length % 4 != 0) { @@ -1443,7 +1552,9 @@ static w25q_error_codes_t w25q128jw_sanity_checks(uint32_t addr, uint8_t *data, return FLASH_OK; // Success } - +#ifdef __cplusplus +} // extern "C" +#endif // __cplusplus /****************************************************************************/ /** **/ /* EOF */ diff --git a/sw/device/bsp/w25q/w25q128jw.h b/sw/device/bsp/w25q/w25q128jw.h index 16f1d2a07..1bc8b5e4d 100644 --- a/sw/device/bsp/w25q/w25q128jw.h +++ b/sw/device/bsp/w25q/w25q128jw.h @@ -49,6 +49,7 @@ */ #define FLASH_CLK_MAX_HZ (133*1000*1000) + /** * @defgroup flash_commands Flash commands * @{ @@ -266,8 +267,17 @@ w25q_error_codes_t w25q128jw_erase_and_write_standard(uint32_t addr, void* data, * @param length number of bytes to read. * @return FLASH_OK if the read is successful, @ref error_codes otherwise. */ -w25q_error_codes_t w25q128jw_read_standard_dma(uint32_t addr, void* data, uint32_t length); +w25q_error_codes_t w25q128jw_read_standard_dma(uint32_t addr, void *data, uint32_t length, uint8_t no_wait_dma, uint8_t no_sanity_checks); +/** + * @brief Read from flash at standard speed using DMA but wait for DMA in the application + * + * @param addr 24-bit flash address to read from. + * @param data pointer to the data buffer. + * @param length number of bytes to read, must be multiple of 4 + * @return FLASH_OK if the read is successful, @ref error_codes otherwise. +*/ +w25q_error_codes_t w25q128jw_read_standard_dma_async(uint32_t addr, void *data, uint32_t length); /** * @brief Write to flash at standard speed using DMA. Use this function only to write to unitialized data diff --git a/sw/device/lib/base/freestanding/assert.h b/sw/device/lib/base/freestanding/assert.h index 0b701dc80..2619af76d 100644 --- a/sw/device/lib/base/freestanding/assert.h +++ b/sw/device/lib/base/freestanding/assert.h @@ -21,11 +21,26 @@ // defined for C. #ifndef __cplusplus #define static_assert _Static_assert -#endif // __cplusplus +#endif + // `assert()` should not be used. When building with Clang, using this function // in device code will emit a compile-time error. + + +#ifndef __cplusplus #define assert(do_not_use) \ static_assert(false, "do not use assert(); use CHECK() instead") +#else + #include + #define assert(expression) \ + do { \ + if (!(expression)) { \ + printf("Assertion failed: %s, file %s, line %d\n", \ + #expression, __FILE__, __LINE__); \ + while(1); \ + } \ + } while (0) +#endif // __cplusplus*/ #endif // OPENTITAN_SW_DEVICE_LIB_BASE_FREESTANDING_ASSERT_H_ diff --git a/sw/device/lib/base/freestanding/stdalign.h b/sw/device/lib/base/freestanding/stdalign.h index 10126c9e3..d0cc3f2a1 100644 --- a/sw/device/lib/base/freestanding/stdalign.h +++ b/sw/device/lib/base/freestanding/stdalign.h @@ -13,10 +13,16 @@ * S4p6. This header is specified in detail in S7.15 of the same. */ + + +#ifndef __cplusplus #define alignas _Alignas #define __alignas_is_defined 1 #define alignof _Alignof #define __alignof_is_defined 1 +#endif // __cplusplus + + #endif // OPENTITAN_SW_DEVICE_LIB_BASE_FREESTANDING_STDALIGN_H_ diff --git a/sw/device/lib/base/freestanding/stdbool.h b/sw/device/lib/base/freestanding/stdbool.h index f9a9fa0bb..2b04a3af8 100644 --- a/sw/device/lib/base/freestanding/stdbool.h +++ b/sw/device/lib/base/freestanding/stdbool.h @@ -12,11 +12,15 @@ * This header implements the stdbool.h standard header, as required by C11 * S4p6. This header is specified in detail in S7.18 of the same. */ +#ifndef __cplusplus +#define _Bool char #define bool _Bool #define true 1 #define false 0 #define __bool_true_false_are_defined 1 +#endif + #endif // OPENTITAN_SW_DEVICE_LIB_BASE_FREESTANDING_STDBOOL_H_ diff --git a/sw/device/lib/base/memory.c b/sw/device/lib/base/memory.c index 180cdd901..e0234bf1b 100644 --- a/sw/device/lib/base/memory.c +++ b/sw/device/lib/base/memory.c @@ -2,6 +2,10 @@ // Licensed under the Apache License, Version 2.0, see LICENSE for details. // SPDX-License-Identifier: Apache-2.0 +#ifdef __cplusplus +extern "C" { +#endif // __cplusplus + #include "memory.h" extern uint32_t read_32(const void *); @@ -18,7 +22,7 @@ extern void write_32(uint32_t, void *); // built for host-side software. #if !defined(HOST_BUILD) -void *memcpy(void *restrict dest, const void *restrict src, size_t len) { +void *memcpy(void *__restrict dest, const void *__restrict src, size_t len) { uint8_t *dest8 = (uint8_t *)dest; uint8_t *src8 = (uint8_t *)src; for (size_t i = 0; i < len; ++i) { @@ -84,3 +88,9 @@ void *memrchr(const void *ptr, int value, size_t len) { } return NULL; } + + + +#ifdef __cplusplus +} +#endif // __cplusplus diff --git a/sw/device/lib/base/memory.h b/sw/device/lib/base/memory.h index 4a9f213be..3ff64ff76 100644 --- a/sw/device/lib/base/memory.h +++ b/sw/device/lib/base/memory.h @@ -107,7 +107,7 @@ inline void write_32(uint32_t value, void *ptr) { * @param len the number of bytes to copy. * @return the value of `dest`. */ -void *memcpy(void *restrict dest, const void *restrict src, size_t len); +void *memcpy(void *__restrict dest, const void *__restrict src, size_t len); /** * Set a region of memory to a particular byte value. diff --git a/sw/device/lib/base/mmio.c b/sw/device/lib/base/mmio.c index 583091702..dff7f78aa 100644 --- a/sw/device/lib/base/mmio.c +++ b/sw/device/lib/base/mmio.c @@ -2,6 +2,11 @@ // Licensed under the Apache License, Version 2.0, see LICENSE for details. // SPDX-License-Identifier: Apache-2.0 +#ifdef __cplusplus +extern "C" { +#endif // __cplusplus + + #include "mmio.h" #include @@ -158,3 +163,8 @@ extern void mmio_region_nonatomic_set_bit32(mmio_region_t base, extern void mmio_region_write_only_set_bit32(mmio_region_t base, ptrdiff_t offset, uint32_t bit_index); + + +#ifdef __cplusplus +} +#endif // __cplusplus diff --git a/sw/device/lib/crt/vectors.S b/sw/device/lib/crt/vectors.S index f6a805c65..8e573eefd 100644 --- a/sw/device/lib/crt/vectors.S +++ b/sw/device/lib/crt/vectors.S @@ -126,39 +126,13 @@ new vector table (which is at mtvec) */ .section .text.vecs /* exception handling */ +.globl __no_irq_handler __no_irq_handler: la a0, no_exception_handler_msg jal ra, puts j __no_irq_handler - -sw_irq_handler: - csrr t0, mcause - slli t0, t0, 1 /* shift off the high bit */ - srli t0, t0, 1 - li t1, 2 - beq t0, t1, handle_illegal_insn - li t1, 11 - beq t0, t1, handle_ecall - li t1, 3 - beq t0, t1, handle_ebreak - j handle_unknown - -handle_ecall: - la a0, ecall_msg - jal ra, puts - j end_handler - -handle_ebreak: - la a0, ebreak_msg - jal ra, puts - j end_handler - -handle_illegal_insn: - la a0, illegal_insn_msg - jal ra, puts - j end_handler - +.globl handle_unknown handle_unknown: la a0, unknown_msg jal ra, puts diff --git a/sw/device/lib/crt/vectors_freertos.S b/sw/device/lib/crt/vectors_freertos.S index 3596dc186..c4586b300 100644 --- a/sw/device/lib/crt/vectors_freertos.S +++ b/sw/device/lib/crt/vectors_freertos.S @@ -132,33 +132,6 @@ __no_irq_handler: jal ra, puts j __no_irq_handler -sw_irq_handler: - csrr t0, mcause - slli t0, t0, 1 /* shift off the high bit */ - srli t0, t0, 1 - li t1, 2 - beq t0, t1, handle_illegal_insn - li t1, 11 - beq t0, t1, handle_ecall - li t1, 3 - beq t0, t1, handle_ebreak - j handle_unknown - -handle_ecall: - la a0, ecall_msg - jal ra, puts - j end_handler - -handle_ebreak: - la a0, ebreak_msg - jal ra, puts - j end_handler - -handle_illegal_insn: - la a0, illegal_insn_msg - jal ra, puts - j end_handler - handle_unknown: la a0, unknown_msg jal ra, puts diff --git a/sw/device/lib/drivers/dma/dma.c b/sw/device/lib/drivers/dma/dma.c index 6e9a2ef78..520977702 100644 --- a/sw/device/lib/drivers/dma/dma.c +++ b/sw/device/lib/drivers/dma/dma.c @@ -31,6 +31,11 @@ /** **/ /****************************************************************************/ +#ifdef __cplusplus +extern "C" +{ +#endif // __cplusplus + #include "dma.h" /* To manage addresses. */ @@ -57,11 +62,6 @@ */ #define DMA_CSR_REG_MIE_MASK (( 1 << 19 ) | (1 << 11 ) ) // @ToDo Add definitions for this 19 and 11 -/** - * Size of a register of 32 bits. - */ -#define DMA_REGISTER_SIZE_BYTES sizeof(int) - /** * Mask to determine if an address is multiple of 4 (Word aligned). */ @@ -191,37 +191,23 @@ static inline uint8_t is_region_outbound_2D( uint8_t *p_start, uint32_t p_inc_d1_du, uint32_t p_inc_d2_du ); -/** - * @brief Writes a given value into the specified register. Its operation - * mimics that of bitfield_field32_write(), but does not require the use of - * a field structure, that is not always provided in the _regs.h file. - * @param p_val The value to be written. - * @param p_offset The register's offset from the peripheral's base address - * where the target register is located. - * @param p_mask The variable's mask to only modify its bits inside the whole - * register. - * @param p_sel The selection index (i.e. From which bit inside the register - * the value is to be written). - */ -static inline void write_register( uint32_t p_val, - uint32_t p_offset, - uint32_t p_mask, - uint8_t p_sel ); - - /** * @brief Analyzes a target to determine the size of its D1 increment (in bytes). * @param p_tgt A pointer to the target to analyze. + * @param channel The channel to use as target. * @return The number of bytes of the increment. */ -static inline uint32_t get_increment_b_1D( dma_target_t * p_tgt ); +static inline uint32_t get_increment_b_1D( dma_target_t * p_tgt, + uint8_t channel ); /** * @brief Analyzes a target to determine the size of its D2 increment (in bytes). * @param p_tgt A pointer to the target to analyze. + * @param channel The channel to use as target. * @return The number of bytes of the increment. */ -static inline uint32_t get_increment_b_2D( dma_target_t * p_tgt ); +static inline uint32_t get_increment_b_2D( dma_target_t * p_tgt, + uint8_t channel ); /****************************************************************************/ @@ -237,10 +223,10 @@ static inline uint32_t get_increment_b_2D( dma_target_t * p_tgt ); /****************************************************************************/ /** - * Control Block (CB) of the DMA peripheral. + * Control Block (CB) of a single DMA channel. * Has variables and constant necessary/useful for its control. */ -static struct +typedef struct { /** * Pointer to the transaction to be performed. @@ -259,7 +245,14 @@ static struct */ dma *peri; -}dma_cb; +}dma_ch_cb; + +/* Allocate the channel's memory space */ +static dma_ch_cb dma_subsys_per[DMA_CH_NUM]; + +/* High priority interrupts counters */ +uint16_t dma_hp_tr_intr_counter = 0; +uint16_t dma_hp_win_intr_counter = 0; /****************************************************************************/ @@ -270,58 +263,130 @@ static struct void handler_irq_dma(uint32_t id) { - /* - * Call the weak implementation provided in this module, + /* + * Find out which channel raised the interrupt and call + * either the weak implementation provided in this module, * or the non-weak implementation. */ - dma_intr_handler_window_done(); + + for (int i = 0; i < DMA_CH_NUM; i++) + { + if (dma_subsys_per[i].peri->WINDOW_IFR == 1) + { + dma_intr_handler_window_done(i); + + #ifdef DMA_HP_INTR_INDEX + /* + * If the channel that raised the interrupt is among the high priority channels, + * return to break the loop. + */ + #ifdef DMA_NUM_HP_INTR + if (i <= DMA_HP_INTR_INDEX && dma_hp_win_intr_counter < DMA_NUM_HP_INTR) + { + dma_hp_win_intr_counter++; + return; + } else if (i > DMA_HP_INTR_INDEX) + { + dma_hp_win_intr_counter = 0; + } + + #else + + if (i <= DMA_HP_INTR_INDEX) + { + return; + } + #endif + + #endif + } + } + return; } void fic_irq_dma(void) { - /* The flag is raised so the waiting loop can be broken.*/ - dma_cb.intrFlag = 1; - - /* - * Call the weak implementation provided in this module, + /* + * Find out which channel raised the interrupt and call + * either the weak implementation provided in this module, * or the non-weak implementation. */ - dma_intr_handler_trans_done(); + + for (int i = 0; i < DMA_CH_NUM; i++) + { + if (dma_subsys_per[i].peri->TRANSACTION_IFR == 1) + { + dma_subsys_per[i].intrFlag = 1; + dma_intr_handler_trans_done(i); + + #ifdef DMA_HP_INTR_INDEX + /* + * If the channel that raised the interrupt is among the high priority channels, + * return to break the loop. + */ + #ifdef DMA_NUM_HP_INTR + if (i <= DMA_HP_INTR_INDEX && dma_hp_tr_intr_counter < DMA_NUM_HP_INTR) + { + dma_hp_tr_intr_counter++; + return; + } + else if (i > DMA_HP_INTR_INDEX) + { + dma_hp_tr_intr_counter = 0; + } + + #else + + if (i <= DMA_HP_INTR_INDEX) + { + return; + } + #endif + + #endif + } + } + return; } -void dma_init( dma *peri ) +void dma_init( dma *dma_peri ) { /* * If a DMA peripheral was provided, use that one, otherwise use the * integrated one. */ - dma_cb.peri = peri ? peri : dma_peri; - - /* Clear the loaded transaction */ - dma_cb.trans = NULL; - /* Clear all values in the DMA registers. */ - dma_cb.peri->SRC_PTR = 0; - dma_cb.peri->DST_PTR = 0; - dma_cb.peri->ADDR_PTR = 0; - dma_cb.peri->SIZE_D1 = 0; - dma_cb.peri->SIZE_D2 = 0; - dma_cb.peri->SRC_PTR_INC_D1 = 0; - dma_cb.peri->SRC_PTR_INC_D2 = 0; - dma_cb.peri->DST_PTR_INC_D1 = 0; - dma_cb.peri->DST_PTR_INC_D2 = 0; - dma_cb.peri->DIM_CONFIG = 0; - dma_cb.peri->SLOT = 0; - dma_cb.peri->SRC_DATA_TYPE = 0; - dma_cb.peri->DST_DATA_TYPE = 0; - dma_cb.peri->SIGN_EXT = 0; - dma_cb.peri->MODE = 0; - dma_cb.peri->WINDOW_SIZE = 0; - dma_cb.peri->INTERRUPT_EN = 0; - dma_cb.peri->PAD_TOP = 0; - dma_cb.peri->PAD_BOTTOM = 0; - dma_cb.peri->PAD_LEFT = 0; - dma_cb.peri->PAD_RIGHT = 0; - dma_cb.peri->DIM_INV = 0; + + for (int i = 0; i < DMA_CH_NUM; i++) + { + dma_subsys_per[i].peri = dma_peri ? dma_peri : dma_peri(i); + + /* Clear the loaded transaction */ + dma_subsys_per[i].trans = NULL; + + /* Clear all values in the DMA registers. */ + dma_subsys_per[i].peri->SRC_PTR = 0; + dma_subsys_per[i].peri->DST_PTR = 0; + dma_subsys_per[i].peri->ADDR_PTR = 0; + dma_subsys_per[i].peri->SIZE_D1 = 0; + dma_subsys_per[i].peri->SIZE_D2 = 0; + dma_subsys_per[i].peri->SRC_PTR_INC_D1 = 0; + dma_subsys_per[i].peri->SRC_PTR_INC_D2 = 0; + dma_subsys_per[i].peri->DST_PTR_INC_D1 = 0; + dma_subsys_per[i].peri->DST_PTR_INC_D2 = 0; + dma_subsys_per[i].peri->DIM_CONFIG = 0; + dma_subsys_per[i].peri->DIM_INV = 0; + dma_subsys_per[i].peri->SLOT = 0; + dma_subsys_per[i].peri->SRC_DATA_TYPE = 0; + dma_subsys_per[i].peri->DST_DATA_TYPE = 0; + dma_subsys_per[i].peri->SIGN_EXT = 0; + dma_subsys_per[i].peri->MODE = 0; + dma_subsys_per[i].peri->WINDOW_SIZE = 0; + dma_subsys_per[i].peri->PAD_TOP = 0; + dma_subsys_per[i].peri->PAD_BOTTOM = 0; + dma_subsys_per[i].peri->PAD_LEFT = 0; + dma_subsys_per[i].peri->PAD_RIGHT = 0; + dma_subsys_per[i].peri->INTERRUPT_EN = 0; + } } dma_config_flags_t dma_validate_transaction( dma_trans_t *p_trans, @@ -475,7 +540,7 @@ dma_config_flags_t dma_validate_transaction( dma_trans_t *p_trans, /* The copy size of the source (in data units -of the source-) is transformed to bytes, to be used as default size.*/ - uint8_t dataSize_b = DMA_DATA_TYPE_2_SIZE(p_trans->dst->type); + uint8_t dataSize_b = DMA_DATA_TYPE_2_SIZE(p_trans->src->type); p_trans->size_b = p_trans->src->size_du * dataSize_b; p_trans->size_d2_b = p_trans->src->size_d2_du * dataSize_b; @@ -596,9 +661,10 @@ dma_config_flags_t dma_validate_transaction( dma_trans_t *p_trans, * If realignment is allowed and there are no discontinuities, * a more granular data type is used according to the detected * misalignment in order to overcome it. - */ - p_trans->dst_type += misalignment; - /* + */ + p_trans->dst_type = p_trans->dst_type + misalignment; + + /* * Source and destination increment should now be of the size * of the data. * As increments are given in bytes, in both cases should be the @@ -714,8 +780,9 @@ dma_config_flags_t dma_validate_transaction( dma_trans_t *p_trans, return p_trans->flags; } -dma_config_flags_t dma_load_transaction( dma_trans_t *p_trans ) +dma_config_flags_t dma_load_transaction( dma_trans_t *p_trans) { + uint8_t channel = p_trans->channel; /* * CHECK FOR CRITICAL ERRORS */ @@ -727,7 +794,7 @@ dma_config_flags_t dma_load_transaction( dma_trans_t *p_trans ) */ if( p_trans->flags & DMA_CONFIG_CRITICAL_ERROR ) { - dma_cb.trans = NULL; + dma_subsys_per[channel].trans = NULL; return DMA_CONFIG_CRITICAL_ERROR; } @@ -742,38 +809,40 @@ dma_config_flags_t dma_load_transaction( dma_trans_t *p_trans ) * until it has ended. * Transactions can still be validated in the meantime. */ - if( !dma_is_ready() ) + if( !dma_is_ready(channel) ) { return DMA_CONFIG_TRANS_OVERRIDE; } /* Save the current transaction */ - dma_cb.trans = p_trans; + dma_subsys_per[channel].trans = p_trans; /* * ENABLE/DISABLE INTERRUPTS */ /* - * If the selected en event is polling, interrupts are disabled. + * If the selected end event is polling, interrupts are disabled. * Otherwise the mie.MEIE bit is set to one to enable machine-level * fast DMA interrupt. */ - dma_cb.peri->INTERRUPT_EN = INTR_EN_NONE; + dma_subsys_per[channel].peri->INTERRUPT_EN = INTR_EN_NONE; CSR_CLEAR_BITS(CSR_REG_MIE, DMA_CSR_REG_MIE_MASK ); - if( dma_cb.trans->end != DMA_TRANS_END_POLLING ) + if( dma_subsys_per[channel].trans->end != DMA_TRANS_END_POLLING ) { /* Enable global interrupt. */ CSR_SET_BITS(CSR_REG_MSTATUS, 0x8 ); /* Enable machine-level fast interrupt. */ CSR_SET_BITS(CSR_REG_MIE, DMA_CSR_REG_MIE_MASK ); + /* Enable the transaction interrupt for the channel by setting the corresponding bit in Transaction IFR */ write_register( 0x1, DMA_INTERRUPT_EN_REG_OFFSET, 0xffff, - DMA_INTERRUPT_EN_TRANSACTION_DONE_BIT + DMA_INTERRUPT_EN_TRANSACTION_DONE_BIT, + dma_subsys_per[channel].peri ); /* Only if a window is used should the window interrupt be set. */ @@ -783,7 +852,8 @@ dma_config_flags_t dma_load_transaction( dma_trans_t *p_trans ) 0x1, DMA_INTERRUPT_EN_REG_OFFSET, 0xffff, - DMA_INTERRUPT_EN_WINDOW_DONE_BIT + DMA_INTERRUPT_EN_WINDOW_DONE_BIT, + dma_subsys_per[channel].peri ); } } @@ -802,58 +872,74 @@ dma_config_flags_t dma_load_transaction( dma_trans_t *p_trans ) p_trans->dim = DMA_DIM_CONF_2D; p_trans->size_d2_b = DMA_DATA_TYPE_2_SIZE( p_trans->dst_type ); p_trans->src->inc_d2_du = DMA_DATA_TYPE_2_SIZE( p_trans->dst_type ); - } + + write_register( dma_subsys_per[channel].trans->pad_left_du * DMA_DATA_TYPE_2_SIZE( p_trans->src_type ), + DMA_PAD_LEFT_REG_OFFSET, + DMA_PAD_LEFT_PAD_MASK, + DMA_PAD_LEFT_PAD_OFFSET, + dma_subsys_per[channel].peri); - if (dma_cb.trans->pad_top_du != 0 || dma_cb.trans->pad_bottom_du != 0 || dma_cb.trans->pad_left_du != 0 || dma_cb.trans->pad_right_du != 0) + write_register( dma_subsys_per[channel].trans->pad_right_du * DMA_DATA_TYPE_2_SIZE( p_trans->src_type ), + DMA_PAD_RIGHT_REG_OFFSET, + DMA_PAD_RIGHT_PAD_MASK, + DMA_PAD_RIGHT_PAD_OFFSET, + dma_subsys_per[channel].peri); + } + else if (p_trans->dim == DMA_DIM_CONF_2D) { - write_register( dma_cb.trans->pad_top_du * DMA_DATA_TYPE_2_SIZE( p_trans->dst_type ), + write_register( dma_subsys_per[channel].trans->pad_top_du * DMA_DATA_TYPE_2_SIZE( p_trans->src_type ), DMA_PAD_TOP_REG_OFFSET, DMA_PAD_TOP_PAD_MASK, - DMA_PAD_TOP_PAD_OFFSET); + DMA_PAD_TOP_PAD_OFFSET, + dma_subsys_per[channel].peri); - write_register( dma_cb.trans->pad_bottom_du * DMA_DATA_TYPE_2_SIZE( p_trans->dst_type ), + write_register( dma_subsys_per[channel].trans->pad_bottom_du * DMA_DATA_TYPE_2_SIZE( p_trans->src_type ), DMA_PAD_BOTTOM_REG_OFFSET, DMA_PAD_BOTTOM_PAD_MASK, - DMA_PAD_BOTTOM_PAD_OFFSET); + DMA_PAD_BOTTOM_PAD_OFFSET, + dma_subsys_per[channel].peri); - write_register( dma_cb.trans->pad_left_du * DMA_DATA_TYPE_2_SIZE( p_trans->dst_type ), + write_register( dma_subsys_per[channel].trans->pad_left_du * DMA_DATA_TYPE_2_SIZE( p_trans->src_type ), DMA_PAD_LEFT_REG_OFFSET, DMA_PAD_LEFT_PAD_MASK, - DMA_PAD_LEFT_PAD_OFFSET); + DMA_PAD_LEFT_PAD_OFFSET, + dma_subsys_per[channel].peri); - write_register( dma_cb.trans->pad_right_du * DMA_DATA_TYPE_2_SIZE( p_trans->dst_type ), + write_register( dma_subsys_per[channel].trans->pad_right_du * DMA_DATA_TYPE_2_SIZE( p_trans->src_type ), DMA_PAD_RIGHT_REG_OFFSET, DMA_PAD_RIGHT_PAD_MASK, - DMA_PAD_RIGHT_PAD_OFFSET); + DMA_PAD_RIGHT_PAD_OFFSET, + dma_subsys_per[channel].peri); } /* * SET THE POINTERS */ - dma_cb.peri->SRC_PTR = (uint32_t)dma_cb.trans->src->ptr; + dma_subsys_per[channel].peri->SRC_PTR = (uint32_t)dma_subsys_per[channel].trans->src->ptr; - if(dma_cb.trans->mode != DMA_TRANS_MODE_ADDRESS) + if(dma_subsys_per[channel].trans->mode != DMA_TRANS_MODE_ADDRESS) { /* Write to the destination pointers only if we are not in address mode, otherwise the destination address is read in a separate port in parallel with the data from the address port */ - dma_cb.peri->DST_PTR = (uint32_t)dma_cb.trans->dst->ptr; + dma_subsys_per[channel].peri->DST_PTR = (uint32_t)dma_subsys_per[channel].trans->dst->ptr; } else { - dma_cb.peri->ADDR_PTR = (uint32_t)dma_cb.trans->src_addr->ptr; + dma_subsys_per[channel].peri->ADDR_PTR = (uint32_t)dma_subsys_per[channel].trans->src_addr->ptr; } /* * SET THE TRANSPOSITION MODE */ - write_register(dma_cb.trans->dim_inv, + write_register(dma_subsys_per[channel].trans->dim_inv, DMA_DIM_INV_REG_OFFSET, 0x1 << DMA_DIM_INV_SEL_BIT, - DMA_DIM_INV_SEL_BIT); + DMA_DIM_INV_SEL_BIT, + dma_subsys_per[channel].peri); /* * SET THE INCREMENTS @@ -869,33 +955,37 @@ dma_config_flags_t dma_load_transaction( dma_trans_t *p_trans ) * as the values read from the second port are instead used. * In case of a 2D DMA transaction, the second dimension increment is set. */ - - write_register( get_increment_b_1D( dma_cb.trans->src ), + + write_register( get_increment_b_1D( dma_subsys_per[channel].trans->src, channel), DMA_SRC_PTR_INC_D1_REG_OFFSET, DMA_SRC_PTR_INC_D1_INC_MASK, - DMA_SRC_PTR_INC_D1_INC_OFFSET ); + DMA_SRC_PTR_INC_D1_INC_OFFSET, + dma_subsys_per[channel].peri); - if(dma_cb.trans->dim == DMA_DIM_CONF_2D) + if(dma_subsys_per[channel].trans->dim == DMA_DIM_CONF_2D) { - write_register( get_increment_b_2D( dma_cb.trans->src ), + write_register( get_increment_b_2D( dma_subsys_per[channel].trans->src, channel), DMA_SRC_PTR_INC_D2_REG_OFFSET, DMA_SRC_PTR_INC_D2_INC_MASK, - DMA_SRC_PTR_INC_D2_INC_OFFSET ); + DMA_SRC_PTR_INC_D2_INC_OFFSET, + dma_subsys_per[channel].peri ); } - if(dma_cb.trans->mode != DMA_TRANS_MODE_ADDRESS) + if(dma_subsys_per[channel].trans->mode != DMA_TRANS_MODE_ADDRESS) { - write_register( get_increment_b_1D( dma_cb.trans->dst ), + write_register( get_increment_b_1D( dma_subsys_per[channel].trans->dst, channel), DMA_DST_PTR_INC_D1_REG_OFFSET, DMA_DST_PTR_INC_D1_INC_MASK, - DMA_DST_PTR_INC_D1_INC_OFFSET ); + DMA_DST_PTR_INC_D1_INC_OFFSET, + dma_subsys_per[channel].peri ); - if(dma_cb.trans->dim == DMA_DIM_CONF_2D) + if(dma_subsys_per[channel].trans->dim == DMA_DIM_CONF_2D) { - write_register( get_increment_b_2D( dma_cb.trans->dst ), + write_register( get_increment_b_2D( dma_subsys_per[channel].trans->dst, channel), DMA_DST_PTR_INC_D2_REG_OFFSET, DMA_DST_PTR_INC_D2_INC_MASK, - DMA_DST_PTR_INC_D2_INC_OFFSET ); + DMA_DST_PTR_INC_D2_INC_OFFSET, + dma_subsys_per[channel].peri ); } } @@ -903,66 +993,84 @@ dma_config_flags_t dma_load_transaction( dma_trans_t *p_trans ) * SET THE OPERATION MODE AND WINDOW SIZE */ - dma_cb.peri->MODE = dma_cb.trans->mode; + dma_subsys_per[channel].peri->MODE = dma_subsys_per[channel].trans->mode; /* The window size is set to the transaction size if it was set to 0 in order to disable the functionality (it will never be triggered). */ - dma_cb.peri->WINDOW_SIZE = dma_cb.trans->win_du - ? dma_cb.trans->win_du - : dma_cb.trans->size_b; + dma_subsys_per[channel].peri->WINDOW_SIZE = dma_subsys_per[channel].trans->win_du + ? dma_subsys_per[channel].trans->win_du + : dma_subsys_per[channel].trans->size_b; /* * SET THE DIMENSIONALITY */ - write_register( dma_cb.trans->dim, + write_register( dma_subsys_per[channel].trans->dim, DMA_DIM_CONFIG_REG_OFFSET, - 0x1 << DMA_DIM_CONFIG_DMA_DIM_BIT, - DMA_DIM_CONFIG_DMA_DIM_BIT ); + 0x1, + DMA_DIM_CONFIG_DMA_DIM_BIT, + dma_subsys_per[channel].peri ); + + /* + * SET THE SIGN EXTENSION BIT + */ + write_register( dma_subsys_per[channel].trans->sign_ext, + DMA_SIGN_EXT_REG_OFFSET, + 0x1 << DMA_SIGN_EXT_SIGNED_BIT, + DMA_SIGN_EXT_SIGNED_BIT, + dma_subsys_per[channel].peri ); + /* * SET THE SIGN EXTENSION BIT */ - write_register( dma_cb.trans->sign_ext, + write_register( dma_subsys_per[channel].trans->sign_ext, DMA_SIGN_EXT_REG_OFFSET, 0x1 << DMA_SIGN_EXT_SIGNED_BIT, - DMA_SIGN_EXT_SIGNED_BIT ); + DMA_SIGN_EXT_SIGNED_BIT, + dma_subsys_per[channel].peri ); /* * SET TRIGGER SLOTS AND DATA TYPE */ - write_register( dma_cb.trans->src->trig, + write_register( dma_subsys_per[channel].trans->src->trig, DMA_SLOT_REG_OFFSET, DMA_SLOT_RX_TRIGGER_SLOT_MASK, - DMA_SLOT_RX_TRIGGER_SLOT_OFFSET ); + DMA_SLOT_RX_TRIGGER_SLOT_OFFSET, + dma_subsys_per[channel].peri ); - write_register( dma_cb.trans->dst->trig, + write_register( dma_subsys_per[channel].trans->dst->trig, DMA_SLOT_REG_OFFSET, DMA_SLOT_TX_TRIGGER_SLOT_MASK, - DMA_SLOT_TX_TRIGGER_SLOT_OFFSET ); + DMA_SLOT_TX_TRIGGER_SLOT_OFFSET, + dma_subsys_per[channel].peri ); - write_register( dma_cb.trans->dst_type, + write_register( dma_subsys_per[channel].trans->dst_type, DMA_DST_DATA_TYPE_REG_OFFSET, DMA_DST_DATA_TYPE_DATA_TYPE_MASK, - DMA_SELECTION_OFFSET_START ); + DMA_SELECTION_OFFSET_START, + dma_subsys_per[channel].peri ); - write_register( dma_cb.trans->src_type, + write_register( dma_subsys_per[channel].trans->src_type, DMA_SRC_DATA_TYPE_REG_OFFSET, DMA_SRC_DATA_TYPE_DATA_TYPE_MASK, - DMA_SELECTION_OFFSET_START ); + DMA_SELECTION_OFFSET_START, + dma_subsys_per[channel].peri ); return DMA_CONFIG_OK; } dma_config_flags_t dma_launch( dma_trans_t *p_trans) { + uint8_t channel = p_trans->channel; + /* * Make sure that the loaded transaction is the intended transaction. * If the loaded trans was NULL'd, then this the transaction is never * launched. */ if( ( p_trans == NULL ) - || ( dma_cb.trans != p_trans ) ) // @ToDo: Check per-element. + || ( dma_subsys_per[channel].trans != p_trans ) ) // @ToDo: Check per-element. { return DMA_CONFIG_CRITICAL_ERROR; } @@ -978,7 +1086,7 @@ dma_config_flags_t dma_launch( dma_trans_t *p_trans) * until it has ended. * Transactions can still be validated in the meantime. */ - if( !dma_is_ready() ) + if( !dma_is_ready(channel) ) { return DMA_CONFIG_TRANS_OVERRIDE; } @@ -987,23 +1095,25 @@ dma_config_flags_t dma_launch( dma_trans_t *p_trans) * This has to be done prior to writing the register because otherwise * the interrupt could arrive before it is lowered. */ - dma_cb.intrFlag = 0; + dma_subsys_per[channel].intrFlag = 0; /* Load the size(s) and start the transaction. */ - if(dma_cb.trans->dim == DMA_DIM_CONF_2D) + if(dma_subsys_per[channel].trans->dim == DMA_DIM_CONF_2D) { - write_register( dma_cb.trans->size_d2_b, + write_register( dma_subsys_per[channel].trans->size_d2_b, DMA_SIZE_D2_REG_OFFSET, DMA_SIZE_D2_SIZE_MASK, - DMA_SIZE_D2_SIZE_OFFSET + DMA_SIZE_D2_SIZE_OFFSET, + dma_subsys_per[channel].peri ); } - write_register( dma_cb.trans->size_b, - DMA_SIZE_D1_REG_OFFSET, - DMA_SIZE_D1_SIZE_MASK, - DMA_SIZE_D1_SIZE_OFFSET + write_register( dma_subsys_per[channel].trans->size_b, + DMA_SIZE_D1_REG_OFFSET, + DMA_SIZE_D1_SIZE_MASK, + DMA_SIZE_D1_SIZE_OFFSET, + dma_subsys_per[channel].peri ); /* * If the end event was set to wait for the interrupt, the dma_launch @@ -1012,47 +1122,47 @@ dma_config_flags_t dma_launch( dma_trans_t *p_trans) while( p_trans->end == DMA_TRANS_END_INTR_WAIT - && ( dma_cb.intrFlag != 0x0 ) ) { + && ( dma_subsys_per[channel].intrFlag != 0x0 ) ) { wait_for_interrupt(); } return DMA_CONFIG_OK; } -__attribute__((optimize("O0"))) uint32_t dma_is_ready(void) +__attribute__((optimize("O0"))) uint32_t dma_is_ready(uint8_t channel) { /* The transaction READY bit is read from the status register*/ - uint32_t ret = ( dma_cb.peri->STATUS & (1<STATUS & (1<DONE ); + * return ( 1 && dma_subsys_per[channel].peri->DONE ); * 2) Consider only the LSB == 1 to be a valid 1 using a BITWISE AND. - * return ( 1 & dma_cb.peri->DONE ); + * return ( 1 & dma_subsys_per[channel].peri->DONE ); * This would be fixed if the DONE register was a 1 bit field. */ -uint32_t dma_get_window_count() +uint32_t dma_get_window_count(uint8_t channel) { - return dma_cb.peri->WINDOW_COUNT; + return dma_subsys_per[channel].peri->WINDOW_COUNT; } -void dma_stop_circular() +void dma_stop_circular(uint8_t channel) { /* * The DMA finishes the current transaction before and does not start * a new one. */ - dma_cb.peri->MODE = DMA_TRANS_MODE_SINGLE; + dma_subsys_per[channel].peri->MODE = DMA_TRANS_MODE_SINGLE; } -__attribute__((weak, optimize("O0"))) void dma_intr_handler_trans_done() +__attribute__((weak, optimize("O0"))) void dma_intr_handler_trans_done(uint8_t channel) { /* * The DMA transaction has finished! @@ -1063,7 +1173,7 @@ __attribute__((weak, optimize("O0"))) void dma_intr_handler_trans_done() */ } -__attribute__((weak, optimize("O0"))) void dma_intr_handler_window_done() +__attribute__((weak, optimize("O0"))) void dma_intr_handler_window_done(uint8_t channel) { /* * The DMA has copied another window. @@ -1338,33 +1448,8 @@ static inline uint8_t is_region_outbound_2D( uint8_t *p_start, // Size is be guaranteed to be non-zero before calling this function. } -/* @ToDo: Consider changing the "mask" parameter for a bitfield definition -(see dma_regs.h) */ -static inline void write_register( uint32_t p_val, - uint32_t p_offset, - uint32_t p_mask, - uint8_t p_sel ) -{ - /* - * The index is computed to avoid needing to access the structure - * as a structure. - */ - uint8_t index = p_offset / DMA_REGISTER_SIZE_BYTES; - /* - * An intermediate variable "value" is used to prevent writing twice into - * the register. - */ - uint32_t value = (( uint32_t * ) dma_cb.peri ) [ index ]; - value &= ~( p_mask << p_sel ); - value |= (p_val & p_mask) << p_sel; - (( uint32_t * ) dma_cb.peri ) [ index ] = value; - -// @ToDo: mmio_region_write32(dma->base_addr, (ptrdiff_t)(DMA_SLOT_REG_OFFSET), (tx_slot_mask << DMA_SLOT_TX_TRIGGER_SLOT_OFFSET) + rx_slot_mask) - -} - - -static inline uint32_t get_increment_b_1D( dma_target_t * p_tgt ) +static inline uint32_t get_increment_b_1D( dma_target_t * p_tgt, + uint8_t channel) { uint32_t inc_b = 0; /* If the target uses a trigger, the increment remains 0. */ @@ -1374,7 +1459,8 @@ static inline uint32_t get_increment_b_1D( dma_target_t * p_tgt ) * If the transaction increment has been overriden (due to * misalignments), then that value is used (it's always set to 1). */ - inc_b = dma_cb.trans->inc_b; + inc_b = dma_subsys_per[channel].trans->inc_b; + /* * Otherwise, the target-specific increment is used transformed into * bytes). @@ -1388,7 +1474,8 @@ static inline uint32_t get_increment_b_1D( dma_target_t * p_tgt ) return inc_b; } -static inline uint32_t get_increment_b_2D( dma_target_t * p_tgt ) +static inline uint32_t get_increment_b_2D( dma_target_t * p_tgt, + uint8_t channel ) { uint32_t inc_b = 0; /* If the target uses a trigger, the increment remains 0. */ @@ -1398,7 +1485,7 @@ static inline uint32_t get_increment_b_2D( dma_target_t * p_tgt ) * If the transaction increment has been overriden (due to * misalignments), then that value is used (it's always set to 1). */ - inc_b = dma_cb.trans->inc_b; + inc_b = dma_subsys_per[channel].trans->inc_b; /* * Otherwise, the target-specific increment is used transformed into @@ -1413,8 +1500,13 @@ static inline uint32_t get_increment_b_2D( dma_target_t * p_tgt ) return inc_b; } + +#ifdef __cplusplus +} +#endif // __cplusplus + /****************************************************************************/ /** **/ /* EOF */ /** **/ -/****************************************************************************/ +/****************************************************************************/ \ No newline at end of file diff --git a/sw/device/lib/drivers/dma/dma.h b/sw/device/lib/drivers/dma/dma.h index 2534c4945..ad87434a8 100644 --- a/sw/device/lib/drivers/dma/dma.h +++ b/sw/device/lib/drivers/dma/dma.h @@ -70,6 +70,34 @@ #define DMA_INT_TR_START 0x0 +/* + * For multichannel configurations, a priority mechanism can be set up to allow the interrupt handler + * to prioritize a set of channels. + * + * In order to enable this feature, the user must define DMA_HP_INTR_INDEX. + * + * When an interrupt is raised, the handler will loop through the channels. If the channel that + * raised the interrupt is part of the high priority channels (index <= DMA_HP_INTR_INDEX), + * it will call the actual handler and exit the loop. + * It this way, low index channels will always be serviced first. + * + * However, this feature could cause low priority channels to never be serviced if the high priority + * interrupts are raised at a faster frequency. + * In order to avoid this, the user can define DMA_NUM_HP_INTR (uint16_t). + * This macro puts a limit to the number of consecutive interrupts raised by high priority channels + * that can trigger a "return". + * If N = DMA_NUM_HP_INTR interrupts are raised by high priority channels, the N+1 interrupt will be + * serviced and then no "return" will be triggered, thus allowing the handler's loop to continue and + * low priority channels to be serviced, if necessary. + * + * The priority mechanism is applied to both transaction done and window done interrupts, if enabled. + * Separate counters are used for each type of interrupt. + */ + +//#define DMA_HP_INTR_INDEX 0 +//#define DMA_NUM_HP_INTR 5 + + #ifdef __cplusplus extern "C" { #endif @@ -79,6 +107,8 @@ extern "C" { */ #define DMA_DATA_TYPE_2_SIZE(type) (0b00000100 >> (type) ) +#define DMA_SELECTION_OFFSET_START 0 + /****************************************************************************/ /** **/ /** TYPEDEFS AND STRUCTURES **/ @@ -342,7 +372,7 @@ typedef struct the targets. */ uint8_t sign_ext; /*!< Whether to sign extend the data. */ dma_trans_mode_t mode; /*!< The copy mode to use. */ - uint8_t dim_inv; /*!< If the D1 and D2 dimensions are inverted, i.e. perform transposition. */ + uint8_t dim_inv; /*!< If the D1 and D2 dimensions are inverted, i.e. perform transposition. */ uint32_t win_du; /*!< The amount of data units every which the WINDOW_DONE flag is raised and its corresponding interrupt triggered. It can be set to 0 to disable this functionality. */ @@ -350,6 +380,7 @@ typedef struct is launched. */ dma_config_flags_t flags; /*!< A mask with possible issues aroused from the creation of the transaction. */ + uint8_t channel; /*!< The channel to use. */ } dma_trans_t; /****************************************************************************/ @@ -367,22 +398,62 @@ typedef struct /** * @brief Attends the plic interrupt. */ -void handler_irq_dma( uint32_t id ); +__attribute__((optimize("O0"))) void handler_irq_dma( uint32_t id ); /** * @brief This is a non-weak implementation of the function declared in * fast_intr_ctrl.c */ -void fic_irq_dma(void); +__attribute__((optimize("O0"))) void fic_irq_dma(void); + +/** + * @brief Writes a given value into the specified register. Its operation + * mimics that of bitfield_field32_write(), but does not require the use of + * a field structure, that is not always provided in the _regs.h file. + * @param p_val The value to be written. + * @param p_offset The register's offset from the peripheral's base address + * where the target register is located. + * @param p_mask The variable's mask to only modify its bits inside the whole + * register. + * @param p_sel The selection index (i.e. From which bit inside the register + * the value is to be written). + * @param p_peri The peripheral where the register is located. + */ +/* @ToDo: Consider changing the "mask" parameter for a bitfield definition +(see dma_regs.h) */ +inline void write_register( uint32_t p_val, + uint32_t p_offset, + uint32_t p_mask, + uint8_t p_sel, + dma* p_dma) +{ + /* + * The index is computed to avoid needing to access the structure + * as a structure. + */ + uint8_t index = p_offset / sizeof(uint32_t); + /* + * An intermediate variable "value" is used to prevent writing twice into + * the register. + */ + uint32_t value = (( uint32_t * ) p_dma ) [ index ]; + value &= ~( p_mask << p_sel ); + value |= (p_val & p_mask) << p_sel; + (( uint32_t * ) p_dma ) [ index ] = value; + +// @ToDo: mmio_region_write32(dma->base_addr, (ptrdiff_t)(DMA_SLOT_REG_OFFSET), (tx_slot_mask << DMA_SLOT_TX_TRIGGER_SLOT_OFFSET) + rx_slot_mask) + +} + /** *@brief Takes all DMA configurations to a state where no accidental * transaction can be performed. * It can be called anytime to reset the DMA control block. - * @param peri Pointer to a register address following the dma structure. By + * @param dma_peri Pointer to a register address following the dma structure. By * default (peri == NULL), the integrated DMA will be used. */ -void dma_init( dma *peri ); +void dma_init( dma *dma_peri); /** * @brief Creates a transaction that can be loaded into the DMA. @@ -413,7 +484,7 @@ dma_config_flags_t dma_validate_transaction( dma_trans_t *p_trans, * the result from inside target structure as an error could have appeared * before the creation of the structure. */ -dma_config_flags_t dma_load_transaction( dma_trans_t* p_trans ); +dma_config_flags_t dma_load_transaction( dma_trans_t* p_trans); /** * @brief Launches the loaded transaction. @@ -434,41 +505,45 @@ dma_config_flags_t dma_launch( dma_trans_t* p_trans); * running or a new transaction was launched. * Be careful when calling this function if interrupts were chosen as the end * event. + * @param channel The channel to read from. * @return Whether the DMA is working or not. It starts returning 0 as soon as * the dma_launch function has returned. * @retval 0 - DMA is working. * @retval 1 - DMA has finished the transmission. DMA is idle. */ -uint32_t dma_is_ready(void); +uint32_t dma_is_ready(uint8_t channel); /** * @brief Get the number of windows that have already been written. Resets on * the start of each transaction. + * @param channel The channel to read from. * @return The number of windows that have been written from this transaction. */ -uint32_t dma_get_window_count(void); +uint32_t dma_get_window_count(uint8_t channel); /** * @brief Prevent the DMA from relaunching the transaction automatically after * finishing the current one. It does not affect the currently running * transaction. It has no effect if the DMA is operating in SINGLE * transaction mode. + * @param channel The channel to stop. */ -void dma_stop_circular(void); +void dma_stop_circular(uint8_t channel); /** * @brief DMA interrupt handler. * `dma.c` provides a weak definition of this symbol, which can be overridden * at link-time by providing an additional non-weak definition. +* @param channel The channel that triggered the interrupt. */ -void dma_intr_handler_trans_done(void); +void dma_intr_handler_trans_done(uint8_t channel); /** * @brief DMA interrupt handler. * `dma.c` provides a weak definition of this symbol, which can be overridden * at link-time by providing an additional non-weak definition. */ -void dma_intr_handler_window_done(void); +void dma_intr_handler_window_done(uint8_t channel); /** * @brief This weak implementation allows the user to override the threshold diff --git a/sw/device/lib/drivers/dma/dma_regs.h b/sw/device/lib/drivers/dma/dma_regs.h index a598323d3..408b23ae7 100644 --- a/sw/device/lib/drivers/dma/dma_regs.h +++ b/sw/device/lib/drivers/dma/dma_regs.h @@ -176,6 +176,14 @@ extern "C" { #define DMA_INTERRUPT_EN_TRANSACTION_DONE_BIT 0 #define DMA_INTERRUPT_EN_WINDOW_DONE_BIT 1 +// Interrupt Flag Register for transactions +#define DMA_TRANSACTION_IFR_REG_OFFSET 0x60 +#define DMA_TRANSACTION_IFR_FLAG_BIT 0 + +// Interrupt Flag Register for windows +#define DMA_WINDOW_IFR_REG_OFFSET 0x64 +#define DMA_WINDOW_IFR_FLAG_BIT 0 + #ifdef __cplusplus } // extern "C" #endif diff --git a/sw/device/lib/drivers/fast_intr_ctrl/fast_intr_ctrl.c b/sw/device/lib/drivers/fast_intr_ctrl/fast_intr_ctrl.c index 27c000a00..0493864e4 100644 --- a/sw/device/lib/drivers/fast_intr_ctrl/fast_intr_ctrl.c +++ b/sw/device/lib/drivers/fast_intr_ctrl/fast_intr_ctrl.c @@ -29,6 +29,9 @@ /* MODULES USED */ /** **/ /****************************************************************************/ +#ifdef __cplusplus +extern "C" { +#endif #include "fast_intr_ctrl.h" #include "core_v_mini_mcu.h" @@ -418,6 +421,9 @@ void handler_irq_fast_gpio_7(void) // call the weak fic handler fic_irq_gpio_7(); } +#ifdef __cplusplus +} +#endif /****************************************************************************/ /** **/ diff --git a/sw/device/lib/drivers/fast_intr_ctrl/fast_intr_ctrl.h b/sw/device/lib/drivers/fast_intr_ctrl/fast_intr_ctrl.h index 879975515..46c0ada20 100644 --- a/sw/device/lib/drivers/fast_intr_ctrl/fast_intr_ctrl.h +++ b/sw/device/lib/drivers/fast_intr_ctrl/fast_intr_ctrl.h @@ -32,7 +32,9 @@ /** MODULES USED **/ /** **/ /****************************************************************************/ - +#ifdef __cplusplus +extern "C" { +#endif #include #include #include "mmio.h" @@ -242,7 +244,9 @@ void fic_irq_gpio_7(void); /** INLINE FUNCTIONS **/ /** **/ /****************************************************************************/ - +#ifdef __cplusplus +} +#endif /****************************************************************************/ /** **/ /** EOF **/ diff --git a/sw/device/lib/drivers/gpio/gpio.c b/sw/device/lib/drivers/gpio/gpio.c index f5cb004f2..7f28db54a 100644 --- a/sw/device/lib/drivers/gpio/gpio.c +++ b/sw/device/lib/drivers/gpio/gpio.c @@ -23,7 +23,9 @@ * @version 1 * @brief GPIO driver */ - +#ifdef __cplusplus +extern "C" { +#endif // __cplusplus /****************************************************************************/ /** **/ /* MODULES USED */ @@ -572,4 +574,9 @@ __attribute__((optimize("O0"))) static void gpio_handler_irq_dummy( uint32_t dum /** **/ /* EOF */ /** **/ -/****************************************************************************/ \ No newline at end of file +/****************************************************************************/ + + +#ifdef __cplusplus +} +#endif // __cplusplus \ No newline at end of file diff --git a/sw/device/lib/drivers/power_manager/power_manager.c b/sw/device/lib/drivers/power_manager/power_manager.c index 4a9c37a85..066771218 100644 --- a/sw/device/lib/drivers/power_manager/power_manager.c +++ b/sw/device/lib/drivers/power_manager/power_manager.c @@ -15,174 +15,7 @@ #include "x-heep.h" - -void __attribute__ ((noinline)) power_gate_core_asm() -{ - asm volatile ( - - // write POWER_GATE_CORE[0] = 1 - "lui a0, %[base_address_20bit]\n" - "li a1, 1\n" - "sw a1, %[power_manager_power_gate_core_reg_offset](a0)\n" - - // write WAKEUP_STATE[0] = 1 - "sw a1, %[power_manager_wakeup_state_reg_offset](a0)\n" : : \ - \ - [base_address_20bit] "i" (POWER_MANAGER_START_ADDRESS >> 12), \ - [power_manager_power_gate_core_reg_offset] "i" (POWER_MANAGER_POWER_GATE_CORE_REG_OFFSET), \ - [power_manager_wakeup_state_reg_offset] "i" (POWER_MANAGER_WAKEUP_STATE_REG_OFFSET) : "a0", "a1" \ - ); - - asm volatile ( - - // write registers - "la a0, __power_manager_start\n" - "sw x1, 0(a0)\n" - "sw x2, 4(a0)\n" - "sw x3, 8(a0)\n" - "sw x4, 12(a0)\n" - "sw x5, 16(a0)\n" - "sw x6, 20(a0)\n" - "sw x7, 24(a0)\n" - "sw x8, 28(a0)\n" - "sw x9, 32(a0)\n" - "sw x10, 36(a0)\n" - "sw x11, 40(a0)\n" - "sw x12, 44(a0)\n" - "sw x13, 48(a0)\n" - "sw x14, 52(a0)\n" - "sw x15, 56(a0)\n" - "sw x16, 60(a0)\n" - "sw x17, 64(a0)\n" - "sw x18, 68(a0)\n" - "sw x19, 72(a0)\n" - "sw x20, 76(a0)\n" - "sw x21, 80(a0)\n" - "sw x22, 88(a0)\n" - "sw x23, 92(a0)\n" - "sw x24, 96(a0)\n" - "sw x25, 100(a0)\n" - "sw x26, 104(a0)\n" - "sw x27, 108(a0)\n" - "sw x28, 112(a0)\n" - "sw x29, 116(a0)\n" - "sw x30, 120(a0)\n" - "sw x31, 124(a0)\n" - //csr - "csrr a1, mstatus\n" - "sw a1, 128(a0)\n" - "csrr a1, mie\n" - "sw a1, 132(a0)\n" - "csrr a1, mtvec\n" - "sw a1, 136(a0)\n" - "csrr a1, mscratch\n" - "sw a1, 140(a0)\n" - "csrr a1, mepc\n" - "sw a1, 144(a0)\n" - "csrr a1, mcause\n" - "sw a1, 148(a0)\n" - "csrr a1, mtval\n" - "sw a1, 152(a0)\n" - "csrr a1, mcycle\n" - "sw a1, 156(a0)\n" - "csrr a1, minstret\n" - "sw a1, 160(a0)\n" : : : "a0", "a1" \ - ); - - asm volatile ( - - // write RESTORE_ADDRESS[31:0] = PC - "lui a0, %[base_address_20bit]\n" - "la a1, wakeup\n" - "sw a1, %[power_manager_restore_address_reg_offset](a0)\n" - - // wait for interrupt - "wfi\n" - - // ---------------------------- - // power-gate - // ---------------------------- - - // ---------------------------- - // wake-up - // ---------------------------- - - // write POWER_GATE_CORE[0] = 0 - "wakeup:" - "lui a0, %[base_address_20bit]\n" - "sw x0, %[power_manager_power_gate_core_reg_offset](a0)\n" - - // write WAKEUP_STATE[0] = 0 - "sw x0, %[power_manager_wakeup_state_reg_offset](a0)\n" - - // write RESTORE_ADDRESS[31:0] = 0 - "sw x0, %[power_manager_restore_address_reg_offset](a0)\n" : : \ - \ - [base_address_20bit] "i" (POWER_MANAGER_START_ADDRESS >> 12), \ - [power_manager_power_gate_core_reg_offset] "i" (POWER_MANAGER_POWER_GATE_CORE_REG_OFFSET), \ - [power_manager_wakeup_state_reg_offset] "i" (POWER_MANAGER_WAKEUP_STATE_REG_OFFSET), \ - [power_manager_restore_address_reg_offset] "i" (POWER_MANAGER_RESTORE_ADDRESS_REG_OFFSET) : "a0", "a1" \ - ); - - asm volatile ( - - // write CORE_REG_Xn[31:0] = Xn - "la a0, __power_manager_start\n" - //one of the following load is gonna overwrite a0, but a0 was already stored before to the right value - "lw x1, 0(a0)\n" - "lw x2, 4(a0)\n" - "lw x3, 8(a0)\n" - "lw x4, 12(a0)\n" - "lw x5, 16(a0)\n" - "lw x6, 20(a0)\n" - "lw x7, 24(a0)\n" - "lw x8, 28(a0)\n" - "lw x9, 32(a0)\n" - "lw x10, 36(a0)\n" - "lw x11, 40(a0)\n" - "lw x12, 44(a0)\n" - "lw x13, 48(a0)\n" - "lw x14, 52(a0)\n" - "lw x15, 56(a0)\n" - "lw x16, 60(a0)\n" - "lw x17, 64(a0)\n" - "lw x18, 68(a0)\n" - "lw x19, 72(a0)\n" - "lw x20, 76(a0)\n" - "lw x21, 80(a0)\n" - "lw x22, 88(a0)\n" - "lw x23, 92(a0)\n" - "lw x24, 96(a0)\n" - "lw x25, 100(a0)\n" - "lw x26, 104(a0)\n" - "lw x27, 108(a0)\n" - "lw x28, 112(a0)\n" - "lw x29, 116(a0)\n" - "lw x30, 120(a0)\n" - "lw x31, 124(a0)\n" - //csr - "lw a1, 128(a0)\n" - "csrw mstatus, a1\n" - "lw a1, 132(a0)\n" - "csrw mie, a1\n" - "lw a1, 136(a0)\n" - "csrw mtvec, a1\n" - "lw a1, 140(a0)\n" - "csrw mscratch, a1\n" - "lw a1, 144(a0)\n" - "csrw mepc, a1\n" - "lw a1, 148(a0)\n" - "csrw mcause, a1\n" - "lw a1, 152(a0)\n" - "csrw mtval, a1\n" - "lw a1, 156(a0)\n" - "csrw mcycle, a1\n" - "lw a1, 160(a0)\n" - "csrw minstret, a1\n": : : "a0", "a1" \ - ); - - return; -} +extern void power_manager_cpu_store(); power_manager_result_t __attribute__ ((noinline)) power_gate_core(const power_manager_t *power_manager, power_manager_sel_intr_t sel_intr, power_manager_counters_t* cpu_counter) { @@ -201,14 +34,11 @@ power_manager_result_t __attribute__ ((noinline)) power_gate_core(const power_ma mmio_region_write32(power_manager->base_addr, (ptrdiff_t)(POWER_MANAGER_INTR_STATE_REG_OFFSET), 0x0); // enable wait for SWITCH ACK - #ifdef TARGET_PYNQ_Z2 - reg = bitfield_bit32_write(reg, POWER_MANAGER_CPU_WAIT_ACK_SWITCH_ON_COUNTER_CPU_WAIT_ACK_SWITCH_ON_COUNTER_BIT, 0x0); - #else - reg = bitfield_bit32_write(reg, POWER_MANAGER_CPU_WAIT_ACK_SWITCH_ON_COUNTER_CPU_WAIT_ACK_SWITCH_ON_COUNTER_BIT, 0x1); - #endif + reg = bitfield_bit32_write(reg, POWER_MANAGER_CPU_WAIT_ACK_SWITCH_ON_COUNTER_CPU_WAIT_ACK_SWITCH_ON_COUNTER_BIT, 0x1); + mmio_region_write32(power_manager->base_addr, (ptrdiff_t)(POWER_MANAGER_CPU_WAIT_ACK_SWITCH_ON_COUNTER_REG_OFFSET), reg); - power_gate_core_asm(); + power_manager_cpu_store(); // clean up states mmio_region_write32(power_manager->base_addr, (ptrdiff_t)(POWER_MANAGER_EN_WAIT_FOR_INTR_REG_OFFSET), 0x0); @@ -231,11 +61,7 @@ power_manager_result_t __attribute__ ((noinline)) power_gate_periph(const power_ { uint32_t reg = 0; - #ifdef TARGET_PYNQ_Z2 - mmio_region_write32(power_manager->base_addr, (ptrdiff_t)(POWER_MANAGER_PERIPH_WAIT_ACK_SWITCH_ON_REG_OFFSET), 0x0); - #else - mmio_region_write32(power_manager->base_addr, (ptrdiff_t)(POWER_MANAGER_PERIPH_WAIT_ACK_SWITCH_ON_REG_OFFSET), 0x1); - #endif + mmio_region_write32(power_manager->base_addr, (ptrdiff_t)(POWER_MANAGER_PERIPH_WAIT_ACK_SWITCH_ON_REG_OFFSET), 0x1); if (sel_state == kOn_e) { @@ -265,11 +91,7 @@ power_manager_result_t __attribute__ ((noinline)) power_gate_ram_block(const pow if (sel_state == kOn_e) { - #ifdef TARGET_PYNQ_Z2 - mmio_region_write32(power_manager->base_addr, (ptrdiff_t)(power_manager_ram_map[sel_block].wait_ack_switch), 0x0); - #else - mmio_region_write32(power_manager->base_addr, (ptrdiff_t)(power_manager_ram_map[sel_block].wait_ack_switch), 0x1); - #endif + mmio_region_write32(power_manager->base_addr, (ptrdiff_t)(power_manager_ram_map[sel_block].wait_ack_switch), 0x1); for (int i=0; iswitch_on; i++) asm volatile ("nop\n;"); mmio_region_write32(power_manager->base_addr, (ptrdiff_t)(power_manager_ram_map[sel_block].switch_off), 0x0); for (int i=0; iiso_off; i++) asm volatile ("nop\n;"); @@ -277,11 +99,7 @@ power_manager_result_t __attribute__ ((noinline)) power_gate_ram_block(const pow } else if (sel_state == kOff_e) { - #ifdef TARGET_PYNQ_Z2 - mmio_region_write32(power_manager->base_addr, (ptrdiff_t)(power_manager_ram_map[sel_block].wait_ack_switch), 0x0); - #else - mmio_region_write32(power_manager->base_addr, (ptrdiff_t)(power_manager_ram_map[sel_block].wait_ack_switch), 0x1); - #endif + mmio_region_write32(power_manager->base_addr, (ptrdiff_t)(power_manager_ram_map[sel_block].wait_ack_switch), 0x1); for (int i=0; iiso_on; i++) asm volatile ("nop\n;"); mmio_region_write32(power_manager->base_addr, (ptrdiff_t)(power_manager_ram_map[sel_block].iso), 0x1); for (int i=0; iswitch_off; i++) asm volatile ("nop\n;"); @@ -309,11 +127,7 @@ power_manager_result_t __attribute__ ((noinline)) power_gate_external(const powe if (sel_state == kOn_e) { - #ifdef TARGET_PYNQ_Z2 - mmio_region_write32(power_manager->base_addr, (ptrdiff_t)(power_manager_external_map[sel_external].wait_ack_switch), 0x0); - #else - mmio_region_write32(power_manager->base_addr, (ptrdiff_t)(power_manager_external_map[sel_external].wait_ack_switch), 0x1); - #endif + mmio_region_write32(power_manager->base_addr, (ptrdiff_t)(power_manager_external_map[sel_external].wait_ack_switch), 0x1); for (int i=0; iswitch_on; i++) asm volatile ("nop\n;"); mmio_region_write32(power_manager->base_addr, (ptrdiff_t)(power_manager_external_map[sel_external].switch_off), 0x0); for (int i=0; iiso_off; i++) asm volatile ("nop\n;"); @@ -323,11 +137,7 @@ power_manager_result_t __attribute__ ((noinline)) power_gate_external(const powe } else if (sel_state == kOff_e) { - #ifdef TARGET_PYNQ_Z2 - mmio_region_write32(power_manager->base_addr, (ptrdiff_t)(power_manager_external_map[sel_external].wait_ack_switch), 0x0); - #else - mmio_region_write32(power_manager->base_addr, (ptrdiff_t)(power_manager_external_map[sel_external].wait_ack_switch), 0x1); - #endif + mmio_region_write32(power_manager->base_addr, (ptrdiff_t)(power_manager_external_map[sel_external].wait_ack_switch), 0x1); for (int i=0; iiso_on; i++) asm volatile ("nop\n;"); mmio_region_write32(power_manager->base_addr, (ptrdiff_t)(power_manager_external_map[sel_external].iso), 0x1); for (int i=0; iswitch_off; i++) asm volatile ("nop\n;"); diff --git a/sw/device/lib/drivers/power_manager/power_manager_cpu_restore.S b/sw/device/lib/drivers/power_manager/power_manager_cpu_restore.S new file mode 100644 index 000000000..00c9f9f3c --- /dev/null +++ b/sw/device/lib/drivers/power_manager/power_manager_cpu_restore.S @@ -0,0 +1,88 @@ +// Copyright 2024 EPFL +// Solderpad Hardware License, Version 2.1, see LICENSE.md for details. +// SPDX-License-Identifier: Apache-2.0 WITH SHL-2.1 + +#include "core_v_mini_mcu.h" +#include "power_manager_regs.h" // Generated. + +# power_manager_cpu.S +# This function re-stores the CPU context when back from (deep) sleep +.global power_manager_cpu_restore +.type power_manager_cpu_restore, @function + +#define POWER_MANAGER_START_ADDRESS_20bit (POWER_MANAGER_START_ADDRESS >> 12) + + +power_manager_cpu_restore: + + //using lui to load the upper 20 bits of the address instead of la as I want to be sure no other registers are used + lui t0, POWER_MANAGER_START_ADDRESS_20bit + sw x0, POWER_MANAGER_POWER_GATE_CORE_REG_OFFSET(t0) + + // write WAKEUP_STATE[0] = 0 + sw x0, POWER_MANAGER_WAKEUP_STATE_REG_OFFSET(t0) + + // write RESTORE_ADDRESS[31:0] = 0 + sw x0, POWER_MANAGER_RESTORE_ADDRESS_REG_OFFSET(t0) + + // restore gp as it is gonna be used to calculate the address of __power_manager_start + lw gp, POWER_MANAGER_GLOBAL_POINTER_REG_OFFSET(t0) + + // write CORE_REG_Xn[31:0] = Xn + la t0, __power_manager_start + // restore context, this part could be optimized + + //one of the following load is gonna overwrite t0, but t0 was already stored before to the right value + lw x1, 0(t0) + lw x2, 4(t0) + lw x3, 8(t0) + lw x4, 12(t0) + lw x5, 16(t0) + lw x6, 20(t0) + lw x7, 24(t0) + lw x8, 28(t0) + lw x9, 32(t0) + lw x10, 36(t0) + lw x11, 40(t0) + lw x12, 44(t0) + lw x13, 48(t0) + lw x14, 52(t0) + lw x15, 56(t0) + lw x16, 60(t0) + lw x17, 64(t0) + lw x18, 68(t0) + lw x19, 72(t0) + lw x20, 76(t0) + lw x21, 80(t0) + lw x22, 88(t0) + lw x23, 92(t0) + lw x24, 96(t0) + lw x25, 100(t0) + lw x26, 104(t0) + lw x27, 108(t0) + lw x28, 112(t0) + lw x29, 116(t0) + lw x30, 120(t0) + lw x31, 124(t0) + //csr + lw t1, 128(t0) + csrw mstatus, t1 + lw t1, 132(t0) + csrw mie, t1 + lw t1, 136(t0) + csrw mtvec, t1 + lw t1, 140(t0) + csrw mscratch, t1 + lw t1, 144(t0) + csrw mepc, t1 + lw t1, 148(t0) + csrw mcause, t1 + lw t1, 152(t0) + csrw mtval, t1 + lw t1, 156(t0) + csrw mcycle, t1 + lw t1, 160(t0) + csrw minstret, t1 + + + ret \ No newline at end of file diff --git a/sw/device/lib/drivers/power_manager/power_manager_cpu_store.S b/sw/device/lib/drivers/power_manager/power_manager_cpu_store.S new file mode 100644 index 000000000..92e1ca8e0 --- /dev/null +++ b/sw/device/lib/drivers/power_manager/power_manager_cpu_store.S @@ -0,0 +1,88 @@ +// Copyright 2024 EPFL +// Solderpad Hardware License, Version 2.1, see LICENSE.md for details. +// SPDX-License-Identifier: Apache-2.0 WITH SHL-2.1 + +#include "core_v_mini_mcu.h" +#include "power_manager_regs.h" // Generated. + +# power_manager_cpu.S +# This function stores the CPU context, goes to (deep) sleep with WFI +.global power_manager_cpu_store # make the function visible to the linker +.type power_manager_cpu_store, @function + +power_manager_cpu_store: + + // write POWER_GATE_CORE[0] = 1 + la t0, POWER_MANAGER_START_ADDRESS + li t1, 1 + sw t1, POWER_MANAGER_POWER_GATE_CORE_REG_OFFSET(t0) + + // write WAKEUP_STATE[0] = 1, this is check in the bootrom at reset time when waking up + sw t1, POWER_MANAGER_WAKEUP_STATE_REG_OFFSET(t0) + + // save the global pointer, _power_manager_start is the start of the power_manager section and saved in the gp reg + // when returning from deep sleep, the gp register is used to restore the context, thus saving it in an always on register + sw gp, POWER_MANAGER_GLOBAL_POINTER_REG_OFFSET(t0) + + // save context, this part could be optimized + la t0, __power_manager_start + sw x1, 0(t0) + sw x2, 4(t0) + sw x3, 8(t0) + sw x4, 12(t0) + sw x5, 16(t0) + sw x6, 20(t0) + sw x7, 24(t0) + sw x8, 28(t0) + sw x9, 32(t0) + sw x10, 36(t0) + sw x11, 40(t0) + sw x12, 44(t0) + sw x13, 48(t0) + sw x14, 52(t0) + sw x15, 56(t0) + sw x16, 60(t0) + sw x17, 64(t0) + sw x18, 68(t0) + sw x19, 72(t0) + sw x20, 76(t0) + sw x21, 80(t0) + sw x22, 88(t0) + sw x23, 92(t0) + sw x24, 96(t0) + sw x25, 100(t0) + sw x26, 104(t0) + sw x27, 108(t0) + sw x28, 112(t0) + sw x29, 116(t0) + sw x30, 120(t0) + sw x31, 124(t0) + //csr + csrr t1, mstatus + sw t1, 128(t0) + csrr t1, mie + sw t1, 132(t0) + csrr t1, mtvec + sw t1, 136(t0) + csrr t1, mscratch + sw t1, 140(t0) + csrr t1, mepc + sw t1, 144(t0) + csrr t1, mcause + sw t1, 148(t0) + csrr t1, mtval + sw t1, 152(t0) + csrr t1, mcycle + sw t1, 156(t0) + csrr t1, minstret + sw t1, 160(t0) + + la t0, POWER_MANAGER_START_ADDRESS + //save return address to restore + la t1, power_manager_cpu_restore + sw t1, POWER_MANAGER_RESTORE_ADDRESS_REG_OFFSET(t0) + + // wait for interrupt + wfi + + ret \ No newline at end of file diff --git a/sw/device/lib/drivers/rv_plic/rv_plic.c b/sw/device/lib/drivers/rv_plic/rv_plic.c index ffbb822df..c7fb8c7cf 100644 --- a/sw/device/lib/drivers/rv_plic/rv_plic.c +++ b/sw/device/lib/drivers/rv_plic/rv_plic.c @@ -34,6 +34,9 @@ /* MODULES USED */ /** **/ /****************************************************************************/ +#ifdef __cplusplus +extern "C" { +#endif #include "rv_plic.h" #include "rv_plic_structs.h" @@ -72,7 +75,7 @@ const uint32_t plicMaxPriority = RV_PLIC_PRIO0_PRIO0_MASK; * Pointer used to dynamically access the different interrupt handlers. */ typedef void (*handler_funct_t)(uint32_t); - +//#endif /****************************************************************************/ /** **/ /* PROTOTYPES OF LOCAL FUNCTIONS */ @@ -366,8 +369,7 @@ plic_result_t plic_software_irq_is_pending(void) } -plic_result_t plic_assign_external_irq_handler( uint32_t id, - void *handler ) +plic_result_t plic_assign_external_irq_handler( uint32_t id, void *handler ) { if( id >= EXT_IRQ_START && id <= QTY_INTR ) { @@ -377,6 +379,7 @@ plic_result_t plic_assign_external_irq_handler( uint32_t id, return kPlicBadArg; } + void plic_reset_handlers_list(void) { handlers[NULL_INTR] = &handler_irq_dummy; @@ -433,7 +436,9 @@ static uint8_t plic_irq_bit_index( uint32_t irq) { return irq % RV_PLIC_PARAM_REG_WIDTH; } - +#ifdef __cplusplus +} +#endif /****************************************************************************/ /** **/ /* EOF */ diff --git a/sw/device/lib/drivers/rv_timer/rv_timer.h b/sw/device/lib/drivers/rv_timer/rv_timer.h index 09f309452..cba449743 100644 --- a/sw/device/lib/drivers/rv_timer/rv_timer.h +++ b/sw/device/lib/drivers/rv_timer/rv_timer.h @@ -105,6 +105,12 @@ typedef enum rv_timer_approximate_tick_params_result { * @param[out] out Tick parameters that will approximately produce the desired * counter frequency. * @return The result of the operation. + * + * The minimum value for `counter_freq` is given by: + * counter_freq_min = (255/4096) * clock_freq + * For example, if the clock frequency is 15MHz, the minimum value for `counter_freq` is about + * 1 MHz. + * The maximum value for `counter_freq` is given by the clock frequency. */ rv_timer_approximate_tick_params_result_t rv_timer_approximate_tick_params(uint64_t clock_freq, uint64_t counter_freq, diff --git a/sw/device/lib/drivers/soc_ctrl/soc_ctrl.h b/sw/device/lib/drivers/soc_ctrl/soc_ctrl.h index 9cfa45aff..fe0e33c76 100644 --- a/sw/device/lib/drivers/soc_ctrl/soc_ctrl.h +++ b/sw/device/lib/drivers/soc_ctrl/soc_ctrl.h @@ -21,7 +21,7 @@ extern "C" { * Initialization parameters for SOC CTRL. * */ -typedef struct soc_ctrl { +typedef struct /*soc_ctrl*/ { /** * The base address for the soc_ctrl hardware registers. */ diff --git a/sw/device/lib/drivers/spi_host/spi_host.c b/sw/device/lib/drivers/spi_host/spi_host.c index b1c945cda..f22c2bdb3 100644 --- a/sw/device/lib/drivers/spi_host/spi_host.c +++ b/sw/device/lib/drivers/spi_host/spi_host.c @@ -31,7 +31,9 @@ /* MODULES USED */ /** **/ /****************************************************************************/ - +#ifdef __cplusplus +extern "C" { +#endif #include "spi_host.h" #include "bitfield.h" @@ -479,9 +481,11 @@ spi_return_flags_e spi_acknowledge_event(spi_host_t* spi) { SPI_HOST_INTR_STATE_SPI_EVENT_BIT, true); return SPI_FLAG_OK; } - +#ifdef __cplusplus +} +#endif /****************************************************************************/ /** **/ /* EOF */ /** **/ -/****************************************************************************/ +/****************************************************************************/ \ No newline at end of file diff --git a/sw/device/lib/drivers/spi_host/spi_host.h b/sw/device/lib/drivers/spi_host/spi_host.h index dafe9b736..6eef81b96 100644 --- a/sw/device/lib/drivers/spi_host/spi_host.h +++ b/sw/device/lib/drivers/spi_host/spi_host.h @@ -1021,4 +1021,4 @@ void spi_intr_handler_error_host2(spi_error_e errors); /** **/ /** EOF **/ /** **/ -/****************************************************************************/ +/****************************************************************************/ \ No newline at end of file diff --git a/sw/device/lib/drivers/uart/uart.c b/sw/device/lib/drivers/uart/uart.c index 08a32fa7f..fef349c45 100644 --- a/sw/device/lib/drivers/uart/uart.c +++ b/sw/device/lib/drivers/uart/uart.c @@ -4,12 +4,14 @@ // Modified version for core-v-mini-mcu // original at: https://github.com/lowRISC/opentitan/blob/master/sw/ - +#ifdef __cplusplus +extern "C" { +#endif #include "uart.h" #include #include - +#include "assert.h" #include "bitfield.h" #include "mmio.h" #include "error.h" @@ -17,8 +19,12 @@ #include "uart_regs.h" // Generated. #define NCO_WIDTH 16 -_Static_assert((1UL << NCO_WIDTH) - 1 == UART_CTRL_NCO_MASK, - "Bad value for NCO_WIDTH"); + +//#ifdef __cplusplus +static_assert((1UL << NCO_WIDTH) - 1 == UART_CTRL_NCO_MASK, "Bad value for NCO_WIDTH"); +//#else +static_assert((1UL << NCO_WIDTH) - 1 == UART_CTRL_NCO_MASK, "Bad value for NCO_WIDTH"); +//#endif static void uart_reset(const uart_t *uart) { mmio_region_write32(uart->base_addr, UART_CTRL_REG_OFFSET, 0u); @@ -147,4 +153,8 @@ size_t uart_sink(void *uart, const char *data, size_t len) { __attribute__((weak, optimize("O0"))) void handler_irq_uart(uint32_t id) { // Replace this function with a non-weak implementation -} \ No newline at end of file +} + +#ifdef __cplusplus +} +#endif \ No newline at end of file diff --git a/sw/device/lib/runtime/core_v_mini_mcu.h.tpl b/sw/device/lib/runtime/core_v_mini_mcu.h.tpl index a0ae2ed11..710cf02e6 100644 --- a/sw/device/lib/runtime/core_v_mini_mcu.h.tpl +++ b/sw/device/lib/runtime/core_v_mini_mcu.h.tpl @@ -14,6 +14,12 @@ extern "C" { #define HAS_MEMORY_BANKS_IL % endif +% for bank in xheep.iter_ram_banks(): +#define RAM${bank.name()}_START_ADDRESS 0x${f'{bank.start_address():08X}'} +#define RAM${bank.name()}_END_ADDRESS 0x${f'{bank.end_address():08X}'} +% endfor + + #define EXTERNAL_DOMAINS ${external_domains} #define DEBUG_START_ADDRESS 0x${debug_start_address} @@ -33,6 +39,9 @@ extern "C" { %endfor +#define DMA_CH_NUM ${dma_ch_count} +#define DMA_CH_SIZE 0x${dma_ch_size} + //switch-on/off peripherals #define PERIPHERAL_START_ADDRESS 0x${peripheral_start_address} #define PERIPHERAL_SIZE 0x${peripheral_size_address} diff --git a/sw/device/lib/runtime/core_v_mini_mcu_memory.h.tpl b/sw/device/lib/runtime/core_v_mini_mcu_memory.h.tpl new file mode 100644 index 000000000..02a99da59 --- /dev/null +++ b/sw/device/lib/runtime/core_v_mini_mcu_memory.h.tpl @@ -0,0 +1,29 @@ +// Copyright 2024 EPFL +// Solderpad Hardware License, Version 2.1, see LICENSE.md for details. +// SPDX-License-Identifier: Apache-2.0 WITH SHL-2.1 + +#ifndef COREV_MINI_MCU_MEMORY_H_ +#define COREV_MINI_MCU_MEMORY_H_ + +#ifdef __cplusplus +extern "C" { +#endif // __cplusplus + +#include "core_v_mini_mcu.h" + +typedef struct memory_address { + unsigned int start; + unsigned int end; +} xheep_memory_address_t; + +xheep_memory_address_t xheep_memory_regions[MEMORY_BANKS] = { +% for bank in xheep.iter_ram_banks(): + {.start = RAM${bank.name()}_START_ADDRESS, .end = RAM${bank.name()}_END_ADDRESS}, +% endfor +}; + +#ifdef __cplusplus +} // extern "C" +#endif // __cplusplus + +#endif // COREV_MINI_MCU_MEMORY_H_ diff --git a/sw/device/lib/runtime/handler.c b/sw/device/lib/runtime/handler.c index c9cd02d35..564abdcdc 100644 --- a/sw/device/lib/runtime/handler.c +++ b/sw/device/lib/runtime/handler.c @@ -2,6 +2,10 @@ // Licensed under the Apache License, Version 2.0, see LICENSE for details. // SPDX-License-Identifier: Apache-2.0 +#ifdef __cplusplus +extern "C" { +#endif // __cplusplus + #include "handler.h" #include "csr.h" #include "stdasm.h" @@ -110,3 +114,7 @@ __attribute__((weak)) void handler_ecall(void) { while (1) { } } +#ifdef __cplusplus +} +#endif // __cplusplus + diff --git a/sw/device/lib/runtime/handler.h b/sw/device/lib/runtime/handler.h index 8f987be7f..1aaacc3ba 100644 --- a/sw/device/lib/runtime/handler.h +++ b/sw/device/lib/runtime/handler.h @@ -5,6 +5,10 @@ #ifndef OPENTITAN_SW_DEVICE_LIB_HANDLER_H_ #define OPENTITAN_SW_DEVICE_LIB_HANDLER_H_ +#ifdef __cplusplus +extern "C" { +#endif // __cplusplus + typedef enum exc_id { kInstMisa = 0, kInstAccFault = 1, @@ -126,4 +130,9 @@ void handler_lsu_fault(void); */ void handler_ecall(void); + +#ifdef __cplusplus +} +#endif // __cplusplus + #endif // OPENTITAN_SW_DEVICE_LIB_HANDLER_H_ diff --git a/sw/device/lib/runtime/hart.h b/sw/device/lib/runtime/hart.h index c2e885c0e..9759edfb4 100644 --- a/sw/device/lib/runtime/hart.h +++ b/sw/device/lib/runtime/hart.h @@ -5,6 +5,11 @@ #ifndef OPENTITAN_SW_DEVICE_LIB_RUNTIME_HART_H_ #define OPENTITAN_SW_DEVICE_LIB_RUNTIME_HART_H_ +#ifdef __cplusplus +extern "C" { +#endif // __cplusplus + + #include #include @@ -24,4 +29,9 @@ */ inline void wait_for_interrupt(void) { asm volatile("wfi"); } + +#ifdef __cplusplus +} +#endif // __cplusplus + #endif // OPENTITAN_SW_DEVICE_LIB_RUNTIME_HART_H_ diff --git a/sw/device/lib/runtime/heap.cpp b/sw/device/lib/runtime/heap.cpp new file mode 100644 index 000000000..d4c8fd8b1 --- /dev/null +++ b/sw/device/lib/runtime/heap.cpp @@ -0,0 +1,60 @@ +/* +* Copyright 2024 Anestis Athanasiadis +* +* 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. +*/ + +//Additional heap functions required for C++ + +#include +#include + +void* operator new(size_t size) noexcept +{ + return malloc(size); +} + +void operator delete(void *p) noexcept +{ + free(p); +} + +void* operator new[](size_t size) noexcept +{ + return operator new(size); // Same as regular new +} + +void operator delete[](void *p) noexcept +{ + operator delete(p); // Same as regular delete +} + +void* operator new(size_t size, std::nothrow_t) noexcept +{ + return operator new(size); // Same as regular new +} + +void operator delete(void *p, std::nothrow_t) noexcept +{ + operator delete(p); // Same as regular delete +} + +void* operator new[](size_t size, std::nothrow_t) noexcept +{ + return operator new(size); // Same as regular new +} + +void operator delete[](void *p, std::nothrow_t) noexcept +{ + operator delete(p); // Same as regular delete +} \ No newline at end of file diff --git a/sw/device/lib/runtime/init.c b/sw/device/lib/runtime/init.c index cfbf206a5..ad29fca65 100644 --- a/sw/device/lib/runtime/init.c +++ b/sw/device/lib/runtime/init.c @@ -1,7 +1,16 @@ // Copyright 2022 OpenHW Group // Solderpad Hardware License, Version 2.1, see LICENSE.md for details. // SPDX-License-Identifier: Apache-2.0 WITH SHL-2.1 + +#ifdef __cplusplus +extern "C" { +#endif + int init() { return 0; -} \ No newline at end of file +} + +#ifdef __cplusplus +} +#endif \ No newline at end of file diff --git a/sw/device/lib/runtime/syscalls.c b/sw/device/lib/runtime/syscalls.c index a9a7fcc20..8e516af07 100644 --- a/sw/device/lib/runtime/syscalls.c +++ b/sw/device/lib/runtime/syscalls.c @@ -17,8 +17,13 @@ * PERFORMANCE OF THIS SOFTWARE. */ +#ifdef __cplusplus +extern "C" { +#endif + #include -#include +#include +#include #include #include #include @@ -46,6 +51,7 @@ int _link (const char *__path1, const char *__path2); _off_t _lseek (int __fildes, _off_t __offset, int __whence); ssize_t _read (int __fd, void *__buf, size_t __nbyte); void * _sbrk (ptrdiff_t __incr); +int _brk(void *addr); int _unlink (const char *__path); ssize_t _write (int __fd, const void *__buf, size_t __nbyte); int _execve (const char *__path, char * const __argv[], char * const __envp[]); @@ -259,9 +265,13 @@ ssize_t _write(int file, const void *ptr, size_t len) errno = ENOSYS; return -1; } - return uart_write(&uart,(uint8_t *)ptr,len); +} + +_ssize_t _write_r(struct _reent *ptr, int fd, const void *buf, size_t cnt) +{ + return _write(fd,buf,cnt); } extern char __heap_start[]; @@ -270,8 +280,12 @@ static char *brk = __heap_start; int _brk(void *addr) { - brk = addr; - return 0; + if (addr >= (void *)__heap_start && addr <= (void *)__heap_end) { + brk = addr; + return 0; + } else { + return -1; + } } void *_sbrk(ptrdiff_t incr) @@ -279,13 +293,27 @@ void *_sbrk(ptrdiff_t incr) char *old_brk = brk; if (__heap_start == __heap_end) { - return NULL; + return NULL; } - if ((brk += incr) < __heap_end) { + if (brk + incr < __heap_end && brk + incr >= __heap_start) { brk += incr; } else { - brk = __heap_end; + return (void *)-1; } return old_brk; } + +int raise(int sig) +{ + return _kill(_getpid(), sig); +} + +void abort(void) +{ + _exit(-1); +} + +#ifdef __cplusplus +} +#endif \ No newline at end of file diff --git a/sw/device/lib/runtime/syscalls_cpp.cpp b/sw/device/lib/runtime/syscalls_cpp.cpp new file mode 100644 index 000000000..3385fcb55 --- /dev/null +++ b/sw/device/lib/runtime/syscalls_cpp.cpp @@ -0,0 +1,39 @@ +/* +* Copyright 2024 Anestis Athanasiadis +* +* 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. +*/ + + +// This function is required to register the call for the destructor +// of static object +extern "C" int __aeabi_atexit(void *object, void (*destructor)(void *),void *dso_handle) +{ + static_cast(object); + static_cast(destructor); + static_cast(dso_handle); + return 0; +} + +void operator delete(void *,unsigned int) +{ +} + +// Required when there is pure virtual function +extern "C" void __cxa_pure_virtual() +{ + while (true) {} +} + + +void *__dso_handle = 0; \ No newline at end of file diff --git a/sw/device/lib/sdk/dma/dma_sdk.c b/sw/device/lib/sdk/dma/dma_sdk.c index d123d20bb..673715311 100644 --- a/sw/device/lib/sdk/dma/dma_sdk.c +++ b/sw/device/lib/sdk/dma/dma_sdk.c @@ -3,7 +3,7 @@ // SPDX-License-Identifier: Apache-2.0 WITH SHL-2.1 // // File: dma_sdk.c -// Author: Michele Caon +// Author: Michele Caon, Luigi Giuffrida // Date: 19/06/2023 // Description: Utility functions for DMA peripheral. @@ -15,469 +15,86 @@ #include "core_v_mini_mcu.h" #include "csr.h" -/******************************/ -/* ---- GLOBAL VARIABLES ---- */ -/******************************/ - -volatile uint8_t dma_sdk_intr_flag; - -/**********************************/ -/* ---- FUNCTION DEFINITIONS ---- */ -/**********************************/ - -#ifndef USE_HEEP_DMA_HAL - -#define DMA_REGISTER_SIZE_BYTES sizeof(int) -#define DMA_SELECTION_OFFSET_START 0 - -static inline void write_register(uint32_t p_val, - uint32_t p_offset, - uint32_t p_mask, - uint8_t p_sel, - dma *peri) +#ifdef __cplusplus +extern "C" { - /* - * The index is computed to avoid needing to access the structure - * as a structure. - */ - uint8_t index = p_offset / DMA_REGISTER_SIZE_BYTES; - /* - * An intermediate variable "value" is used to prevent writing twice into - * the register. - */ - uint32_t value = ((uint32_t *)peri)[index]; - value &= ~(p_mask << p_sel); - value |= (p_val & p_mask) << p_sel; - ((uint32_t *)peri)[index] = value; -} - #endif -// Copy data from source to destination using DMA peripheral -void dma_copy_32b(uint32_t *dst, uint32_t *src, uint32_t size) -{ - - dma_config_flags_t res; - - dma_target_t tgt_src = { - .ptr = (uint8_t *) src, - .inc_du = 1, - .size_du = size, - .trig = DMA_TRIG_MEMORY, - .type = DMA_DATA_TYPE_WORD, - }; - dma_target_t tgt_dst = { - .ptr = (uint8_t *) dst, - .inc_du = 1, - .size_du = size, - .trig = DMA_TRIG_MEMORY, - .type = DMA_DATA_TYPE_WORD, - }; - - dma_trans_t trans = { - .src = &tgt_src, - .dst = &tgt_dst, - .src_addr = NULL, - .mode = DMA_TRANS_MODE_SINGLE, - .win_du = 0, - .end = DMA_TRANS_END_INTR, - }; + /******************************/ + /* ---- GLOBAL VARIABLES ---- */ + /******************************/ -#ifdef USE_HEEP_DMA_HAL - res = dma_validate_transaction(&trans, DMA_ENABLE_REALIGN, DMA_PERFORM_CHECKS_INTEGRITY); - res = dma_load_transaction(&trans); - res = dma_launch(&trans); -#else + volatile uint8_t dma_sdk_intr_flag; - dma *peri = dma_peri; - - uint8_t dataSize_b = DMA_DATA_TYPE_2_SIZE(trans.src->type); - trans.size_b = trans.src->size_du * dataSize_b; - /* By default, the source defines the data type.*/ - trans.src_type = trans.src->type; - - /* - * SET THE POINTERS - */ - peri->SRC_PTR = (uint32_t) trans.src->ptr; - peri->DST_PTR = (uint32_t) trans.dst->ptr; - - /* - * SET THE INCREMENTS - */ - - write_register(4, - DMA_SRC_PTR_INC_D1_REG_OFFSET, - DMA_SRC_PTR_INC_D1_INC_MASK, - DMA_SRC_PTR_INC_D1_INC_OFFSET, - peri); - - write_register(4, - DMA_DST_PTR_INC_D1_REG_OFFSET, - DMA_DST_PTR_INC_D1_INC_MASK, - DMA_DST_PTR_INC_D1_INC_OFFSET, - peri); - - /* - * SET THE OPERATION MODE AND WINDOW SIZE - */ - - peri->MODE = trans.mode; - - write_register(trans.src_type, - DMA_SRC_DATA_TYPE_REG_OFFSET, - DMA_SRC_DATA_TYPE_DATA_TYPE_MASK, - DMA_SELECTION_OFFSET_START, - peri); - - peri->INTERRUPT_EN = 0x1; +#define DMA_REGISTER_SIZE_BYTES sizeof(int) +#define DMA_SELECTION_OFFSET_START 0 - /* Load the size and start the transaction. */ - peri->SIZE_D1 = trans.size_b; +/* Mask for direct register operations */ +#define DMA_CSR_REG_MIE_MASK ((1 << 19) | (1 << 11)) -#endif + /**********************************/ + /* ---- FUNCTION DEFINITIONS ---- */ + /**********************************/ - while (!dma_is_ready()) + static __attribute__((always_inline)) void dma_start(dma *peri, uint32_t size, dma_data_type_t src_type) { - // disable_interrupts - // this does not prevent waking up the core as this is controlled by the MIP register - CSR_CLEAR_BITS(CSR_REG_MSTATUS, 0x8); - if (dma_is_ready() == 0) - { - wait_for_interrupt(); - // from here we wake up even if we did not jump to the ISR - } - CSR_SET_BITS(CSR_REG_MSTATUS, 0x8); + peri->SIZE_D1 = (uint32_t)((size * DMA_DATA_TYPE_2_SIZE(src_type)) & DMA_SIZE_D1_SIZE_MASK); } - return; -} - -void dma_fill(uint32_t *dst, uint32_t *value, uint32_t size) -{ - - dma *peri = dma_peri; - - /* - * SET THE POINTERS - */ - peri->SRC_PTR = (uint32_t) value; - peri->DST_PTR = (uint32_t) dst; - - /* - * SET THE INCREMENTS - */ - - write_register(0, - DMA_SRC_PTR_INC_D1_REG_OFFSET, - DMA_SRC_PTR_INC_D1_INC_MASK, - DMA_SRC_PTR_INC_D1_INC_OFFSET, - peri); - - write_register(4, - DMA_DST_PTR_INC_D1_REG_OFFSET, - DMA_DST_PTR_INC_D1_INC_MASK, - DMA_DST_PTR_INC_D1_INC_OFFSET, - peri); - - /* - * SET THE OPERATION MODE AND WINDOW SIZE - */ - - peri->MODE = DMA_TRANS_MODE_SINGLE; - - write_register(DMA_DATA_TYPE_WORD, - DMA_SRC_DATA_TYPE_REG_OFFSET, - DMA_SRC_DATA_TYPE_DATA_TYPE_MASK, - DMA_SELECTION_OFFSET_START, - peri); - - peri->INTERRUPT_EN = 0x1; - - /* Load the size and start the transaction. */ - peri->SIZE_D1 = size * sizeof(uint32_t); - - while (!dma_is_ready()) + // Initialize the DMA + void dma_sdk_init(void) { - // disable_interrupts - // this does not prevent waking up the core as this is controlled by the MIP register - CSR_CLEAR_BITS(CSR_REG_MSTATUS, 0x8); - if (dma_is_ready() == 0) - { - wait_for_interrupt(); - // from here we wake up even if we did not jump to the ISR - } - CSR_SET_BITS(CSR_REG_MSTATUS, 0x8); - } - - return; -} - -void dma_copy_16_32(uint32_t *dst, uint16_t *src, uint32_t size) -{ - - dma *peri = dma_peri; - - uint8_t dataSize_b = DMA_DATA_TYPE_2_SIZE(DMA_DATA_TYPE_WORD); - - /* - * SET THE POINTERS - */ - peri->SRC_PTR = (uint32_t) src; - peri->DST_PTR = (uint32_t) dst; + dma_init(NULL); - /* - * SET THE INCREMENTS - */ - - write_register(2, - DMA_SRC_PTR_INC_D1_REG_OFFSET, - DMA_SRC_PTR_INC_D1_INC_MASK, - DMA_SRC_PTR_INC_D1_INC_OFFSET, - peri); - - write_register(4, - DMA_SRC_PTR_INC_D1_REG_OFFSET, - DMA_DST_PTR_INC_D1_INC_MASK, - DMA_DST_PTR_INC_D1_INC_OFFSET, - peri); - /* - * SET THE OPERATION MODE AND WINDOW SIZE - */ - - peri->MODE = DMA_TRANS_MODE_SINGLE; - - write_register(DMA_DATA_TYPE_HALF_WORD, - DMA_SRC_DATA_TYPE_REG_OFFSET, - DMA_SRC_DATA_TYPE_DATA_TYPE_MASK, - DMA_SELECTION_OFFSET_START, - peri); - - peri->INTERRUPT_EN = 0x1; - - /* Load the size and start the transaction. */ - peri->SIZE_D1 = dataSize_b * size; - - // #endif - - while (!dma_is_ready()) - { - // disable_interrupts - // this does not prevent waking up the core as this is controlled by the MIP register - CSR_CLEAR_BITS(CSR_REG_MSTATUS, 0x8); - if (dma_is_ready() == 0) - { - wait_for_interrupt(); - // from here we wake up even if we did not jump to the ISR - } + /* Enable global interrupts */ CSR_SET_BITS(CSR_REG_MSTATUS, 0x8); - } - - return; -} - - -// Copy data from source to destination using DMA peripheral -void dma_copy_to_addr_32b(uint32_t *dst_addr, uint32_t *src, uint32_t size) -{ - - dma_config_flags_t res; - - dma_target_t tgt_src = { - .ptr = (uint8_t *) src, - .inc_du = 1, - .size_du = size, - .trig = DMA_TRIG_MEMORY, - .type = DMA_DATA_TYPE_WORD, - }; - dma_target_t tgt_addr = { - .ptr = (uint8_t *) dst_addr, - .inc_du = 1, - .size_du = size, - .trig = DMA_TRIG_MEMORY, - }; - - dma_trans_t trans = { - .src = &tgt_src, - .dst = NULL, - .src_addr = &tgt_addr, - .mode = DMA_TRANS_MODE_ADDRESS, - .win_du = 0, - .end = DMA_TRANS_END_INTR, - }; - -#ifdef USE_HEEP_DMA_HAL - /** TO BE DONE */ -#else - - dma *peri = dma_peri; - - uint8_t dataSize_b = DMA_DATA_TYPE_2_SIZE(trans.src->type); - trans.size_b = trans.src->size_du * dataSize_b; - /* By default, the source defines the data type.*/ - trans.src_type = trans.src->type; - - /* - * SET THE POINTERS - */ - peri->SRC_PTR = (uint32_t) trans.src->ptr; - peri->ADDR_PTR = (uint32_t) trans.src_addr->ptr; - - /* - * SET THE INCREMENTS - */ - - write_register(4, - DMA_SRC_PTR_INC_D1_REG_OFFSET, - DMA_SRC_PTR_INC_D1_INC_MASK, - DMA_SRC_PTR_INC_D1_INC_OFFSET, - peri); - /* - * SET THE OPERATION MODE AND WINDOW SIZE - */ + /* Enable fast interrupts */ + CSR_SET_BITS(CSR_REG_MIE, DMA_CSR_REG_MIE_MASK); - peri->MODE = trans.mode; - - write_register(trans.src_type, - DMA_SRC_DATA_TYPE_REG_OFFSET, - DMA_SRC_DATA_TYPE_DATA_TYPE_MASK, - DMA_SELECTION_OFFSET_START, - peri); - - peri->INTERRUPT_EN = 0x1; - - /* Load the size and start the transaction. */ - peri->SIZE_D1 = trans.size_b; - -#endif - - while (!dma_is_ready()) - { - // disable_interrupts - // this does not prevent waking up the core as this is controlled by the MIP register - CSR_CLEAR_BITS(CSR_REG_MSTATUS, 0x8); - if (dma_is_ready() == 0) - { - wait_for_interrupt(); - // from here we wake up even if we did not jump to the ISR - } - CSR_SET_BITS(CSR_REG_MSTATUS, 0x8); + return; } - return; -} - -// Copy data from source to destination using DMA peripheral -int dma_copy(const uint8_t *dst, const uint8_t *src, const size_t bytes, const dma_data_type_t type) -{ - // Number of words - size_t num_du = bytes >> 2; - - // Last bytes - size_t last_bytes = bytes & 0x3; - uint8_t *last_src = src + (num_du << 2); - uint8_t *last_dst = dst + (num_du << 2); - - // DMA configuration - dma_config_flags_t dma_ret; - dma_data_type_t dma_type; - - // Check alignment - if (((uintptr_t)src & 0x3) || ((uintptr_t)dst & 0x3)) - { - // NOTE: use data units (half words or bytes) instead of words when - // dealing with unaligned base addresses to avoid alignment issues - // (unsupported by X-HEEP's DMA. This is 2x or 4x slower than words. - switch (type) - { - case DMA_DATA_TYPE_WORD: - num_du = bytes >> 2; - last_bytes = bytes & 0x3; - last_src = src + (num_du << 2); - last_dst = dst + (num_du << 2); - break; - case DMA_DATA_TYPE_HALF_WORD: - num_du = bytes >> 1; - last_bytes = bytes & 0x1; - last_src = src + (num_du << 1); - last_dst = dst + (num_du << 1); - break; - default: - num_du = bytes; - last_bytes = 0; - break; - } - dma_type = type; - } - else + void dma_copy(uint32_t dst_ptr, uint32_t src_ptr, uint32_t size, uint8_t channel, dma_data_type_t src_type, dma_data_type_t dst_type, uint8_t signed_data) { - // Use word transactions (faster) - dma_type = DMA_DATA_TYPE_WORD; + volatile dma *the_dma = dma_peri(channel); + DMA_COPY(dst_ptr, src_ptr, size, src_type, dst_type, signed_data, the_dma); + dma_start(the_dma, size, src_type); + DMA_WAIT(channel); + return; } - // Source pointer - dma_target_t tgt_src = { - .ptr = (uint8_t *) src, - .inc_du = 1, - .size_du = num_du, - .trig = DMA_TRIG_MEMORY, - .type = dma_type, - }; - - // Destination pointer - dma_target_t tgt_dst = { - .ptr = (uint8_t *) dst, - .inc_du = 1, - .trig = DMA_TRIG_MEMORY, - }; - - // DMA transaction - dma_trans_t trans = { - .src = &tgt_src, - .dst = &tgt_dst, - .src_addr = NULL, - .mode = DMA_TRANS_MODE_SINGLE, - .win_du = 0, - .end = DMA_TRANS_END_INTR, - }; - - // Configure and launch DMA transfer - dma_ret = dma_validate_transaction(&trans, DMA_DO_NOT_ENABLE_REALIGN, DMA_PERFORM_CHECKS_ONLY_SANITY); - if (dma_ret != DMA_CONFIG_OK) - { - return -1; - } - dma_ret = dma_load_transaction(&trans); - if (dma_ret != DMA_CONFIG_OK) + void dma_fill(uint32_t dst_ptr, uint32_t value_ptr, uint32_t size, uint8_t channel, dma_data_type_t src_type, dma_data_type_t dst_type, uint8_t signed_data) { - return -1; + volatile dma *the_dma = dma_peri(channel); + DMA_FILL(dst_ptr, value_ptr, size, src_type, dst_type, signed_data, the_dma); + dma_start(the_dma, size, src_type); + DMA_WAIT(channel); + return; } - dma_ret = dma_launch(&trans); - if (dma_ret != DMA_CONFIG_OK) + + void __attribute__ ((noinline)) dma_copy_async(uint32_t dst_ptr, uint32_t src_ptr, uint32_t size, uint8_t channel, dma_data_type_t src_type, dma_data_type_t dst_type, uint8_t signed_data) { - return -1; + volatile dma *the_dma = dma_peri(channel); + DMA_COPY(dst_ptr, src_ptr, size, src_type, dst_type, signed_data, the_dma); + dma_start(the_dma, size, src_type); + return; } - // Wait for DMA transfer to finish - while (!dma_is_ready()) + void __attribute__ ((noinline)) dma_fill_async(uint32_t dst_ptr, uint32_t value_ptr, uint32_t size, uint8_t channel, dma_data_type_t src_type, dma_data_type_t dst_type, uint8_t signed_data) { - // disable_interrupts - // this does not prevent waking up the core as this is controlled by the MIP register - CSR_CLEAR_BITS(CSR_REG_MSTATUS, 0x8); - if (dma_is_ready() == 0) - { - wait_for_interrupt(); - // from here we wake up even if we did not jump to the ISR - } - CSR_SET_BITS(CSR_REG_MSTATUS, 0x8); + volatile dma *the_dma = dma_peri(channel); + DMA_FILL(dst_ptr, value_ptr, size, src_type, dst_type, signed_data, the_dma); + dma_start(the_dma, size, src_type); + return; } - // Manually copy the last bytes (unaligned transfers not supported by DMA) - for (size_t i = 0; i < last_bytes; i++) + void __attribute__ ((noinline)) dma_wait(uint8_t channel) { - last_dst[i] = last_src[i]; + DMA_WAIT(channel); + return; } - return 0; +#ifdef __cplusplus } +#endif diff --git a/sw/device/lib/sdk/dma/dma_sdk.h b/sw/device/lib/sdk/dma/dma_sdk.h index 0b3ae0aa5..726024e06 100644 --- a/sw/device/lib/sdk/dma/dma_sdk.h +++ b/sw/device/lib/sdk/dma/dma_sdk.h @@ -3,74 +3,106 @@ // SPDX-License-Identifier: Apache-2.0 WITH SHL-2.1 // // File: dma_sdk.h -// Author: Michele Caon +// Author: Michele Caon, Luigi Giuffrida // Date: 19/06/2023 // Description: DMA utility functions -#ifndef DMA_UTIL_H_ -#define DMA_UTIL_H_ +#ifndef DMA_SDK_H_ +#define DMA_SDK_H_ #include #include // for size_t +#include "csr.h" #include "dma.h" +#ifdef __cplusplus +extern "C" +{ +#endif // __cplusplus -/********************************/ -/* ---- EXPORTED VARIABLES ---- */ -/********************************/ - -extern volatile uint8_t dma_sdk_intr_flag; - -/********************************/ -/* ---- EXPORTED FUNCTIONS ---- */ -/********************************/ - -/** - * @brief Copy data words from source address to destination address - * - * @param dst Destination address - * @param src Source address - * @param size Number of words (not bytes) to copy - */ -void dma_copy_32b(uint32_t *dst, uint32_t *src, uint32_t size); - -/** - * @brief Copy data from source address to explicit destination addresses - * - * @param dst_addr Array of destination addresses - * @param src Source address - * @param bytes Number of words (not bytes) to copy - */ -void dma_copy_to_addr_32b(uint32_t *dst_addr, uint32_t *src, uint32_t size); - -/** - * @brief Copy data from source address to destination address - * - * @param dst Destination address - * @param src Source address - * @param bytes Number of bytes to copy - * @param type Data type (DMA_DATA_TYPE_WORD, DMA_DATA_TYPE_HALF_WORD, DMA_DATA_TYPE_BYTE) - * @return int 0 if success, -1 if error - */ -int dma_copy(const uint8_t *dst, const uint8_t *src, const size_t bytes, const dma_data_type_t type); - -/** - * @brief Fill a memory region with a 32-bit value - * - * @param dst Destination address - * @param value Pointer to the value to fill the memory with - * @param size Number of words (not bytes) to fill - * @return int 0 if success, -1 if error - */ -void dma_fill(uint32_t *dst, uint32_t *value, uint32_t size); - -/** - * @brief Copy data from source address to destination address (16-bit aligned) [BROKEN until siigned DMA works] - * - * @param dst Destination address (32-bit aligned) - * @param src Source address (16-bit aligned) - * @param size Number of bytes to copy - */ -void dma_copy_16_32(uint32_t *dst, uint16_t *src, uint32_t size); - -#endif /* DMA_UTIL_H_ */ +#define INCREMENT(TYPE) ((TYPE == DMA_DATA_TYPE_BYTE) ? 1 : (TYPE == DMA_DATA_TYPE_HALF_WORD) ? 2 \ + : 4) + +#define DMA_COPY(DST, SRC, SIZE, SRC_TYPE, DST_TYPE, SIGNED, DMA_PERI) \ + DMA_PERI->INTERRUPT_EN = (uint32_t)0x1; \ + DMA_PERI->SRC_PTR = (uint32_t)SRC; \ + DMA_PERI->DST_PTR = (uint32_t)DST; \ + DMA_PERI->SRC_PTR_INC_D1 = (uint32_t)(INCREMENT(SRC_TYPE) & DMA_SRC_PTR_INC_D1_INC_MASK); \ + DMA_PERI->DST_PTR_INC_D1 = (uint32_t)(INCREMENT(DST_TYPE) & DMA_DST_PTR_INC_D1_INC_MASK); \ + DMA_PERI->MODE = (uint32_t)(DMA_TRANS_MODE_SINGLE & DMA_MODE_MODE_MASK); \ + DMA_PERI->SRC_DATA_TYPE = (uint32_t)(SRC_TYPE & DMA_SRC_DATA_TYPE_DATA_TYPE_MASK); \ + DMA_PERI->DST_DATA_TYPE = (uint32_t)(DST_TYPE & DMA_DST_DATA_TYPE_DATA_TYPE_MASK); \ + DMA_PERI->SIGN_EXT = (uint32_t)SIGNED; + +#define DMA_FILL(DST, VALUE_PTR, SIZE, SRC_TYPE, DST_TYPE, SIGNED, DMA_PERI) \ + DMA_PERI->INTERRUPT_EN = (uint32_t)0x1; \ + DMA_PERI->SRC_PTR = (uint32_t)VALUE_PTR; \ + DMA_PERI->DST_PTR = (uint32_t)DST; \ + DMA_PERI->SRC_PTR_INC_D1 = (uint32_t)0; \ + DMA_PERI->DST_PTR_INC_D1 = (uint32_t)(INCREMENT(DST_TYPE) & DMA_DST_PTR_INC_D1_INC_MASK); \ + DMA_PERI->MODE = (uint32_t)(DMA_TRANS_MODE_SINGLE & DMA_MODE_MODE_MASK); \ + DMA_PERI->SRC_DATA_TYPE = (uint32_t)(SRC_TYPE & DMA_SRC_DATA_TYPE_DATA_TYPE_MASK); \ + DMA_PERI->DST_DATA_TYPE = (uint32_t)(DST_TYPE & DMA_DST_DATA_TYPE_DATA_TYPE_MASK); \ + DMA_PERI->SIGN_EXT = (uint32_t)SIGNED; + +#define DMA_WAIT(CH) \ + while (!dma_is_ready(CH)) \ + { \ + CSR_CLEAR_BITS(CSR_REG_MSTATUS, 0x8); \ + if (dma_is_ready(CH) == 0) \ + { \ + wait_for_interrupt(); \ + } \ + CSR_SET_BITS(CSR_REG_MSTATUS, 0x8); \ + } + + /********************************/ + /* ---- EXPORTED VARIABLES ---- */ + /********************************/ + + extern volatile uint8_t dma_sdk_intr_flag; + + /** + * @brief Initializes the DMA SDK. + * + * This function initializes the DMA SDK and prepares it for use. + */ + void __attribute__((noinline)) dma_sdk_init(void); + + /** + * @brief Copies data from source to destination using DMA. + * + * @param dst_ptr Pointer to the destination memory location. + * @param src_ptr Pointer to the source memory location. + * @param size Size of the data to be copied in bytes. + * @param channel DMA channel to be used for the transfer. + * @param src_type Source variable type (byte, half-word, word). + * @param dst_type Destination variable type (byte, half-word, word). + * @param signed_data Indicates whether the data is signed or unsigned. + */ + void __attribute__((noinline)) dma_copy(uint32_t dst_ptr, uint32_t src_ptr, uint32_t size, uint8_t channel, dma_data_type_t src_type, dma_data_type_t dst_type, uint8_t signed_data); + + /** + * @brief Fills a memory region with a specified value using DMA. + * + * @param dst_ptr Pointer to the destination memory location. + * @param value_ptr Pointer to the value to be filled. + * @param size Size of the memory region to be filled in bytes. + * @param channel DMA channel to be used for the transfer. + * @param src_type Source variable type (byte, half-word, word). + * @param dst_type Destination variable type (byte, half-word, word). + * @param signed_data Indicates whether the data is signed or unsigned. + */ + void __attribute__((noinline)) dma_fill(uint32_t dst_ptr, uint32_t value_ptr, uint32_t size, uint8_t channel, dma_data_type_t src_type, dma_data_type_t dst_type, uint8_t signed_data); + + void __attribute__((noinline)) dma_copy_async(uint32_t dst_ptr, uint32_t src_ptr, uint32_t size, uint8_t channel, dma_data_type_t src_type, dma_data_type_t dst_type, uint8_t signed_data); + + void __attribute__((noinline)) dma_fill_async(uint32_t dst_ptr, uint32_t value_ptr, uint32_t size, uint8_t channel, dma_data_type_t src_type, dma_data_type_t dst_type, uint8_t signed_data); + + void __attribute__((noinline)) dma_wait(uint8_t channel); + +#ifdef __cplusplus +} +#endif // __cplusplus + +#endif /* DMA_SDK_H_ */ diff --git a/sw/device/lib/sdk/spi/spi_sdk.c b/sw/device/lib/sdk/spi/spi_sdk.c index efb0d885d..8c8a6a167 100644 --- a/sw/device/lib/sdk/spi/spi_sdk.c +++ b/sw/device/lib/sdk/spi/spi_sdk.c @@ -756,12 +756,12 @@ spi_codes_e spi_set_slave(spi_t* spi) // Build the HAL configopts to be set based on our slave spi_configopts_t config = { .clkdiv = clk_div, - .cpha = bitfield_read(spi->slave.data_mode, BIT_MASK_1, DATA_MODE_CPHA_OFFS), - .cpol = bitfield_read(spi->slave.data_mode, BIT_MASK_1, DATA_MODE_CPOL_OFFS), .csnidle = spi->slave.csn_idle, - .csnlead = spi->slave.csn_lead, .csntrail = spi->slave.csn_trail, - .fullcyc = spi->slave.full_cycle + .csnlead = spi->slave.csn_lead, + .fullcyc = spi->slave.full_cycle, + .cpha = bitfield_read(spi->slave.data_mode, BIT_MASK_1, DATA_MODE_CPHA_OFFS), + .cpol = bitfield_read(spi->slave.data_mode, BIT_MASK_1, DATA_MODE_CPOL_OFFS) }; // Set the configopts spi_return_flags_e config_error = spi_set_configopts(peripherals[spi->idx].instance, @@ -945,10 +945,10 @@ void spi_issue_next_seg(spi_peripheral_t* peri) peri->scnt++; // Construct our word command to be passed to HAL uint32_t cmd_reg = spi_create_command((spi_command_t) { - .direction = bitfield_read(seg.mode, DIR_SPD_MASK, DIR_INDEX), - .speed = bitfield_read(seg.mode, DIR_SPD_MASK, SPD_INDEX), + .len = seg.len - 1, // -1 because of SPI Host IP specifications .csaat = peri->txn.seglen == peri->scnt ? false : true, - .len = seg.len - 1 // -1 because of SPI Host IP specifications + .speed = bitfield_read(seg.mode, DIR_SPD_MASK, SPD_INDEX), + .direction = bitfield_read(seg.mode, DIR_SPD_MASK, DIR_INDEX), }); // Since all checks were already made we do not need to check the result of function spi_set_command(peri->instance, cmd_reg); diff --git a/sw/device/lib/sdk/timer/timer_sdk.c b/sw/device/lib/sdk/timer/timer_sdk.c new file mode 100644 index 000000000..3e4e27195 --- /dev/null +++ b/sw/device/lib/sdk/timer/timer_sdk.c @@ -0,0 +1,142 @@ +// Copyright 2023 EPFL and Politecnico di Torino. +// Solderpad Hardware License, Version 2.1, see LICENSE.md for details. +// SPDX-License-Identifier: Apache-2.0 WITH SHL-2.1 +// +// File: timer_sdk.c +// Author: Michele Caon, Francesco Poluzzi +// Date: 23/07/2024 +// Description: Timer functions + +#include + +#include "timer_sdk.h" +#include "csr.h" +#include "soc_ctrl.h" + + +/******************************/ +/* ---- GLOBAL VARIABLES ---- */ +/******************************/ + +// Timer value +int32_t hw_timer_value = 0; + +rv_timer_t timer; + +rv_timer_config_t timer_cfg = { + .hart_count = RV_TIMER_PARAM_N_HARTS, + .comparator_count = RV_TIMER_PARAM_N_TIMERS, +}; + +mmio_region_t timer_base = { + .base = (void *)RV_TIMER_AO_START_ADDRESS, +}; + +rv_timer_tick_params_t tick_params; + + +/*************************************/ +/* ---- FUNCTION IMPLEMENTATION ---- */ +/*************************************/ + +uint32_t timer_get_cycles() +{ + uint64_t cycle_count; + rv_timer_counter_read(&timer, 0, &cycle_count); + return (uint32_t)cycle_count; +} + +void timer_irq_enable() +{ + rv_timer_irq_enable(&timer, 0, 0, kRvTimerEnabled); +} + +void timer_irq_clear() +{ + rv_timer_irq_clear(&timer, 0, 0); +} + +void timer_arm_start(uint32_t threshold) +{ + rv_timer_arm(&timer, 0, 0, threshold); + timer_start(); +} + +void timer_arm_stop() +{ + rv_timer_counter_set_enabled(&timer, 0, kRvTimerDisabled); +} + + +void timer_arm_set(uint32_t threshold) +{ + rv_timer_arm(&timer, 0, 0, threshold); +} + +void timer_start() +{ + hw_timer_value = -timer_get_cycles(); + rv_timer_counter_set_enabled(&timer, 0, kRvTimerEnabled); +} + +void timer_reset() +{ + hw_timer_value = 0; + rv_timer_reset(&timer); +} + +uint32_t timer_stop() +{ + hw_timer_value += timer_get_cycles(); + rv_timer_counter_set_enabled(&timer, 0, kRvTimerDisabled); + return hw_timer_value; +} + +// Initialize the timer +void timer_cycles_init() +{ + // Get current Frequency + soc_ctrl_t soc_ctrl; + soc_ctrl.base_addr = mmio_region_from_addr((uintptr_t)SOC_CTRL_START_ADDRESS); + uint32_t freq_hz = soc_ctrl_get_frequency(&soc_ctrl); + + // Initialize the timer + timer_reset(); + rv_timer_init(timer_base, timer_cfg, &timer); + rv_timer_approximate_tick_params(freq_hz, freq_hz, &tick_params); + rv_timer_set_tick_params(&timer, 0, tick_params); +} + +// Initialize the timer +void timer_wait_us(uint32_t us) +{ + // Get current Frequency + soc_ctrl_t soc_ctrl; + soc_ctrl.base_addr = mmio_region_from_addr((uintptr_t)SOC_CTRL_START_ADDRESS); + uint32_t freq_hz = soc_ctrl_get_frequency(&soc_ctrl); + + timer_cycles_init(); + timer_irq_enable(); + timer_arm_start(us*(freq_hz/1000000)-50); // 50 cycles for taking into account initialization + asm volatile ("wfi"); + timer_irq_clear(); + return; +} + +void enable_timer_interrupt() +{ + //enable timer interrupt + CSR_SET_BITS(CSR_REG_MSTATUS, 0x8); + // Set mie.MEIE bit to one to enable machine-level timer interrupts + const uint32_t mask = 1 << 7; + CSR_SET_BITS(CSR_REG_MIE, mask); +} + +float get_time_from_cycles(uint32_t cycles){ + // Get current Frequency + soc_ctrl_t soc_ctrl; + soc_ctrl.base_addr = mmio_region_from_addr((uintptr_t)SOC_CTRL_START_ADDRESS); + uint32_t freq_hz = soc_ctrl_get_frequency(&soc_ctrl); + + return (float)cycles/((float)freq_hz/1000000); +} \ No newline at end of file diff --git a/sw/device/lib/sdk/timer/timer_sdk.h b/sw/device/lib/sdk/timer/timer_sdk.h new file mode 100644 index 000000000..154ef30bc --- /dev/null +++ b/sw/device/lib/sdk/timer/timer_sdk.h @@ -0,0 +1,121 @@ + +// Copyright 2023 EPFL and Politecnico di Torino. +// Solderpad Hardware License, Version 2.1, see LICENSE.md for details. +// SPDX-License-Identifier: Apache-2.0 WITH SHL-2.1 +// +// File: timer_sdk.h +// Authors: Michele Caon, Luigi Giuffrida, Francesco Poluzzi +// Date: 23/07/2024 +// Description: Execution time measurements utilities + +#ifndef TIMER_SDK_H_ +#define TIMER_SDK_H_ + +#include + +#include "csr.h" +#include "gpio.h" +#include "rv_timer.h" +#include "rv_timer_regs.h" +#include "core_v_mini_mcu.h" +#include "x-heep.h" + +#define FREQ_1MHz 1000000 + +/******************************/ +/* ---- GLOBAL VARIABLES ---- */ +/******************************/ + +// Timer value +extern int32_t hw_timer_value; + +extern rv_timer_t timer; + +/********************************/ +/* ---- EXPORTED FUNCTIONS ---- */ +/********************************/ + +/** + * @brief Get the current value of the HW timer + * +* @return int64_t Current value of the HW timer + */ +uint32_t timer_get_cycles(); + +/** + * @brief Start the timer + * + */ +void timer_start(); + +/** + * @brief Stop and reset the timer to 0 + * + */ +void timer_reset(); + +/** + * @brief Stop the HW timer + * + * @return int64_t Elapsed time in clock cycles + */ +uint32_t timer_stop(); + +/** + * @brief Initialize the timer for counting clock cycles + * @brief Initialize the timer for counting clock cycles + * + */ +void timer_cycles_init(); + +/** + * @brief Enable the timer IRQ + */ +void timer_irq_enable(); + +/** + * @brief Clear the timer IRQ + */ +void timer_irq_clear(); + +/** + * @brief Arms the timer to go off once the counter value is greater than or equal to threshold + * and starts the timer + */ +void timer_arm_start(uint32_t threshold); + +/** + * @brief Stop to output when timer is greater than or equal to threshold previously set + */ +void timer_arm_stop(); + +/** + * @brief Set the timer to go off once the counter value is greater than or equal to threshold, + * without starting the timer + */ +void timer_arm_set(uint32_t threshold); + +/** + * @brief Enable the timer machine-level interrupts for X-Heep + */ +void enable_timer_interrupt(); + +/** + * @brief Wait for a certain amount of microseconds. + * You need to enable timer interrupts with enable_timer_interrupt() before using this function + */ +void timer_wait_us(uint32_t ms); + +/** + * @brief Enable the timer machine-level interrupts for X-Heep + */ +void enable_timer_interrupt(); + +/** + * @brief Get the time taken to execute a certain number of cycles + * + * @return float value representing the time taken in microseconds + */ +float get_time_from_cycles(uint32_t cycles); + +#endif /* TIMER_SDK_H_ */ \ No newline at end of file diff --git a/sw/device/target/nexys-a7-100t/x-heep.h b/sw/device/target/nexys-a7-100t/x-heep.h index c1ef5c5ce..072a31e0e 100644 --- a/sw/device/target/nexys-a7-100t/x-heep.h +++ b/sw/device/target/nexys-a7-100t/x-heep.h @@ -15,6 +15,7 @@ extern "C" { #define REFERENCE_CLOCK_Hz 15*1000*1000 #define UART_BAUDRATE 9600 #define TARGET_NEXYS_A7_100T 1 +#define TARGET_IS_FPGA 1 /** * As the hw is configurable, we can have setups with different number of diff --git a/sw/device/target/pynq-z2/x-heep.h b/sw/device/target/pynq-z2/x-heep.h index 43cef0d47..2733e7164 100644 --- a/sw/device/target/pynq-z2/x-heep.h +++ b/sw/device/target/pynq-z2/x-heep.h @@ -15,6 +15,7 @@ extern "C" { #define REFERENCE_CLOCK_Hz 15*1000*1000 #define UART_BAUDRATE 9600 #define TARGET_PYNQ_Z2 1 +#define TARGET_IS_FPGA 1 /** * As the hw is configurable, we can have setups with different number of diff --git a/sw/device/target/zcu104/x-heep.h b/sw/device/target/zcu104/x-heep.h index af1b4cd5a..7c0b0602a 100644 --- a/sw/device/target/zcu104/x-heep.h +++ b/sw/device/target/zcu104/x-heep.h @@ -14,6 +14,7 @@ extern "C" { #define REFERENCE_CLOCK_Hz 15*1000*1000 #define UART_BAUDRATE 9600 #define TARGET_ZCU104 1 +#define TARGET_IS_FPGA 1 /** * As the hw is configurable, we can have setups with different number of diff --git a/sw/linker/link_flash_load.ld.tpl b/sw/linker/link_flash_load.ld.tpl index cb801f864..9e7b637f7 100644 --- a/sw/linker/link_flash_load.ld.tpl +++ b/sw/linker/link_flash_load.ld.tpl @@ -60,7 +60,8 @@ SECTIONS { KEEP (*(.text.spi_wait_for_rx_watermark*)) KEEP (*(.text.spi_read_word*)) KEEP (*(.text.memcpy)) - KEEP (*(.text.w25q128jw_read_standard*)) /* as this function is used in the crt0, link it in the top, should be before 1024 Bytes loaded by the bootrom */ + KEEP (*(.text.w25q128jw_read_standard)) /* as this function is used in the crt0, link it in the top, should be before 1024 Bytes loaded by the bootrom */ + KEEP (*(.text.w25q128jw_read_standard.*)) /* sometimes the function is renamed as w25q128jw_read_standard.part */ *(.xheep_init_data_crt0) /* this global variables are used in the crt0 */ KEEP (*_bswapsi2*(.text)) /* this function is used in the w25q128jw_read_standard */ } >ram0 AT >FLASH0 diff --git a/tb/testharness.sv b/tb/testharness.sv index 5a6a68443..9008beb4d 100644 --- a/tb/testharness.sv +++ b/tb/testharness.sv @@ -88,6 +88,17 @@ module testharness #( logic iffifo_in_ready, iffifo_out_valid; logic iffifo_int_o; + // External DMA slots + logic [core_v_mini_mcu_pkg::DMA_CH_NUM-1:0] ext_dma_slot_tx; + logic [core_v_mini_mcu_pkg::DMA_CH_NUM-1:0] ext_dma_slot_rx; + + assign ext_dma_slot_tx[0] = iffifo_in_ready; + assign ext_dma_slot_rx[0] = iffifo_out_valid; + if (core_v_mini_mcu_pkg::DMA_CH_NUM > 1) begin + assign ext_dma_slot_tx[core_v_mini_mcu_pkg::DMA_CH_NUM-1:1] = '0; + assign ext_dma_slot_rx[core_v_mini_mcu_pkg::DMA_CH_NUM-1:1] = '0; + end + // External xbar master/slave and peripheral ports obi_req_t [EXT_XBAR_NMASTER_RND-1:0] ext_master_req; obi_req_t [EXT_XBAR_NMASTER_RND-1:0] heep_slave_req; @@ -262,8 +273,9 @@ module testharness #( .external_subsystem_rst_no(external_subsystem_rst_n), .external_ram_banks_set_retentive_no(external_ram_banks_set_retentive_n), .external_subsystem_clkgate_en_no(external_subsystem_clkgate_en_n), - .ext_dma_slot_tx_i(iffifo_in_ready), - .ext_dma_slot_rx_i(iffifo_out_valid) + .ext_dma_slot_tx_i(ext_dma_slot_tx), + .ext_dma_slot_rx_i(ext_dma_slot_rx), + .ext_dma_stop_i('0) ); // Testbench external bus @@ -315,7 +327,7 @@ module testharness #( always_ff @(negedge clk_i) begin tb_cpu_subsystem_powergate_switch_ack_n[0] <= x_heep_system_i.cpu_subsystem_powergate_switch_n; tb_peripheral_subsystem_powergate_switch_ack_n[0] <= x_heep_system_i.peripheral_subsystem_powergate_switch_n; - tb_memory_subsystem_banks_powergate_switch_ack_n[0] <= x_heep_system_i.memory_subsystem_banks_powergate_switch_n; + tb_memory_subsystem_banks_powergate_switch_ack_n[0] <= x_heep_system_i.core_v_mini_mcu_i.memory_subsystem_banks_powergate_switch_n; tb_external_subsystem_powergate_switch_ack_n[0] <= external_subsystem_powergate_switch_n; for (int i = 0; i < SWITCH_ACK_LATENCY; i++) begin tb_memory_subsystem_banks_powergate_switch_ack_n[i+1] <= tb_memory_subsystem_banks_powergate_switch_ack_n[i]; @@ -334,12 +346,12 @@ module testharness #( `ifndef VERILATOR force x_heep_system_i.core_v_mini_mcu_i.cpu_subsystem_powergate_switch_ack_ni = delayed_tb_cpu_subsystem_powergate_switch_ack_n; force x_heep_system_i.core_v_mini_mcu_i.peripheral_subsystem_powergate_switch_ack_ni = delayed_tb_peripheral_subsystem_powergate_switch_ack_n; - force x_heep_system_i.core_v_mini_mcu_i.memory_subsystem_banks_powergate_switch_ack_ni = delayed_tb_memory_subsystem_banks_powergate_switch_ack_n; + force x_heep_system_i.core_v_mini_mcu_i.memory_subsystem_banks_powergate_switch_ack_n = delayed_tb_memory_subsystem_banks_powergate_switch_ack_n; force external_subsystem_powergate_switch_ack_n = delayed_tb_external_subsystem_powergate_switch_ack_n; `else x_heep_system_i.cpu_subsystem_powergate_switch_ack_n = delayed_tb_cpu_subsystem_powergate_switch_ack_n; x_heep_system_i.peripheral_subsystem_powergate_switch_ack_n = delayed_tb_peripheral_subsystem_powergate_switch_ack_n; - x_heep_system_i.memory_subsystem_banks_powergate_switch_ack_n = delayed_tb_memory_subsystem_banks_powergate_switch_ack_n; + x_heep_system_i.core_v_mini_mcu_i.memory_subsystem_banks_powergate_switch_ack_n = delayed_tb_memory_subsystem_banks_powergate_switch_ack_n; external_subsystem_powergate_switch_ack_n = delayed_tb_external_subsystem_powergate_switch_ack_n; `endif end @@ -457,6 +469,7 @@ module testharness #( ) dma_i ( .clk_i, .rst_ni, + .ext_dma_stop_i('0), .reg_req_i(ext_periph_slv_req[testharness_pkg::MEMCOPY_CTRL_IDX]), .reg_rsp_o(ext_periph_slv_rsp[testharness_pkg::MEMCOPY_CTRL_IDX]), .dma_read_ch0_req_o(ext_master_req[testharness_pkg::EXT_MASTER0_IDX]), diff --git a/util/mcu_gen.py b/util/mcu_gen.py index c661d780d..31826540e 100755 --- a/util/mcu_gen.py +++ b/util/mcu_gen.py @@ -504,7 +504,11 @@ def len_extracted_peripherals(peripherals): ao_peripherals = extract_peripherals(discard_path(obj['ao_peripherals'])) ao_peripherals_count = len(ao_peripherals) + dma_ch_count = ao_peripherals["dma"]["num_channels"] + if int(dma_ch_count, 16) > int('256', 16) or int(dma_ch_count, 16) == 0: + exit("Number of DMA channels has to be between 0 and 256, excluded") + dma_ch_size = ao_peripherals["dma"]["ch_length"] peripheral_start_address = string2int(obj['peripherals']['address']) if int(peripheral_start_address, 16) < int('10000', 16): @@ -822,6 +826,8 @@ def len_extracted_peripherals(peripherals): "ao_peripheral_size_address" : ao_peripheral_size_address, "ao_peripherals" : ao_peripherals, "ao_peripherals_count" : ao_peripherals_count, + "dma_ch_count" : dma_ch_count, + "dma_ch_size" : dma_ch_size, "peripheral_start_address" : peripheral_start_address, "peripheral_size_address" : peripheral_size_address, "peripherals" : peripherals, diff --git a/util/structs_periph_gen.py b/util/structs_periph_gen.py index c737370c9..79608eea6 100644 --- a/util/structs_periph_gen.py +++ b/util/structs_periph_gen.py @@ -1,6 +1,9 @@ import hjson import structs_gen +# Path to the dma file +dma_file_path = "./sw/device/lib/drivers/dma/dma_structs.h" + # Path to the header_structs template template_path = "./sw/device/lib/drivers/template.tpl" @@ -40,6 +43,26 @@ def scan_peripherals(json_list): add_peripheral(p, json_list[p]["path"]) +def format_dma_channels(file_path, new_string): + + try: + # Read the contents of the file + with open(file_path, 'r') as file: + content = file.read() + + # Replace 'DMA_START_ADDRESS' with 'new_address' + updated_content = content.replace('#define dma_peri ((volatile dma *) DMA_START_ADDRESS)', new_string) + + # Write the updated content back to the file + with open(file_path, 'w') as file: + file.write(updated_content) + + print("DMA channel has been successfully updated.") + + except FileNotFoundError: + print(f"The file {file_path} does not exist.") + except Exception as e: + print(f"An error occurred: {str(e)}") # for i in range(len(JSON_FILES)): # print("{}\n{}\n{}\n\n\n".format(PERIPHERAL_NAMES[i], JSON_FILES[i], OUTPUT_FILES[i])) @@ -64,4 +87,7 @@ def scan_peripherals(json_list): # "--peripheral_name", PERIPHERAL_NAMES[i], "--json_filename", JSON_FILES[i], "--output_filename", OUTPUT_FILES[i]] - ) \ No newline at end of file + ) + + new_string = "#define dma_peri(channel) ((volatile dma *) (DMA_START_ADDRESS + DMA_CH_SIZE * channel))" + format_dma_channels(dma_file_path, new_string) \ No newline at end of file diff --git a/util/test_all.sh b/util/test_all.sh index 0025cb154..dd0d03744 100755 --- a/util/test_all.sh +++ b/util/test_all.sh @@ -348,6 +348,14 @@ if [ $DEBUG -eq 0 ]; then echo -e "${WHITE}Building simulation model $SIM_MODEL_CMD ${RESET}" echo -e ${LONG_W} + if [ "$OPT" == "1" ] && [ "$SIMULATOR" == "questasim" ]; then + # Perform optimization + SIM_MODEL_CMD=${SIM_MODEL_CMD}"-opt" + echo -e ${LONG_W} + echo -e "${WHITE}Optimizing simulation model ${RESET}" + echo -e ${LONG_W} + fi + make $SIM_MODEL_CMD fi fi